From aec6b78deda32d7aa1631ec76efda5bcea83a8c9 Mon Sep 17 00:00:00 2001 From: Andre Susano Pinto Date: Tue, 22 Jul 2008 12:02:57 +0000 Subject: svn merge -r 15590:15688 https://svn.blender.org/svnroot/bf-blender/trunk/blender --- source/blender/blenkernel/BKE_global.h | 2 + source/blender/blenkernel/intern/collision.c | 3 + source/blender/blenkernel/intern/lattice.c | 5 +- source/blender/blenkernel/intern/particle_system.c | 5 +- source/blender/blenlib/intern/arithb.c | 1 + source/blender/blenloader/intern/readfile.c | 64 +- source/blender/blenloader/intern/writefile.c | 34 +- source/blender/include/BDR_drawaction.h | 3 + source/blender/include/BDR_gpencil.h | 76 ++ source/blender/include/BIF_drawgpencil.h | 44 + source/blender/include/BIF_editaction.h | 28 +- source/blender/include/BIF_space.h | 8 +- source/blender/include/BSE_editaction_types.h | 6 +- source/blender/include/transform.h | 1 + source/blender/makesdna/DNA_action_types.h | 20 +- source/blender/makesdna/DNA_gpencil_types.h | 141 +++ source/blender/makesdna/DNA_space_types.h | 8 + source/blender/makesdna/DNA_view3d_types.h | 15 +- source/blender/makesdna/intern/makesdna.c | 2 + source/blender/python/api2_2x/Blender.c | 2 +- source/blender/python/api2_2x/Library.c | 69 ++ source/blender/python/api2_2x/Particle.c | 472 ++++--- source/blender/python/api2_2x/doc/LibData.py | 42 +- source/blender/python/api2_2x/doc/Particle.py | 229 +--- source/blender/python/api2_2x/doc/Render.py | 4 +- source/blender/python/api2_2x/sceneRender.c | 2 +- source/blender/render/intern/source/rendercore.c | 27 +- source/blender/render/intern/source/shadeoutput.c | 2 + source/blender/src/drawaction.c | 124 +- source/blender/src/drawgpencil.c | 653 ++++++++++ source/blender/src/drawnode.c | 77 ++ source/blender/src/drawobject.c | 5 + source/blender/src/drawseq.c | 97 +- source/blender/src/drawview.c | 80 +- source/blender/src/editaction.c | 396 +++++- source/blender/src/editaction_gpencil.c | 549 +++++++++ source/blender/src/editmesh.c | 11 +- source/blender/src/editnode.c | 37 +- source/blender/src/editobject.c | 1 + source/blender/src/gpencil.c | 1290 ++++++++++++++++++++ source/blender/src/header_action.c | 377 ++++-- source/blender/src/header_node.c | 10 + source/blender/src/header_seq.c | 13 +- source/blender/src/header_view3d.c | 4 + source/blender/src/interface.c | 7 +- source/blender/src/space.c | 39 +- source/blender/src/transform_conversions.c | 223 +++- source/blender/src/transform_generics.c | 9 + source/blender/src/usiblender.c | 2 +- .../BlenderRoutines/KX_BlenderCanvas.cpp | 12 + .../gameengine/BlenderRoutines/KX_BlenderCanvas.h | 5 + .../Converter/BL_BlenderDataConversion.cpp | 243 +++- .../gameengine/Converter/BL_DeformableGameObject.h | 1 + source/gameengine/Converter/BL_ShapeDeformer.cpp | 7 + source/gameengine/Converter/BL_SkinDeformer.cpp | 48 +- source/gameengine/Converter/BL_SkinDeformer.h | 6 +- .../gameengine/Converter/KX_ConvertActuators.cpp | 2 +- source/gameengine/GameLogic/SCA_ILogicBrick.cpp | 5 +- source/gameengine/GameLogic/SCA_ILogicBrick.h | 3 + source/gameengine/GameLogic/SCA_IObject.cpp | 2 +- .../gameengine/GameLogic/SCA_PropertyActuator.cpp | 33 +- source/gameengine/GameLogic/SCA_PropertyActuator.h | 8 +- source/gameengine/GamePlayer/common/GPC_Canvas.h | 6 + .../Ketsji/KX_BulletPhysicsController.cpp | 40 +- .../gameengine/Ketsji/KX_BulletPhysicsController.h | 9 +- source/gameengine/Ketsji/KX_CameraActuator.cpp | 62 +- source/gameengine/Ketsji/KX_CameraActuator.h | 8 +- source/gameengine/Ketsji/KX_ConstraintActuator.cpp | 6 +- source/gameengine/Ketsji/KX_GameObject.cpp | 86 +- source/gameengine/Ketsji/KX_GameObject.h | 10 + source/gameengine/Ketsji/KX_IPhysicsController.h | 5 +- .../gameengine/Ketsji/KX_OdePhysicsController.cpp | 5 +- source/gameengine/Ketsji/KX_OdePhysicsController.h | 4 +- source/gameengine/Ketsji/KX_ParentActuator.cpp | 51 +- source/gameengine/Ketsji/KX_ParentActuator.h | 7 +- source/gameengine/Ketsji/KX_RadarSensor.cpp | 11 +- .../gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp | 11 + .../gameengine/Ketsji/KX_SCA_AddObjectActuator.h | 3 + source/gameengine/Ketsji/KX_Scene.cpp | 279 ++++- source/gameengine/Ketsji/KX_Scene.h | 16 + source/gameengine/Ketsji/KX_SceneActuator.cpp | 45 +- source/gameengine/Ketsji/KX_SceneActuator.h | 3 + .../gameengine/Ketsji/KX_SumoPhysicsController.cpp | 5 +- .../gameengine/Ketsji/KX_SumoPhysicsController.h | 4 +- source/gameengine/Ketsji/KX_TrackToActuator.cpp | 12 + source/gameengine/Ketsji/KX_TrackToActuator.h | 1 + .../Physics/Bullet/CcdPhysicsController.cpp | 34 +- .../Physics/Bullet/CcdPhysicsController.h | 5 +- .../Physics/Bullet/CcdPhysicsEnvironment.cpp | 32 + .../Physics/Bullet/CcdPhysicsEnvironment.h | 9 + source/gameengine/PyDoc/KX_GameObject.py | 12 + .../gameengine/Rasterizer/RAS_2DFilterManager.cpp | 25 +- source/gameengine/Rasterizer/RAS_2DFilterManager.h | 2 +- source/gameengine/Rasterizer/RAS_ICanvas.h | 8 + source/gameengine/SceneGraph/SG_IObject.cpp | 6 +- source/gameengine/SceneGraph/SG_IObject.h | 2 +- source/gameengine/SceneGraph/SG_Node.cpp | 29 +- source/gameengine/SceneGraph/SG_Node.h | 2 +- 98 files changed, 5732 insertions(+), 817 deletions(-) create mode 100644 source/blender/include/BDR_gpencil.h create mode 100644 source/blender/include/BIF_drawgpencil.h create mode 100644 source/blender/makesdna/DNA_gpencil_types.h create mode 100644 source/blender/src/drawgpencil.c create mode 100644 source/blender/src/editaction_gpencil.c create mode 100644 source/blender/src/gpencil.c diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 62289d227c7..8c9634cba06 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -175,6 +175,7 @@ typedef struct Global { #define G_WEIGHTPAINT (1 << 15) #define G_TEXTUREPAINT (1 << 16) /* #define G_NOFROZEN (1 << 17) also removed */ +#define G_GREASEPENCIL (1 << 17) #define G_DRAWEDGES (1 << 18) #define G_DRAWCREASES (1 << 19) #define G_DRAWSEAMS (1 << 20) @@ -265,3 +266,4 @@ extern Global G; #endif + diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 26c5d186d87..6dfb77504fb 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -1437,6 +1437,9 @@ CollisionModifierData **get_collisionobjects(Object *self, int *numcollobj) if(coll_ob == self) continue; + + if( !collmd->bvhtree) + continue; if(numobj >= maxobj) { diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index e8bcae42d5a..54915058bab 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -915,7 +915,10 @@ void lattice_calc_modifiers(Object *ob) mti->deformVerts(md, ob, NULL, vertexCos, numVerts); } - if (vertexCos) { + /* always displist to make this work like derivedmesh */ + if (!vertexCos) vertexCos = lattice_getVertexCos(ob, &numVerts); + + { DispList *dl = MEM_callocN(sizeof(*dl), "lt_dl"); dl->type = DL_VERTS; dl->parts = 1; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index f70648965f4..7dca87d5c13 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2797,7 +2797,10 @@ void do_effectors(int pa_no, ParticleData *pa, ParticleKey *state, Object *ob, P epart= epsys->part; pd= epart->pd; totepart= epsys->totpart; - + + if(totepart <= 0) + continue; + if(pd->forcefield==PFIELD_HARMONIC){ /* every particle is mapped to only one harmonic effector particle */ p= pa_no%epsys->totpart; diff --git a/source/blender/blenlib/intern/arithb.c b/source/blender/blenlib/intern/arithb.c index dd9c76d9172..a31c769a5b3 100644 --- a/source/blender/blenlib/intern/arithb.c +++ b/source/blender/blenlib/intern/arithb.c @@ -59,6 +59,7 @@ /* A few small defines. Keep'em local! */ #define SMALL_NUMBER 1.e-8 +#define CLAMP(a, b, c) if((a)<(b)) (a)=(b); else if((a)>(c)) (a)=(c) #if defined(WIN32) || defined(__APPLE__) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3c629818b2d..090b1d7c6b6 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -69,6 +69,7 @@ #include "DNA_effect_types.h" #include "DNA_fileglobal_types.h" #include "DNA_group_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ipo_types.h" #include "DNA_image_types.h" #include "DNA_key_types.h" @@ -3698,6 +3699,32 @@ static void lib_link_screen_sequence_ipos(Main *main) /* ************ READ SCREEN ***************** */ +/* relinks grease-pencil data for 3d-view(s) - used for direct_link */ +static void link_gpencil(FileData *fd, bGPdata *gpd) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + /* relink layers */ + link_list(fd, &gpd->layers); + + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + /* relink frames */ + link_list(fd, &gpl->frames); + gpl->actframe= newdataadr(fd, gpl->actframe); + + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + /* relink strokes (and their points) */ + link_list(fd, &gpf->strokes); + + for (gps= gpf->strokes.first; gps; gps= gps->next) { + gps->points= newdataadr(fd, gps->points); + } + } + } +} + /* note: file read without screens option G_FILE_NO_UI; check lib pointers in call below */ static void lib_link_screen(FileData *fd, Main *main) @@ -3709,23 +3736,23 @@ static void lib_link_screen(FileData *fd, Main *main) if(sc->id.flag & LIB_NEEDLINK) { sc->id.us= 1; sc->scene= newlibadr(fd, sc->id.lib, sc->scene); - + sa= sc->areabase.first; while(sa) { SpaceLink *sl; - + sa->full= newlibadr(fd, sc->id.lib, sa->full); - + /* space handler scriptlinks */ lib_link_scriptlink(fd, &sc->id, &sa->scriptlink); - + for (sl= sa->spacedata.first; sl; sl= sl->next) { if(sl->spacetype==SPACE_VIEW3D) { View3D *v3d= (View3D*) sl; - + v3d->camera= newlibadr(fd, sc->id.lib, v3d->camera); v3d->ob_centre= newlibadr(fd, sc->id.lib, v3d->ob_centre); - + if(v3d->bgpic) { v3d->bgpic->ima= newlibadr_us(fd, sc->id.lib, v3d->bgpic->ima); } @@ -4081,6 +4108,10 @@ static void direct_link_screen(FileData *fd, bScreen *sc) v3d->bgpic= newdataadr(fd, v3d->bgpic); if(v3d->bgpic) v3d->bgpic->iuser.ok= 1; + if(v3d->gpd) { + v3d->gpd= newdataadr(fd, v3d->gpd); + link_gpencil(fd, v3d->gpd); + } v3d->localvd= newdataadr(fd, v3d->localvd); v3d->afterdraw.first= v3d->afterdraw.last= NULL; v3d->clipbb= newdataadr(fd, v3d->clipbb); @@ -4115,9 +4146,30 @@ static void direct_link_screen(FileData *fd, bScreen *sc) } else if(sl->spacetype==SPACE_NODE) { SpaceNode *snode= (SpaceNode *)sl; + + if(snode->gpd) { + snode->gpd= newdataadr(fd, snode->gpd); + link_gpencil(fd, snode->gpd); + } snode->nodetree= snode->edittree= NULL; snode->flag |= SNODE_DO_PREVIEW; } + else if(sl->spacetype==SPACE_SEQ) { + SpaceSeq *sseq= (SpaceSeq *)sl; + if(sseq->gpd) { + sseq->gpd= newdataadr(fd, sseq->gpd); + link_gpencil(fd, sseq->gpd); + } + } + else if(sl->spacetype==SPACE_ACTION) { + SpaceAction *sact= (SpaceAction *)sl; + + /* WARNING: action-editor doesn't have it's own gpencil data! + * so only adjust pointer, but DON'T LINK + */ + if (sact->gpd) + sact->gpd= newdataadr(fd, sact->gpd); + } } sa->v1= newdataadr(fd, sa->v1); diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index ca91f1dc346..b59dd851dfe 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -112,6 +112,7 @@ Important to know is that 'streaming' has been added to files, for Blender Publi #include "DNA_customdata_types.h" #include "DNA_effect_types.h" #include "DNA_group_types.h" +#include "DNA_gpencil_types.h" #include "DNA_image_types.h" #include "DNA_ipo_types.h" #include "DNA_fileglobal_types.h" @@ -1565,6 +1566,32 @@ static void write_scenes(WriteData *wd, ListBase *scebase) mywrite(wd, MYWRITE_FLUSH, 0); } +static void write_gpencil(WriteData *wd, bGPdata *gpd) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + /* write gpd data block to file */ + writestruct(wd, DATA, "bGPdata", 1, gpd); + + /* write grease-pencil layers to file */ + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + writestruct(wd, DATA, "bGPDlayer", 1, gpl); + + /* write this layer's frames to file */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + writestruct(wd, DATA, "bGPDframe", 1, gpf); + + /* write strokes */ + for (gps= gpf->strokes.first; gps; gps= gps->next) { + writestruct(wd, DATA, "bGPDstroke", 1, gps); + writestruct(wd, DATA, "bGPDspoint", gps->totpoints, gps->points); + } + } + } +} + static void write_screens(WriteData *wd, ListBase *scrbase) { bScreen *sc; @@ -1610,11 +1637,12 @@ static void write_screens(WriteData *wd, ListBase *scrbase) sl= sa->spacedata.first; while(sl) { if(sl->spacetype==SPACE_VIEW3D) { - View3D *v3d= (View3D*) sl; + View3D *v3d= (View3D *) sl; writestruct(wd, DATA, "View3D", 1, v3d); if(v3d->bgpic) writestruct(wd, DATA, "BGpic", 1, v3d->bgpic); if(v3d->localvd) writestruct(wd, DATA, "View3D", 1, v3d->localvd); if(v3d->clipbb) writestruct(wd, DATA, "BoundBox", 1, v3d->clipbb); + if(v3d->gpd) write_gpencil(wd, v3d->gpd); } else if(sl->spacetype==SPACE_IPO) { writestruct(wd, DATA, "SpaceIpo", 1, sl); @@ -1626,7 +1654,9 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "SpaceFile", 1, sl); } else if(sl->spacetype==SPACE_SEQ) { + SpaceSeq *sseq= (SpaceSeq *)sl; writestruct(wd, DATA, "SpaceSeq", 1, sl); + if(sseq->gpd) write_gpencil(wd, sseq->gpd); } else if(sl->spacetype==SPACE_OOPS) { SpaceOops *so= (SpaceOops *)sl; @@ -1689,7 +1719,9 @@ static void write_screens(WriteData *wd, ListBase *scrbase) writestruct(wd, DATA, "SpaceTime", 1, sl); } else if(sl->spacetype==SPACE_NODE){ + SpaceNode *snode= (SpaceNode *)sl; writestruct(wd, DATA, "SpaceNode", 1, sl); + if(snode->gpd) write_gpencil(wd, snode->gpd); } sl= sl->next; } diff --git a/source/blender/include/BDR_drawaction.h b/source/blender/include/BDR_drawaction.h index 91635123cb7..7cb0768e832 100644 --- a/source/blender/include/BDR_drawaction.h +++ b/source/blender/include/BDR_drawaction.h @@ -38,6 +38,7 @@ struct bAction; struct bActionGroup; struct Object; struct ListBase; +struct bGPDlayer; /* ****************************** Base Structs ****************************** */ @@ -82,6 +83,7 @@ void draw_ipo_channel(struct gla2DDrawInfo *di, struct Ipo *ipo, float ypos); void draw_agroup_channel(struct gla2DDrawInfo *di, struct bActionGroup *agrp, float ypos); void draw_action_channel(struct gla2DDrawInfo *di, struct bAction *act, float ypos); void draw_object_channel(struct gla2DDrawInfo *di, struct Object *ob, float ypos); +void draw_gpl_channel(struct gla2DDrawInfo *di, struct bGPDlayer *gpl, float ypos); /* Keydata Generation */ void icu_to_keylist(struct IpoCurve *icu, ListBase *keys, ListBase *blocks, ActKeysInc *aki); @@ -89,6 +91,7 @@ void ipo_to_keylist(struct Ipo *ipo, ListBase *keys, ListBase *blocks, ActKeysIn void agroup_to_keylist(struct bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki); void action_to_keylist(struct bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki); void ob_to_keylist(struct Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki); +void gpl_to_keylist(struct bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki); #endif /* BDR_DRAWACTION_H */ diff --git a/source/blender/include/BDR_gpencil.h b/source/blender/include/BDR_gpencil.h new file mode 100644 index 00000000000..d0ebd096ecb --- /dev/null +++ b/source/blender/include/BDR_gpencil.h @@ -0,0 +1,76 @@ +/** + * $Id: BDR_gpencil.h 14444 2008-04-16 22:40:48Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BDR_GPENCIL_H +#define BDR_GPENCIL_H + +struct ListBase; +struct bScreen; +struct ScrArea; +struct View3D; +struct SpaceNode; +struct SpaceSeq; +struct bGPdata; +struct bGPDlayer; +struct bGPDframe; + +/* ------------ Grease-Pencil API ------------------ */ + +void free_gpencil_strokes(struct bGPDframe *gpf); +void free_gpencil_frames(struct bGPDlayer *gpl); +void free_gpencil_layers(struct ListBase *list); +void free_gpencil_data(struct bGPdata *gpd); + +struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); +struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd); +struct bGPdata *gpencil_data_addnew(void); + +struct bGPdata *gpencil_data_duplicate(struct bGPdata *gpd); + +struct bGPdata *gpencil_data_getactive(struct ScrArea *sa); +short gpencil_data_setactive(struct ScrArea *sa, struct bGPdata *gpd); +struct bGPdata *gpencil_data_getetime(struct bScreen *sc); +void gpencil_data_setetime(struct bScreen *sc, struct bGPdata *gpd); + +void gpencil_frame_delete_laststroke(struct bGPDframe *gpf); + +struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, short addnew); +void gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); +struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd); +void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); +void gpencil_layer_delactive(struct bGPdata *gpd); + +void gpencil_delete_actframe(struct bGPdata *gpd); +void gpencil_delete_laststroke(struct bGPdata *gpd); + +void gpencil_delete_operation(short mode); +void gpencil_delete_menu(void); + +//short gpencil_paint(short mousebutton); +short gpencil_do_paint(struct ScrArea *sa); + +#endif /* BDR_GPENCIL_H */ diff --git a/source/blender/include/BIF_drawgpencil.h b/source/blender/include/BIF_drawgpencil.h new file mode 100644 index 00000000000..418446313df --- /dev/null +++ b/source/blender/include/BIF_drawgpencil.h @@ -0,0 +1,44 @@ +/** + * $Id: BIF_drawgpencil.h 14444 2008-04-16 22:40:48Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BIF_DRAWGPENCIL_H +#define BIF_DRAWGPENCIL_H + +struct ScrArea; +struct View3D; +struct SpaceNode; +struct SpaceSeq; +struct bGPdata; +struct uiBlock; + +short draw_gpencil_panel(struct uiBlock *block, struct bGPdata *gpd, struct ScrArea *sa); + +void draw_gpencil_2dview(struct ScrArea *sa, short onlyv2d); +void draw_gpencil_3dview(struct ScrArea *sa, short only3d); +void draw_gpencil_oglrender(struct View3D *v3d, int winx, int winy); + +#endif /* BIF_DRAWGPENCIL_H */ diff --git a/source/blender/include/BIF_editaction.h b/source/blender/include/BIF_editaction.h index 7e0f703681b..9f6751daeff 100644 --- a/source/blender/include/BIF_editaction.h +++ b/source/blender/include/BIF_editaction.h @@ -48,7 +48,8 @@ enum { ACTTYPE_FILLIPO, ACTTYPE_FILLCON, ACTTYPE_IPO, - ACTTYPE_SHAPEKEY + ACTTYPE_SHAPEKEY, + ACTTYPE_GPLAYER }; /* Macros for easier/more consistant state testing */ @@ -69,7 +70,10 @@ enum { #define EDITABLE_ICU(icu) ((icu->flag & IPO_PROTECT)==0) #define SEL_ICU(icu) (icu->flag & IPO_SELECT) -#define NLA_ACTION_SCALED (G.saction->pin==0 && OBACT && OBACT->action) +#define EDITABLE_GPL(gpl) ((gpl->flag & GP_LAYER_LOCKED)==0) +#define SEL_GPL(gpl) ((gpl->flag & GP_LAYER_ACTIVE) || (gpl->flag & GP_LAYER_SELECT)) + +#define NLA_ACTION_SCALED (G.saction->mode==SACTCONT_ACTION && G.saction->pin==0 && OBACT && OBACT->action) #define NLA_IPO_SCALED (OBACT && OBACT->action && G.sipo->pin==0 && G.sipo->actname) /* constants for setting ipo-interpolation type */ @@ -114,6 +118,8 @@ struct BWinEvent; struct Key; struct ListBase; struct TimeMarker; +struct bGPdata; +struct bGPDlayer; /* Key operations */ void transform_action_keys(int mode, int dummy); @@ -176,6 +182,24 @@ void action_add_localmarker(struct bAction *act, int frame); void action_rename_localmarker(struct bAction *act); void action_remove_localmarkers(struct bAction *act); +/* Grease-Pencil Data */ +void gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, short onlysel); + +void deselect_gpencil_layers(struct bGPdata *gpd, short select_mode); + +short is_gplayer_frame_selected(struct bGPDlayer *gpl); +void set_gplayer_frame_selection(struct bGPDlayer *gpl, short mode); +void select_gpencil_frames(struct bGPDlayer *gpl, short select_mode); +void select_gpencil_frame(struct bGPDlayer *gpl, int selx, short select_mode); +void borderselect_gplayer_frames(struct bGPDlayer *gpl, float min, float max, short select_mode); + +void delete_gpencil_layers(void); +void delete_gplayer_frames(struct bGPDlayer *gpl); +void duplicate_gplayer_frames(struct bGPDlayer *gpd); + +void snap_gplayer_frames(struct bGPDlayer *gpl, short mode); +void mirror_gplayer_frames(struct bGPDlayer *gpl, short mode); + /* ShapeKey stuff */ struct Key *get_action_mesh_key(void); int get_nearest_key_num(struct Key *key, short *mval, float *x); diff --git a/source/blender/include/BIF_space.h b/source/blender/include/BIF_space.h index 37be4a9eafc..4b2b8e14bb6 100644 --- a/source/blender/include/BIF_space.h +++ b/source/blender/include/BIF_space.h @@ -53,6 +53,7 @@ struct SpaceOops; #define VIEW3D_HANDLER_PREVIEW 4 #define VIEW3D_HANDLER_MULTIRES 5 #define VIEW3D_HANDLER_TRANSFORM 6 +#define VIEW3D_HANDLER_GREASEPENCIL 7 /* ipo handler codes */ #define IPO_HANDLER_PROPERTIES 20 @@ -73,11 +74,15 @@ struct SpaceOops; #define NLA_HANDLER_PROPERTIES 50 /* sequence handler codes */ -#define SEQ_HANDLER_PROPERTIES 60 +#define SEQ_HANDLER_PROPERTIES 60 +#define SEQ_HANDLER_GREASEPENCIL 61 /* imasel handler codes */ #define IMASEL_HANDLER_IMAGE 70 +/* nodes handler codes */ +#define NODES_HANDLER_GREASEPENCIL 80 + /* theme codes */ #define B_ADD_THEME 3301 #define B_DEL_THEME 3302 @@ -145,3 +150,4 @@ extern void mainwindow_close(void); #endif + diff --git a/source/blender/include/BSE_editaction_types.h b/source/blender/include/BSE_editaction_types.h index c531383accc..be210415973 100644 --- a/source/blender/include/BSE_editaction_types.h +++ b/source/blender/include/BSE_editaction_types.h @@ -38,7 +38,8 @@ typedef enum ALE_KEYTYPE { ALE_NONE = 0, ALE_IPO, ALE_ICU, - ALE_GROUP + ALE_GROUP, + ALE_GPFRAME, } ALE_KEYTYPE; /* This struct defines a structure used for quick access */ @@ -78,7 +79,8 @@ typedef enum ACTFILTER_FLAGS { typedef enum ACTCONT_TYPES { ACTCONT_NONE = 0, ACTCONT_ACTION, - ACTCONT_SHAPEKEY + ACTCONT_SHAPEKEY, + ACTCONT_GPENCIL } ACTCONT_TYPES; #endif diff --git a/source/blender/include/transform.h b/source/blender/include/transform.h index 4e3b80134f9..720b856a149 100644 --- a/source/blender/include/transform.h +++ b/source/blender/include/transform.h @@ -397,6 +397,7 @@ int Align(TransInfo *t, short mval[2]); /*********************** transform_conversions.c ********** */ struct ListBase; +void flushTransGPactionData(TransInfo *t); void flushTransIpoData(TransInfo *t); void flushTransUVs(TransInfo *t); void flushTransParticles(TransInfo *t); diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 18d2a1cb6f3..d7969a7379b 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -32,6 +32,7 @@ #include "DNA_listBase.h" #include "DNA_ID.h" +#include "DNA_gpencil_types.h" #include "DNA_view2d_types.h" #include "DNA_userdef_types.h" @@ -183,8 +184,11 @@ typedef struct SpaceAction { View2D v2d; bAction *action; /* the currently active action */ - short flag, autosnap; /* flag: bitmapped settings; autosnap: automatic keyframe snapping mode */ - short pin, actnr, lock; /* pin: keep showing current action; actnr: used for finding chosen action from menu; lock: lock time to other windows */ + bGPdata *gpd; /* the currently active gpencil block (for editing) */ + + char mode, autosnap; /* mode: editing context; autosnap: automatic keyframe snapping mode */ + short flag, actnr; /* flag: bitmapped settings; */ + short pin, lock; /* pin: keep showing current action; actnr: used for finding chosen action from menu; lock: lock time to other windows */ short actwidth; /* width of the left-hand side name panel (in pixels?) */ float timeslide; /* for Time-Slide transform mode drawing - current frame? */ } SpaceAction; @@ -238,6 +242,18 @@ typedef enum SACTION_FLAG { SACTION_NODRAWGCOLORS = (1<<7) } SACTION_FLAG; +/* SpaceAction Mode Settings */ +typedef enum SACTCONT_MODES { + /* action (default) */ + SACTCONT_ACTION = 0, + /* editing of shapekey's IPO block */ + SACTCONT_SHAPEKEY, + /* editing of gpencil data */ + SACTCONT_GPENCIL, + /* dopesheet (unimplemented... future idea?) */ + SACTCONT_DOPESHEET +} SACTCONTEXT_MODES; + /* SpaceAction AutoSnap Settings (also used by SpaceNLA) */ typedef enum SACTSNAP_MODES { /* no auto-snap */ diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h new file mode 100644 index 00000000000..eafd886981b --- /dev/null +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -0,0 +1,141 @@ +/** + * $Id: DNA_gpencil_types.h 8768 2006-11-07 00:10:37Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation. + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef DNA_GPENCIL_TYPES_H +#define DNA_GPENCIL_TYPES_H + +#include "DNA_listBase.h" +#include "DNA_ID.h" + +/* Grease-Pencil Annotations - 'Stroke Point' + * -> Coordinates may either be 2d or 3d depending on settings at the time + * -> Coordinates of point on stroke, in proportions of window size + * (i.e. n/1000). This assumes that the bottom-left corner is (0,0) + */ +typedef struct bGPDspoint { + float x, y, z; /* co-ordinates of point (usually 2d, but can be 3d as well) */ + float pressure; /* pressure of input device (from 0 to 1) at this point */ +} bGPDspoint; + +/* Grease-Pencil Annotations - 'Stroke' + * -> A stroke represents a (simplified version) of the curve + * drawn by the user in one 'mousedown'->'mouseup' operation + */ +typedef struct bGPDstroke { + struct bGPDstroke *next, *prev; + + bGPDspoint *points; /* array of data-points for stroke */ + int totpoints; /* number of data-points in array */ + + short thickness; /* thickness of stroke (currently not used) */ + short flag; /* various settings about this stroke */ +} bGPDstroke; + +/* bGPDstroke->flag */ + /* stroke is in 3d-space */ +#define GP_STROKE_3DSPACE (1<<0) + /* stroke is in 2d-space */ +#define GP_STROKE_2DSPACE (1<<1) + + +/* Grease-Pencil Annotations - 'Frame' + * -> Acts as storage for the 'image' formed by strokes + */ +typedef struct bGPDframe { + struct bGPDframe *next, *prev; + + ListBase strokes; /* list of the simplified 'strokes' that make up the frame's data */ + + int framenum; /* frame number of this frame */ + int flag; /* temp settings */ +} bGPDframe; + +/* bGPDframe->flag */ + /* frame is being painted on */ +#define GP_FRAME_PAINT (1<<0) + /* for editing in Action Editor */ +#define GP_FRAME_SELECT (1<<1) + + +/* Grease-Pencil Annotations - 'Layer' */ +typedef struct bGPDlayer { + struct bGPDlayer *next, *prev; + + ListBase frames; /* list of annotations to display for frames (bGPDframe list) */ + bGPDframe *actframe; /* active frame (should be the frame that is currently being displayed) */ + + int flag; /* settings for layer */ + short thickness; /* current thickness to apply to strokes */ + short gstep; /* max number of frames between active and ghost to show (0=only those on either side) */ + + float color[4]; /* color that should be used to draw all the strokes in this layer */ + + char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3") */ +} bGPDlayer; + +/* bGPDlayer->flag */ + /* don't display layer */ +#define GP_LAYER_HIDE (1<<0) + /* protected from further editing */ +#define GP_LAYER_LOCKED (1<<1) + /* layer is 'active' layer being edited */ +#define GP_LAYER_ACTIVE (1<<2) + /* draw points of stroke for debugging purposes */ +#define GP_LAYER_DRAWDEBUG (1<<3) + /* do onionskinning */ +#define GP_LAYER_ONIONSKIN (1<<4) + /* for editing in Action Editor */ +#define GP_LAYER_SELECT (1<<5) + + +/* Grease-Pencil Annotations - 'DataBlock' */ +typedef struct bGPdata { + /* saved Grease-Pencil data */ + ListBase layers; /* bGPDlayers */ + int flag; /* settings for this datablock */ + + /* not-saved stroke buffer data (only used during paint-session) + * - buffer must be initialised before use, but freed after + * whole paint operation is over + */ + short sbuffer_size; /* number of elements currently in cache */ + short sbuffer_sflag; /* flags for stroke that cache represents */ + bGPDspoint *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */ +} bGPdata; + +/* bGPdata->flag */ + /* draw this datablock's data (not used) */ +#define GP_DATA_DISP (1<<0) + /* show debugging info in viewport (i.e. status print) */ +#define GP_DATA_DISPINFO (1<<1) + /* is the block being shown in Action Editor */ +#define GP_DATA_EDITTIME (1<<2) + /* is the block overriding all clicks? */ +#define GP_DATA_EDITPAINT (1<<3) + /* new strokes are added in viewport space */ +#define GP_DATA_VIEWALIGN (1<<4) + +#endif /* DNA_GPENCIL_TYPES_H */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index bc30a12ff27..a8694dfb7f5 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -50,6 +50,7 @@ struct RenderInfo; struct bNodeTree; struct uiBlock; struct FileList; +struct bGPdata; /** * The base structure all the other spaces @@ -150,6 +151,8 @@ typedef struct SpaceSeq { short zebra; int flag; float zoom; + + struct bGPdata *gpd; /* grease-pencil data */ } SpaceSeq; typedef struct SpaceFile { @@ -339,6 +342,8 @@ typedef struct SpaceNode { float blockscale; struct ScrArea *area; + short blockhandler[8]; + View2D v2d; struct ID *id, *from; /* context, no need to save in file? well... pinning... */ @@ -351,11 +356,13 @@ typedef struct SpaceNode { struct bNodeTree *nodetree, *edittree; int treetype, pad; /* treetype: as same nodetree->type */ + struct bGPdata *gpd; /* grease-pencil data */ } SpaceNode; /* snode->flag */ #define SNODE_DO_PREVIEW 1 #define SNODE_BACKDRAW 2 +#define SNODE_DISPGP 4 typedef struct SpaceImaSel { SpaceLink *next, *prev; @@ -657,6 +664,7 @@ typedef struct SpaceImaSel { #define SEQ_MARKER_TRANS 2 #define SEQ_DRAW_COLOR_SEPERATED 4 #define SEQ_DRAW_SAFE_MARGINS 8 +#define SEQ_DRAW_GPENCIL 16 /* space types, moved from DNA_screen_types.h */ enum { diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index c21d629be83..135272b9ac2 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -40,6 +40,7 @@ struct Base; struct BoundBox; struct RenderInfo; struct RetopoViewData; +struct bGPdata; /* This is needed to not let VC choke on near and far... old * proprietary MS extensions... */ @@ -53,9 +54,12 @@ struct RetopoViewData; #include "DNA_listBase.h" #include "DNA_image_types.h" +/* ******************************** */ + /* The near/far thing is a Win EXCEPTION. Thus, leave near/far in the * code, and patch for windows. */ - + +/* Background Picture in 3D-View */ typedef struct BGpic { struct Image *ima; struct ImageUser iuser; @@ -63,6 +67,9 @@ typedef struct BGpic { short xim, yim; } BGpic; +/* ********************************* */ + +/* 3D ViewPort Struct */ typedef struct View3D { struct SpaceLink *next, *prev; int spacetype; @@ -135,9 +142,10 @@ typedef struct View3D { char ndoffilter; /*filter for 6DOF devices 0 normal, 1 dominant */ void *properties_storage; /* Nkey panel stores stuff here, not in file */ - + struct bGPdata *gpd; /* Grease-Pencil Data (annotation layers) */ } View3D; + /* View3D->flag (short) */ #define V3D_MODE (16+32+64+128+256+512) #define V3D_DISPIMAGE 1 @@ -158,10 +166,12 @@ typedef struct View3D { #define V3D_DRAW_CENTERS 32768 /* View3d->flag2 (short) */ +#define V3D_MODE2 (32) #define V3D_OPP_DIRECTION_NAME 1 #define V3D_FLYMODE 2 #define V3D_DEPRECATED 4 /* V3D_TRANSFORM_SNAP, moved to a scene setting */ #define V3D_SOLID_TEX 8 +#define V3D_DISPGP 16 /* View3D->around */ #define V3D_CENTER 0 @@ -203,3 +213,4 @@ typedef struct View3D { #endif + diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 83f4e633fa1..3818d66b39c 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -126,6 +126,7 @@ char *includefiles[] = { "DNA_customdata_types.h", "DNA_particle_types.h", "DNA_cloth_types.h", + "DNA_gpencil_types.h", // if you add files here, please add them at the end // of makesdna.c (this file) as well @@ -1147,4 +1148,5 @@ int main(int argc, char ** argv) #include "DNA_customdata_types.h" #include "DNA_particle_types.h" #include "DNA_cloth_types.h" +#include "DNA_gpencil_types.h" /* end of list */ diff --git a/source/blender/python/api2_2x/Blender.c b/source/blender/python/api2_2x/Blender.c index d8385c1d660..420d292cdce 100644 --- a/source/blender/python/api2_2x/Blender.c +++ b/source/blender/python/api2_2x/Blender.c @@ -708,7 +708,7 @@ static PyObject *Blender_Save( PyObject * self, PyObject * args ) "expected filename and optional int (overwrite flag) as arguments" ); for( li = G.main->library.first; li; li = li->id.next ) { - if( BLI_streq( li->name, fname ) ) { + if( li->parent==NULL && BLI_streq( li->name, fname ) ) { return EXPP_ReturnPyObjError( PyExc_AttributeError, "cannot overwrite used library" ); } diff --git a/source/blender/python/api2_2x/Library.c b/source/blender/python/api2_2x/Library.c index 799735c2062..468263c4208 100644 --- a/source/blender/python/api2_2x/Library.c +++ b/source/blender/python/api2_2x/Library.c @@ -1135,9 +1135,78 @@ static PyObject *M_Library_Load(PyObject *self, PyObject * args) return (PyObject *)lib; } +static PyObject *M_Library_GetPaths(PyObject *self, PyObject * args) +{ + PyObject *list; + PyObject *name; + int type=0; + Library *lib; + + if( !PyArg_ParseTuple( args, "|i", &type ) || type < 0 || type > 2 ) { + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected an int between 0 and 2." ); + } + + list = PyList_New(0); + + for(lib= G.main->library.first; lib; lib= lib->id.next) { + if (type==0) { + /* any type is ok */ + } else if (type==1 && lib->parent == 0) { + /* only direct linked */ + } else if (type==2 && lib->parent != 0) { + /* only indirect */ + } else { + continue; /* incompatible type */ + } + + name = PyString_FromString(lib->name); + PyList_Append(list, name); + Py_DECREF(name); + } + return list; +} + +static PyObject *M_Library_ReplacePath(PyObject *self, PyObject * args) +{ + char *name_from, *name_to; + Library *lib; + + if( !PyArg_ParseTuple( args, "ss", &name_from, &name_to )) { + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected the name of a library path" ); + } + + for(lib= G.main->library.first; lib; lib= lib->id.next) { + if (strcmp(lib->name, name_from)==0) { + if (lib->parent) { + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "path is indirectly linked, cannot be changed." ); + } + + if (strlen(name_to) > sizeof(lib->name)) { + return EXPP_ReturnPyObjError( PyExc_RuntimeError, + "string length too long, cannot set path." ); + } + + strcpy(lib->name, name_to); + Py_RETURN_NONE; + } + } + + return EXPP_ReturnPyObjError( PyExc_ValueError, + "path given does not exist as a library" ); +} + + + static struct PyMethodDef M_Library_methods[] = { {"load", (PyCFunction)M_Library_Load, METH_VARARGS, "(string) - declare a .blend file for use as a library"}, + {"paths", (PyCFunction)M_Library_GetPaths, METH_VARARGS, + "(type) - return a list of library paths, type 0 for all, 1 only direct links, 2 only indirect links"}, + {"replace", (PyCFunction)M_Library_ReplacePath, METH_VARARGS, + "(from, to) - replace the path of an existing, directly linked library."}, {NULL, NULL, 0, NULL} }; diff --git a/source/blender/python/api2_2x/Particle.c b/source/blender/python/api2_2x/Particle.c index 95de9757b87..2c2e724129e 100644 --- a/source/blender/python/api2_2x/Particle.c +++ b/source/blender/python/api2_2x/Particle.c @@ -40,6 +40,7 @@ #include "BKE_material.h" #include "BKE_utildefines.h" #include "BKE_pointcache.h" +#include "BKE_DerivedMesh.h" #include "BIF_editparticle.h" #include "BIF_space.h" #include "blendef.h" @@ -799,22 +800,27 @@ static PyObject *Part_freeEdit( BPy_PartSys * self, PyObject * args ){ Py_RETURN_NONE; } -static PyObject *Part_GetLoc( BPy_PartSys * self, PyObject * args ){ +static PyObject *Part_GetLoc( BPy_PartSys * self, PyObject * args ) +{ ParticleSystem *psys = 0L; Object *ob = 0L; PyObject *partlist,*seglist; - PyObject* loc = 0L; ParticleCacheKey **cache,*path; + PyObject* loc = 0L; ParticleKey state; - float cfra=bsystem_time(ob,(float)CFRA,0.0); + DerivedMesh* dm; + float cfra; int i,j,k; + float vm[4][4],wm[4][4]; int childexists = 0; int all = 0; int id = 0; + cfra = bsystem_time(ob,(float)CFRA,0.0); + if( !PyArg_ParseTuple( args, "|ii", &all,&id ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, - "expected one optional integer as argument" ); + "expected two optional integers as arguments" ); psys = self->psys; ob = self->object; @@ -822,88 +828,118 @@ static PyObject *Part_GetLoc( BPy_PartSys * self, PyObject * args ){ if (!ob || !psys) Py_RETURN_NONE; - if (psys->part->type == 2){ - cache=psys->pathcache; + G.rendering = 1; - /* little hack to calculate hair steps in render mode */ - psys->renderdata = (void*)(int)1; + /* Just to create a valid rendering context */ + psys_render_set(ob,psys,vm,wm,0,0,0); - psys_cache_paths(ob, psys, cfra, 1); + dm = mesh_create_derived_render(ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); - psys->renderdata = NULL; + if ( !psys_check_enabled(ob,psys) ){ + G.rendering = 0; + psys_render_restore(ob,psys); + Particle_Recalc(self,1); + Py_RETURN_NONE; + } - partlist = PyList_New( 0 ); - if( !partlist ) - return EXPP_ReturnPyObjError( PyExc_MemoryError, "PyList() failed" ); + partlist = PyList_New( 0 ); + if( !partlist ){ + PyErr_SetString( PyExc_MemoryError, "PyList_New() failed" ); + goto error; + } - for(i = 0; i < psys->totpart; i++){ - path=cache[i]; - seglist = PyList_New( 0 ); - k = path->steps+1; - for( j = 0; j < k ; j++){ - loc = PyTuple_New(3); - - PyTuple_SetItem(loc,0,PyFloat_FromDouble((double)path->co[0])); - PyTuple_SetItem(loc,1,PyFloat_FromDouble((double)path->co[1])); - PyTuple_SetItem(loc,2,PyFloat_FromDouble((double)path->co[2])); - - if ( (PyList_Append(seglist,loc) < 0) ){ - Py_DECREF(seglist); - Py_DECREF(partlist); - Py_XDECREF(loc); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, - "Couldn't append item to PyList" ); + if (psys->part->type == PART_HAIR){ + cache = psys->pathcache; + + if ( ((self->psys->part->draw & PART_DRAW_PARENT) && (self->psys->part->childtype != 0)) || (self->psys->part->childtype == 0) ){ + + for(i = 0; i < psys->totpart; i++){ + seglist = PyList_New( 0 ); + if (!seglist){ + PyErr_SetString( PyExc_MemoryError, + "PyList_New() failed" ); + goto error; + } + + path=cache[i]; + k = path->steps+1; + for( j = 0; j < k ; j++, path++){ + loc = Py_BuildValue("(fff)",(double)path->co[0], + (double)path->co[1], (double)path->co[2]); + + if (!loc){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; + } + + if ( (PyList_Append(seglist,loc) < 0) ){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; + } + Py_DECREF(loc); /* PyList_Append increfs */ + loc = NULL; } - Py_DECREF(loc); /* PyList_Append increfs */ - path++; - } - if ( PyList_Append(partlist,seglist) < 0 ){ - Py_DECREF(seglist); - Py_DECREF(partlist); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, - "Couldn't append item to PyList" ); + if ( PyList_Append(partlist,seglist) < 0 ){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; + } + Py_DECREF(seglist); /* PyList_Append increfs */ + seglist = NULL; } - Py_DECREF(seglist); /* PyList_Append increfs */ } cache=psys->childcache; for(i = 0; i < psys->totchild; i++){ - path=cache[i]; seglist = PyList_New( 0 ); - k = path->steps+1; - for( j = 0; j < k ; j++){ - loc = PyTuple_New(3); + if (!seglist){ + PyErr_SetString( PyExc_MemoryError, + "PyList_New() failed" ); + goto error; + } - PyTuple_SetItem(loc,0,PyFloat_FromDouble((double)path->co[0])); - PyTuple_SetItem(loc,1,PyFloat_FromDouble((double)path->co[1])); - PyTuple_SetItem(loc,2,PyFloat_FromDouble((double)path->co[2])); + path=cache[i]; + k = path->steps+1; + for( j = 0; j < k ; j++, path++ ){ + loc = Py_BuildValue("(fff)",(double)path->co[0], + (double)path->co[1], (double)path->co[2]); + + if (!loc){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; + } if ( PyList_Append(seglist,loc) < 0){ - Py_DECREF(partlist); - Py_XDECREF(loc); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, + PyErr_SetString( PyExc_RuntimeError, "Couldn't append item to PyList" ); + goto error; } Py_DECREF(loc);/* PyList_Append increfs */ - path++; + loc = NULL; } if ( PyList_Append(partlist,seglist) < 0){ - Py_DECREF(partlist); - Py_XDECREF(loc); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, + PyErr_SetString( PyExc_RuntimeError, "Couldn't append item to PyList" ); + goto error; } Py_DECREF(seglist); /* PyList_Append increfs */ + seglist = NULL; } - } else { int init; - partlist = PyList_New( 0 ); - if( !partlist ) - return EXPP_ReturnPyObjError( PyExc_MemoryError, "PyList() failed" ); + char *fmt = NULL; + + if(id) + fmt = "(fffi)"; + else + fmt = "(fff)"; if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) childexists = 1; @@ -919,55 +955,67 @@ static PyObject *Part_GetLoc( BPy_PartSys * self, PyObject * args ){ init = 1; if (init){ - if (!id) - loc = PyTuple_New(3); - else - loc = PyTuple_New(4); - PyTuple_SetItem(loc,0,PyFloat_FromDouble((double)state.co[0])); - PyTuple_SetItem(loc,1,PyFloat_FromDouble((double)state.co[1])); - PyTuple_SetItem(loc,2,PyFloat_FromDouble((double)state.co[2])); - if (id) - PyTuple_SetItem(loc,3,PyInt_FromLong(i)); + loc = Py_BuildValue(fmt,(double)state.co[0], + (double)state.co[1], (double)state.co[2],i); + + if (!loc){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; + } if ( PyList_Append(partlist,loc) < 0 ){ - Py_DECREF(partlist); - Py_XDECREF(loc); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, - "Couldn't append item to PyList" ); + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; } - Py_DECREF(loc);/* PyList_Append increfs */ - } - else { - if ( all ){ - if ( PyList_Append(partlist,Py_None) < 0 ){ - Py_DECREF(partlist); - return EXPP_ReturnPyObjError( PyExc_RuntimeError, - "Couldn't append item to PyList" ); - } - Py_DECREF(Py_None); /* PyList_Append increfs */ + Py_DECREF(loc); + loc = NULL; + } else { + if ( all && PyList_Append(partlist,Py_None) < 0 ){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; } } } } + + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); return partlist; + +error: + Py_XDECREF(partlist); + Py_XDECREF(seglist); + Py_XDECREF(loc); + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); + return NULL; } -static PyObject *Part_GetRot( BPy_PartSys * self, PyObject * args ){ +static PyObject *Part_GetRot( BPy_PartSys * self, PyObject * args ) +{ ParticleSystem *psys = 0L; Object *ob = 0L; PyObject *partlist = 0L; PyObject* loc = 0L; ParticleKey state; + DerivedMesh* dm; + float vm[4][4],wm[4][4]; int i; int childexists = 0; int all = 0; int id = 0; + char *fmt = NULL; float cfra=bsystem_time(ob,(float)CFRA,0.0); if( !PyArg_ParseTuple( args, "|ii", &all, &id ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, - "expected one optional integer as argument" ); + "expected two optional integers as arguments" ); psys = self->psys; ob = self->object; @@ -975,63 +1023,105 @@ static PyObject *Part_GetRot( BPy_PartSys * self, PyObject * args ){ if (!ob || !psys) Py_RETURN_NONE; - if (psys->part->type != 2){ + G.rendering = 1; + + /* Just to create a valid rendering context */ + psys_render_set(ob,psys,vm,wm,0,0,0); + + dm = mesh_create_derived_render(ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); + + if ( !psys_check_enabled(ob,psys) ){ + G.rendering = 0; + psys_render_restore(ob,psys); + Particle_Recalc(self,1); + Py_RETURN_NONE; + } + + if (psys->part->type != PART_HAIR){ partlist = PyList_New( 0 ); + if( !partlist ){ + PyErr_SetString( PyExc_MemoryError, "PyList_New() failed" ); + goto error; + } + if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) childexists = 1; + if(id) + fmt = "(ffffi)"; + else + fmt = "(ffff)"; + for (i = 0; i < psys->totpart + psys->totchild; i++){ if (childexists && (i < psys->totpart)) continue; state.time = cfra; if(psys_get_particle_state(ob,psys,i,&state,0)==0){ - if ( all ){ - PyList_Append(partlist,Py_None); - Py_DECREF(Py_None); /* PyList_Append increfs */ - continue; - } else { - continue; + if ( all && PyList_Append(partlist,Py_None) < 0){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; + } + } else { + loc = Py_BuildValue(fmt,(double)state.rot[0], (double)state.rot[1], + (double)state.rot[2], (double)state.rot[3], i); + + if (!loc){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; + } + if (PyList_Append(partlist,loc) < 0){ + PyErr_SetString ( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; } + Py_DECREF(loc); /* PyList_Append increfs */ + loc = NULL; } - if (!id) - loc = PyTuple_New(4); - else - loc = PyTuple_New(5); - PyTuple_SetItem(loc,0,PyFloat_FromDouble((double)state.rot[0])); - PyTuple_SetItem(loc,1,PyFloat_FromDouble((double)state.rot[1])); - PyTuple_SetItem(loc,2,PyFloat_FromDouble((double)state.rot[2])); - PyTuple_SetItem(loc,3,PyFloat_FromDouble((double)state.rot[3])); - if (id) - PyTuple_SetItem(loc,4,PyInt_FromLong(i)); - PyList_Append(partlist,loc); - Py_DECREF(loc); /* PyList_Append increfs */ } + } else { + partlist = EXPP_incr_ret( Py_None ); } + + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); return partlist; + +error: + Py_XDECREF(partlist); + Py_XDECREF(loc); + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); + return NULL; } -static PyObject *Part_GetSize( BPy_PartSys * self, PyObject * args ){ +static PyObject *Part_GetSize( BPy_PartSys * self, PyObject * args ) +{ ParticleKey state; ParticleSystem *psys = 0L; ParticleData *data; Object *ob = 0L; PyObject *partlist,*tuple; - PyObject* siz = 0L; + DerivedMesh* dm; + float vm[4][4],wm[4][4]; float size; int i; int childexists = 0; int all = 0; int id = 0; + char *fmt = NULL; float cfra=bsystem_time(ob,(float)CFRA,0.0); if( !PyArg_ParseTuple( args, "|ii", &all, &id ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, - "expected one optional integer as argument" ); - - data = self->psys->particles; + "expected two optional integers as arguments" ); psys = self->psys; ob = self->object; @@ -1039,13 +1129,39 @@ static PyObject *Part_GetSize( BPy_PartSys * self, PyObject * args ){ if (!ob || !psys) Py_RETURN_NONE; - partlist = PyList_New( 0 ); + G.rendering = 1; - if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) - childexists = 1; + /* Just to create a valid rendering context */ + psys_render_set(ob,psys,vm,wm,0,0,0); + + dm = mesh_create_derived_render(ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); + data = self->psys->particles; + + if ( !psys_check_enabled(ob,psys) ){ + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); + Py_RETURN_NONE; + } + + partlist = PyList_New( 0 ); + + if( !partlist ){ + PyErr_SetString( PyExc_MemoryError, "PyList_New() failed" ); + goto error; + } + + if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) + childexists = 1; + + if(id) + fmt = "(fi)"; + else + fmt = "f"; - for (i = 0; i < psys->totpart + psys->totchild; i++, data++){ - if (psys->part->type != 2){ + for (i = 0; i < psys->totpart + psys->totchild; i++, data++){ + if (psys->part->type != PART_HAIR){ if (childexists && (i < psys->totpart)) continue; @@ -1061,43 +1177,61 @@ static PyObject *Part_GetSize( BPy_PartSys * self, PyObject * args ){ ChildParticle *cpa= &psys->child[i-psys->totpart]; size = psys_get_child_size(psys,cpa,cfra,0); } - if (id){ - tuple = PyTuple_New(2); - PyTuple_SetItem(tuple,0,PyFloat_FromDouble((double)size)); - PyTuple_SetItem(tuple,1,PyInt_FromLong(i)); - PyList_Append(partlist,tuple); - Py_DECREF(tuple); - } else { - siz = PyFloat_FromDouble((double)size); - PyList_Append(partlist,siz); - Py_DECREF(siz); + + tuple = Py_BuildValue(fmt,(double)size,i); + + if (!tuple){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; + } + + if (PyList_Append(partlist,tuple) < 0){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; } + Py_DECREF(tuple); + tuple = NULL; } } + + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); return partlist; + +error: + Py_XDECREF(partlist); + Py_XDECREF(tuple); + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); + return NULL; } -static PyObject *Part_GetAge( BPy_PartSys * self, PyObject * args ){ +static PyObject *Part_GetAge( BPy_PartSys * self, PyObject * args ) +{ ParticleKey state; ParticleSystem *psys = 0L; ParticleData *data; Object *ob = 0L; PyObject *partlist,*tuple; - PyObject* lif = 0L; + DerivedMesh* dm; + float vm[4][4],wm[4][4]; float life; int i; int childexists = 0; int all = 0; int id = 0; + char *fmt = NULL; float cfra=bsystem_time(ob,(float)CFRA,0.0); if( !PyArg_ParseTuple( args, "|ii", &all, &id ) ) return EXPP_ReturnPyObjError( PyExc_TypeError, - "expected one optional integer as argument" ); - - data = self->psys->particles; + "expected two optional integers as arguments" ); psys = self->psys; ob = self->object; @@ -1105,13 +1239,37 @@ static PyObject *Part_GetAge( BPy_PartSys * self, PyObject * args ){ if (!ob || !psys) Py_RETURN_NONE; - partlist = PyList_New( 0 ); + G.rendering = 1; - if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) - childexists = 1; + /* Just to create a valid rendering context */ + psys_render_set(ob,psys,vm,wm,0,0,0); + + dm = mesh_create_derived_render(ob,CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL); + dm->release(dm); + data = self->psys->particles; + + if ( !psys_check_enabled(ob,psys) ){ + psys_render_restore(ob,psys); + G.rendering = 0; + Py_RETURN_NONE; + } + + partlist = PyList_New( 0 ); + if( !partlist ){ + PyErr_SetString( PyExc_MemoryError, "PyList_New() failed" ); + goto error; + } + + if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT)) + childexists = 1; + + if(id) + fmt = "(fi)"; + else + fmt = "f"; - for (i = 0; i < psys->totpart + psys->totchild; i++, data++){ - if (psys->part->type != 2){ + for (i = 0; i < psys->totpart + psys->totchild; i++, data++){ + if (psys->part->type != PART_HAIR){ if (childexists && (i < psys->totpart)) continue; @@ -1128,20 +1286,37 @@ static PyObject *Part_GetAge( BPy_PartSys * self, PyObject * args ){ ChildParticle *cpa= &psys->child[i-psys->totpart]; life = psys_get_child_time(psys,cpa,cfra); } - if (id){ - tuple = PyTuple_New(2); - PyTuple_SetItem(tuple,0,PyFloat_FromDouble((double)life)); - PyTuple_SetItem(tuple,1,PyInt_FromLong(i)); - PyList_Append(partlist,tuple); - Py_DECREF(tuple); - } else { - lif = PyFloat_FromDouble((double)life); - PyList_Append(partlist,lif); - Py_DECREF(lif); + + tuple = Py_BuildValue(fmt,(double)life,i); + + if (!tuple){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't build tuple" ); + goto error; } + + if (PyList_Append(partlist,tuple) < 0){ + PyErr_SetString( PyExc_RuntimeError, + "Couldn't append item to PyList" ); + goto error; + } + Py_DECREF(tuple); + tuple = NULL; } } + + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); return partlist; + +error: + Py_XDECREF(partlist); + Py_XDECREF(tuple); + psys_render_restore(ob,psys); + G.rendering = 0; + Particle_Recalc(self,1); + return NULL; } @@ -1376,11 +1551,8 @@ static int Part_set2d( BPy_PartSys * self, PyObject * args ) { int number; - if( !PyInt_Check( args ) ) { - char errstr[128]; - sprintf ( errstr, "expected int argument" ); - return EXPP_ReturnIntError( PyExc_TypeError, errstr ); - } + if( !PyInt_Check( args ) ) + return EXPP_ReturnIntError( PyExc_TypeError, "expected int argument" ); number = PyInt_AS_LONG( args ); @@ -1526,7 +1698,7 @@ static int Part_setRandEmission( BPy_PartSys * self, PyObject * args ) static PyObject *Part_getRandEmission( BPy_PartSys * self ) { - return PyInt_FromLong( ((long)( self->psys->part->flag & PART_BOIDS_2D )) > 0 ); + return PyInt_FromLong( ((long)( self->psys->part->flag & PART_TRAND )) > 0 ); } static int Part_setParticleDist( BPy_PartSys * self, PyObject * args ) @@ -1546,7 +1718,7 @@ static int Part_setParticleDist( BPy_PartSys * self, PyObject * args ) return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } - self->psys->part->from = number; + self->psys->part->from = (short)number; Particle_RecalcPsys_distr(self,1); @@ -1603,7 +1775,7 @@ static int Part_setDist( BPy_PartSys * self, PyObject * args ) return EXPP_ReturnIntError( PyExc_TypeError, errstr ); } - self->psys->part->distr = number; + self->psys->part->distr = (short)number; Particle_RecalcPsys_distr(self,1); @@ -1731,7 +1903,7 @@ static int Part_setTargetPsys( BPy_PartSys * self, PyObject * args ){ res = EXPP_setIValueRange( args, &self->psys->target_psys, 0, tottpsys, 'h' ); - if((psys=psys_get_current(ob))){ + if( ( psys = psys_get_current(ob) ) ){ if(psys->keyed_ob==ob || psys->target_ob==ob){ if(psys->keyed_ob==ob) psys->keyed_ob=NULL; diff --git a/source/blender/python/api2_2x/doc/LibData.py b/source/blender/python/api2_2x/doc/LibData.py index 47bd7fdb763..daa6a186531 100644 --- a/source/blender/python/api2_2x/doc/LibData.py +++ b/source/blender/python/api2_2x/doc/LibData.py @@ -27,17 +27,37 @@ Example:: """ def load(filename,relative=False): - """ - Select an existing .blend file for use as a library. Unlike the - Library module, multiple libraries can be defined at the same time. - - @type filename: string - @param filename: The filename of a Blender file. Filenames starting with "//" will be loaded relative to the blend file's location. - @type relative: boolean - @param relative: Convert relative paths to absolute paths (default). Setting this parameter to True will leave paths relative. - @rtype: Library - @return: return a L{Library} object. - """ + """ + Select an existing .blend file for use as a library. Unlike the + Library module, multiple libraries can be defined at the same time. + + @type filename: string + @param filename: The filename of a Blender file. Filenames starting with "//" will be loaded relative to the blend file's location. + @type relative: boolean + @param relative: Convert relative paths to absolute paths (default). Setting this parameter to True will leave paths relative. + @rtype: Library + @return: return a L{Library} object. + """ + +def paths(link=0): + """ + Returns a list of paths used in the current blend file. + + @type link: int + @param link: 0 (default if no args given) for all library paths, 1 for directly linked library paths only, 2 for indirectly linked library paths only. + @rtype: List + @return: return a list of path strings. + """ + +def replace(pathFrom, pathTo): + """ + Replaces an existing directly linked path. + + @type pathFrom: string + @param pathFrom: An existing library path. + @type pathTo: string + @param pathTo: A new library path. + """ class Libraries: """ diff --git a/source/blender/python/api2_2x/doc/Particle.py b/source/blender/python/api2_2x/doc/Particle.py index 192ecd5355b..511ad81b45f 100644 --- a/source/blender/python/api2_2x/doc/Particle.py +++ b/source/blender/python/api2_2x/doc/Particle.py @@ -56,17 +56,17 @@ class Particle: @type type: int @ivar resolutionGrid: The resolution of the particle grid. @type resolutionGrid: int - @ivar startFrame: Frame # to start emitting particles. + @ivar startFrame: Frame number to start emitting particles. @type startFrame: float - @ivar endFrame: Frame # to stop emitting particles. + @ivar endFrame: Frame number to stop emitting particles. @type endFrame: float @ivar editable: Finalize hair to enable editing in particle mode. @type editable: int @ivar amount: The total number of particles. @type amount: int - @ivar multireact: React multiple times ( Paricle.REACTON[ 'NEAR' | 'COLLISION' | 'DEATH' ] ). + @ivar multireact: React multiple times ( Particle.REACTON[ 'NEAR' | 'COLLISION' | 'DEATH' ] ). @type multireact: int - @ivar reactshape: Power of reaction strength dependence on distance to target. + @ivar reactshape: Power of reaction strength, dependent on distance to target. @type reactshape: float @ivar hairSegments: Amount of hair segments. @type hairSegments: int @@ -74,13 +74,13 @@ class Particle: @type lifetime: float @ivar randlife: Give the particle life a random variation. @type randlife: float - @ivar randemission: Give the particle life a random variation + @ivar randemission: Emit particles in random order. @type randemission: int - @ivar particleDistribution: Where to emit particles from Paricle.EMITFROM[ 'PARTICLE' | 'VOLUME' | 'FACES' | 'VERTS' ] ) + @ivar particleDistribution: Where to emit particles from ( Particle.EMITFROM[ 'PARTICLE' | 'VOLUME' | 'FACES' | 'VERTS' ] ) @type particleDistribution: int @ivar evenDistribution: Use even distribution from faces based on face areas or edge lengths. @type evenDistribution: int - @ivar distribution: How to distribute particles on selected element Paricle.DISTRIBUTION[ 'GRID' | 'RANDOM' | 'JITTERED' ] ). + @ivar distribution: How to distribute particles on selected element ( Particle.DISTRIBUTION[ 'GRID' | 'RANDOM' | 'JITTERED' ] ). @type distribution: int @ivar jitterAmount: Amount of jitter applied to the sampling. @type jitterAmount: float @@ -131,237 +131,56 @@ class Particle: Get the particles locations. A list of tuple is returned in particle mode. A list of list of tuple is returned in hair mode. - The tuple is a vector list of 3 or 4 floats in world space (x,y,z, optionnaly the particle's id). + The tuple is a vector of 3 or 4 floats in world space (x,y,z, +optionally the particle's id). @type all: int @param all: if not 0 export all particles (uninitialized (unborn or died)particles exported as None). @type id: int @param id: add the particle id in the end of the vector tuple - @rtype: list of vectors (tuple of 3 floats and optionnaly the id) or list of list of vectors - @return: list of vectors or list of list of vectors (hair mode) + @rtype: list of vectors (tuple of 3 floats and optionally the id) or list of list of vectors + @return: list of vectors or list of list of vectors (hair mode) or None if system is disabled """ def getRot(all=0,id=0): """ - Get the particles rotations as quaternion. + Get the particles' rotations as quaternion. A list of tuple is returned in particle mode. - The tuple is a vector list of 4 or 5 floats (x,y,z,w, optionnaly the id of the particle). + The tuple is vector of 4 or 5 floats (x,y,z,w, optionally the id of the particle). @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died) particles exported as None). + @param all: if not 0, export all particles (uninitialized (unborn or died) particles exported as None). @type id: int @param id: add the particle id in the return tuple @rtype: list of tuple of 4 or 5 elements (if id is not zero) - @return: list of 4-tuples + @return: list of 4-tuples or None if system is disabled """ def getMat(): """ - Get the particles material. + Get the particles' material. @rtype: Blender Material - @return: The marterial assigned to particles + @return: The material assigned to particles """ def getSize(all=0,id=0): """ - Get the particles size. + Get the particles' size. A list of float or list of tuple (particle's size,particle's id). @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died) particles exported as None). + @param all: if not 0, export all particles (uninitialized (unborn or died) particles exported as None). @type id: int @param id: add the particle id in the return tuple @rtype: list of floats - @return: list of floats or list of tuples if id is not zero (size,id). + @return: list of floats or list of tuples if id is not zero (size,id) or None if system is disabled. """ def getAge(all=0,id=0): """ - Get the particles age. - A list of float or list of tuple (particle's age,particle's id). + Get the particles' age. + A list of float or list of tuple (particle's age, particle's id). @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died) particles exported as None). + @param all: if not 0, export all particles (uninitialized (unborn or died) particles exported as None). @type id: int @param id: add the particle id in the return tuple @rtype: list of floats - @return: list of floats or list of tuples if id is not zero (size,id). - """ -# Blender.Object module and the Object PyType object - -""" -The Blender.Particle submodule - - -Particle -======== - -This module provides access to the B{Particle} in Blender. - -@type TYPE: readonly dictionary -@var TYPE: Constant dict used for with L{Particle.TYPE} - - HAIR: set particle system to hair mode. - - REACTOR: set particle system to reactor mode. - - EMITTER: set particle system to emitter mode. -@type DISTRIBUTION: readonly dictionary -@var DISTRIBUTION: Constant dict used for with L{Particle.DISTRIBUTION} - - GRID: set grid distribution. - - RANDOM: set random distribution. - - JITTERED: set jittered distribution. -@type EMITFROM: readonly dictionary -@var EMITFROM: Constant dict used for with L{Particle.EMITFROM} - - VERTS: set particles emit from vertices - - FACES: set particles emit from faces - - VOLUME: set particles emit from volume - - PARTICLE: set particles emit from particles -@type REACTON: readonly dictionary -@var REACTON: Constant dict used for with L{Particle.REACTON} - - NEAR: react on near - - COLLISION: react on collision - - DEATH: react on death -@type DRAWAS: readonly dictionary -@var DRAWAS: Constant dict used for with L{Particle.DRAWAS} - - NONE: Don't draw - - POINT: Draw as point - - CIRCLE: Draw as circles - - CROSS: Draw as crosses - - AXIS: Draw as axis - - LINE: Draw as lines - - PATH: Draw pathes - - OBJECT: Draw object - - GROUP: Draw goup - - BILLBOARD: Draw as billboard -""" - -def Get(name): - """ - Get the particle system of the object "name". - @type name: string - @return: The particle system of the object. - """ -def New(name): - """ - Assign a new particle system to the object "name". - @type name: string - @return: The newly created particle system. - """ - -class Particle: - """ - The Particle object - =================== - This object gives access to paticles data. - - @ivar seed: Set an offset in the random table. - @type seed: int - @ivar type: Type of particle system ( Particle.TYPE[ 'HAIR' | 'REACTOR' | 'EMITTER' ] ). - @type type: int - @ivar resolutionGrid: The resolution of the particle grid. - @type resolutionGrid: int - @ivar startFrame: Frame # to start emitting particles. - @type startFrame: float - @ivar endFrame: Frame # to stop emitting particles. - @type endFrame: float - @ivar editable: Finalize hair to enable editing in particle mode. - @type editable: int - @ivar amount: The total number of particles. - @type amount: int - @ivar multireact: React multiple times ( Paricle.REACTON[ 'NEAR' | 'COLLISION' | 'DEATH' ] ). - @type multireact: int - @ivar reactshape: Power of reaction strength dependence on distance to target. - @type reactshape: float - @ivar hairSegments: Amount of hair segments. - @type hairSegments: int - @ivar lifetime: Specify the life span of the particles. - @type lifetime: float - @ivar randlife: Give the particle life a random variation. - @type randlife: float - @ivar randemission: Give the particle life a random variation - @type randemission: int - @ivar particleDistribution: Where to emit particles from Paricle.EMITFROM[ 'PARTICLE' | 'VOLUME' | 'FACES' | 'VERTS' ] ) - @type particleDistribution: int - @ivar evenDistribution: Use even distribution from faces based on face areas or edge lengths. - @type evenDistribution: int - @ivar distribution: How to distribute particles on selected element Paricle.DISTRIBUTION[ 'GRID' | 'RANDOM' | 'JITTERED' ] ). - @type distribution: int - @ivar jitterAmount: Amount of jitter applied to the sampling. - @type jitterAmount: float - @ivar pf: Emission locations / face (0 = automatic). - @type pf:int - @ivar invert: Invert what is considered object and what is not. - @type invert: int - @ivar targetObject: The object that has the target particle system (empty if same object). - @type targetObject: Blender object - @ivar targetpsys: The target particle system number in the object. - @type targetpsys: int - @ivar 2d: Constrain boids to a surface. - @type 2d: float - @ivar maxvel: Maximum velocity. - @type maxvel: float - @ivar avvel: The usual speed % of max velocity. - @type avvel: float - @ivar latacc: Lateral acceleration % of max velocity - @type latacc: float - @ivar tanacc: Tangential acceleration % of max velocity - @type tanacc: float - @ivar groundz: Default Z value. - @type groundz: float - @ivar object: Constrain boids to object's surface. - @type object: Blender Object - @ivar renderEmitter: Render emitter object. - @type renderEmitter: int - @ivar displayPercentage: Particle display percentage. - @type displayPercentage: int - @ivar hairDisplayStep: How many steps paths are drawn with (power of 2) in visu mode. - @type hairDisplayStep: int - @ivar hairRenderStep: How many steps paths are rendered with (power of 2) in render mode." - @type hairRenderStep: int - @ivar duplicateObject: Get the duplicate object. - @type duplicateObject: Blender Object - @ivar drawAs: Get draw type Particle.DRAWAS([ 'NONE' | 'OBJECT' | 'POINT' | ... ]). - @type drawAs: int - """ - def freeEdit(): - """ - Free edit mode. - @return: None - """ - - def getLoc(all=0,id=0): - """ - Get the particles locations. - A list of tuple is returned in particle mode. - A list of list of tuple is returned in hair mode. - The tuple is a vector list of 3 floats in world space. - @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died)particles exported as None). - @type id: int - @param id: add the particle id in the end of the vector tuple - @rtype: list of vectors (tuple of 3 floats and optionnaly the id) or list of list of vectors - @return: list of vectors or list of list of vectors (hair mode) - """ - def getRot(all=0,id=0): - """ - Get the particles rotations as quaternion. - A list of tuple is returned in particle mode. - The tuple is a vector list of 4 floats (quaternion). - @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died) particles exported as None). - @type id: int - @param id: add the particle id in the return tuple - @rtype: list of tuple of 4 or 5 elements (if id is not zero) - @return: list of 4-tuples - """ - def getMat(): - """ - Get the particles material. - @rtype: Blender Material - @return: The marterial assigned to particles - """ - def getSize(all=0,id=0): - """ - Get the particles size. - A list of float. - @type all: int - @param all: if not 0 export all particles (uninitialized (unborn or died) particles exported as None). - @type id: int - @param id: add the particle id in the return tuple - @rtype: list of floats - @return: list of floats or list of tuples if id is not zero (size,id). + @return: list of floats or list of tuples if id is not zero (size,id) or None if system is disabled. """ diff --git a/source/blender/python/api2_2x/doc/Render.py b/source/blender/python/api2_2x/doc/Render.py index 475a4fc5b10..d4dc83e84a0 100644 --- a/source/blender/python/api2_2x/doc/Render.py +++ b/source/blender/python/api2_2x/doc/Render.py @@ -833,9 +833,7 @@ class RenderData: def enableCropping(toggle): """ - Enable/disable exclusion of border rendering from total image. - @type toggle: int - @param toggle: pass 1 for on / 0 for off + Deprecated: see the L{crop} attribute. """ def setImageType(type): diff --git a/source/blender/python/api2_2x/sceneRender.c b/source/blender/python/api2_2x/sceneRender.c index b446af7efd4..d382d450970 100644 --- a/source/blender/python/api2_2x/sceneRender.c +++ b/source/blender/python/api2_2x/sceneRender.c @@ -985,7 +985,7 @@ PyObject *RenderData_EnableCropping( void ) /* return M_Render_BitToggleInt( args, R_MOVIECROP, &self->renderContext->mode ); */ - printf("cropping option is now default, obsolete\n"); + printf("obsolete: movie cropping option is now default\n"); Py_RETURN_NONE; } diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 67be0ce4c00..4520e4c10bb 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -671,8 +671,10 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) RenderPass *zpass; GroupObject *go; LampRen *lar; - - int x, y; + RenderLayer *rlpp[RE_MAX_OSA]; + + int totsample, fullsample, sample; + int x, y,od; short first_lamp; float *zrect; float *rgbrect; @@ -683,7 +685,10 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) fac = 0.5; facm = 1.0 - fac; - + + totsample= get_sample_layers(pa, rl, rlpp); + fullsample= (totsample > 1); + /* check that z pass is enabled */ if(pa->rectz==NULL) return; for(zpass= rl->passes.first; zpass; zpass= zpass->next) @@ -708,9 +713,10 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) zrect = zpass->rect; rgbrect = rl->rectf; + od=0; /* for each x,y and sun lamp*/ for(y=pa->disprect.ymin; ydisprect.ymax; y++) { - for(x=pa->disprect.xmin; xdisprect.xmax; x++, zrect++, rgbrect+=4) { + for(x=pa->disprect.xmin; xdisprect.xmax; x++, zrect++, od++) { first_lamp = 1; for(go=R.lights.first; go; go= go->next) { @@ -724,7 +730,7 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) } if(lar->sunsky->effect_type & LA_SUN_EFFECT_AP){ - VECCOPY(tmp_rgb, rgbrect); + VECCOPY(tmp_rgb, (float*)(rgbrect+4*od)); shadeAtmPixel(lar->sunsky, tmp_rgb, x, y, *zrect); @@ -743,7 +749,16 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) /* if at least for one sun lamp aerial perspective was applied*/ if(first_lamp==0) - VECCOPY(rgbrect, rgb); + { + if(fullsample) { + for(sample=0; samplerectf + od*4), rgb); + } + } + else { + VECCOPY((float*)(rgbrect+4*od), rgb); + } + } } } } diff --git a/source/blender/render/intern/source/shadeoutput.c b/source/blender/render/intern/source/shadeoutput.c index 0928042729a..5b69323667e 100644 --- a/source/blender/render/intern/source/shadeoutput.c +++ b/source/blender/render/intern/source/shadeoutput.c @@ -1379,6 +1379,8 @@ static void shade_one_light(LampRen *lar, ShadeInput *shi, ShadeResult *shr, int } /* specularity */ + shadfac[3]*= phongcorr; /* note, shadfac not allowed to be stored nonlocal */ + if(shadfac[3]>0.0f && shi->spec!=0.0f && !(lar->mode & LA_NO_SPEC) && !(lar->mode & LA_ONLYSHADOW)) { if(!(passflag & (SCE_PASS_COMBINED|SCE_PASS_SPEC))); diff --git a/source/blender/src/drawaction.c b/source/blender/src/drawaction.c index 6fe37c1c6e5..9b1af3f1a06 100644 --- a/source/blender/src/drawaction.c +++ b/source/blender/src/drawaction.c @@ -58,6 +58,7 @@ #include "DNA_constraint_types.h" #include "DNA_key_types.h" #include "DNA_userdef_types.h" +#include "DNA_gpencil_types.h" #include "BKE_action.h" #include "BKE_depsgraph.h" @@ -74,6 +75,7 @@ #include "BIF_editnla.h" #include "BIF_interface.h" #include "BIF_interface_icons.h" +#include "BIF_drawgpencil.h" #include "BIF_gl.h" #include "BIF_glutil.h" #include "BIF_resources.h" @@ -83,6 +85,7 @@ #include "BDR_drawaction.h" #include "BDR_editcurve.h" +#include "BDR_gpencil.h" #include "BSE_drawnla.h" #include "BSE_drawipo.h" @@ -622,6 +625,28 @@ static void draw_channel_names(void) sprintf(name, "Constraint"); } break; + case ACTTYPE_GPLAYER: /* gpencil layer */ + { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + + indent = 0; + special = -1; + expand = -1; + + if (EDITABLE_GPL(gpl)) + protect = ICON_UNLOCKED; + else + protect = ICON_LOCKED; + + if (gpl->flag & GP_LAYER_HIDE) + mute = ICON_MUTE_IPO_ON; + else + mute = ICON_MUTE_IPO_OFF; + + sel = SEL_GPL(gpl); + BLI_snprintf(name, 32, gpl->info); + } + break; } /* now, start drawing based on this information */ @@ -827,6 +852,12 @@ static void draw_channel_strips(void) sel = SEL_ICU(icu); } break; + case ACTTYPE_GPLAYER: + { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + sel = SEL_GPL(gpl); + } + break; } if (datatype == ACTCONT_ACTION) { @@ -865,6 +896,19 @@ static void draw_channel_strips(void) glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(frame1_x, channel_y-CHANNELHEIGHT/2, G.v2d->hor.xmax, channel_y+CHANNELHEIGHT/2); } + else if (datatype == ACTCONT_GPENCIL) { + gla2DDrawTranslatePt(di, G.v2d->cur.xmin, y, &frame1_x, &channel_y); + + /* frames less than one get less saturated background */ + if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); + else glColor4ub(col2[0], col2[1], col2[2], 0x22); + glRectf(0, channel_y-CHANNELHEIGHT/2, frame1_x, channel_y+CHANNELHEIGHT/2); + + /* frames one and higher get a saturated background */ + if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); + else glColor4ub(col2[0], col2[1], col2[2], 0x44); + glRectf(frame1_x, channel_y-CHANNELHEIGHT/2, G.v2d->hor.xmax, channel_y+CHANNELHEIGHT/2); + } } /* Increment the step */ @@ -899,6 +943,9 @@ static void draw_channel_strips(void) case ALE_ICU: draw_icu_channel(di, ale->key_data, y); break; + case ALE_GPFRAME: + draw_gpl_channel(di, ale->data, y); + break; } } @@ -1075,6 +1122,7 @@ void drawactionspace(ScrArea *sa, void *spacedata) { bAction *act = NULL; Key *key = NULL; + bGPdata *gpd = NULL; void *data; short datatype; @@ -1090,18 +1138,38 @@ void drawactionspace(ScrArea *sa, void *spacedata) /* only try to refresh action that's displayed if not pinned */ if (G.saction->pin==0) { - if (OBACT) - G.saction->action = OBACT->action; - else - G.saction->action= NULL; + /* depends on mode */ + switch (G.saction->mode) { + case SACTCONT_ACTION: + { + if (OBACT) + G.saction->action = OBACT->action; + else + G.saction->action= NULL; + } + break; + case SACTCONT_GPENCIL: + { + /* this searching could be slow (so users should pin after this is found) */ + G.saction->gpd= gpencil_data_getetime(G.curscreen); + } + break; + } } /* get data */ data = get_action_context(&datatype); - if (datatype == ACTCONT_ACTION) - act = data; - else if (datatype == ACTCONT_SHAPEKEY) - key = data; + switch (datatype) { + case ACTCONT_ACTION: + act = data; + break; + case ACTCONT_SHAPEKEY: + key = data; + break; + case ACTCONT_GPENCIL: + gpd = data; + break; + } /* Lets make sure the width of the left hand of the screen * is set to an appropriate value based on whether sliders @@ -1450,10 +1518,15 @@ static ActKeysInc *init_aki_data() static ActKeysInc aki; /* init data of static struct here */ - if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED) + if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED && + (G.saction->mode == SACTCONT_ACTION)) + { aki.ob= OBACT; + } else if (curarea->spacetype == SPACE_NLA) + { aki.ob= NULL; // FIXME + } else aki.ob= NULL; @@ -1528,6 +1601,16 @@ void draw_action_channel(gla2DDrawInfo *di, bAction *act, float ypos) BLI_freelistN(&keys); } +void draw_gpl_channel(gla2DDrawInfo *di, bGPDlayer *gpl, float ypos) +{ + ListBase keys = {0, 0}; + ActKeysInc *aki = init_aki_data(); + + gpl_to_keylist(gpl, &keys, NULL, aki); + draw_keylist(di, &keys, NULL, ypos); + BLI_freelistN(&keys); +} + /* --------------- Conversion: data -> keyframe list ------------------ */ void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki) @@ -1674,3 +1757,26 @@ void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysIn } } +void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bGPDframe *gpf; + ActKeyColumn *ak; + + if (gpl && keys) { + /* loop over frames, converting directly to 'keyframes' (should be in order too) */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + BLI_addtail(keys, ak); + + ak->cfra= gpf->framenum; + ak->modified = 1; + ak->handle_type= 0; + + if (gpf->flag & GP_FRAME_SELECT) + ak->sel = SELECT; + else + ak->sel = 0; + } + } +} + diff --git a/source/blender/src/drawgpencil.c b/source/blender/src/drawgpencil.c new file mode 100644 index 00000000000..55d0464b9af --- /dev/null +++ b/source/blender/src/drawgpencil.c @@ -0,0 +1,653 @@ +/** + * $Id: drawgpencil.c 14881 2008-05-18 10:41:42Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "DNA_listBase.h" +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "BIF_butspace.h" +#include "BIF_graphics.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_resources.h" +#include "BIF_space.h" +#include "BIF_screen.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BDR_gpencil.h" +#include "BIF_drawgpencil.h" + +#include "BSE_drawipo.h" +#include "BSE_headerbuttons.h" +#include "BSE_view.h" + +#include "blendef.h" +#include "butspace.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" + +/* ************************************************** */ +/* GREASE PENCIL PANEL-UI DRAWING */ + +/* Every space which implements Grease-Pencil functionality should have a panel + * for the settings. All of the space-dependent parts should be coded in the panel + * code for that space, but the rest is all handled by generic panel here. + */ + +/* ------- Callbacks ----------- */ +/* These are just 'dummy wrappers' around gpencil api calls */ + +/* make layer active one after being clicked on */ +void gp_ui_activelayer_cb (void *gpd, void *gpl) +{ + gpencil_layer_setactive(gpd, gpl); +} + +/* rename layer and set active */ +void gp_ui_renamelayer_cb (void *gpd_arg, void *gpl_arg) +{ + bGPdata *gpd= (bGPdata *)gpd_arg; + bGPDlayer *gpl= (bGPDlayer *)gpl_arg; + + BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info[0]), 128); + gpencil_layer_setactive(gpd, gpl); +} + +/* add a new layer */ +void gp_ui_addlayer_cb (void *gpd, void *dummy) +{ + gpencil_layer_addnew(gpd); +} + +/* delete active layer */ +void gp_ui_dellayer_cb (void *gpd, void *dummy) +{ + gpencil_layer_delactive(gpd); +} + +/* delete last stroke of active layer */ +void gp_ui_delstroke_cb (void *gpd, void *gpl) +{ + bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0); + + gpencil_layer_setactive(gpd, gpl); + gpencil_frame_delete_laststroke(gpf); +} + +/* delete active frame of active layer */ +void gp_ui_delframe_cb (void *gpd, void *gpl) +{ + bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0); + + gpencil_layer_setactive(gpd, gpl); + gpencil_layer_delframe(gpl, gpf); +} + + +/* set this set of gpencil data for editing in action editor */ +void gp_ui_dotime_cb (void *gpd_arg, void *dummy) +{ + bGPdata *gpd= (bGPdata *)gpd_arg; + + /* check if setting or clearing (note: setting was just set...) */ + if (gpd->flag & GP_DATA_EDITTIME) + gpencil_data_setetime(G.curscreen, gpd); + else + gpencil_data_setetime(G.curscreen, NULL); +} + + +/* ------- Drawing Code ------- */ + +/* draw the controls for a given layer */ +static void gp_drawui_layer (uiBlock *block, bGPdata *gpd, bGPDlayer *gpl, short *xco, short *yco) +{ + uiBut *but; + short width= 314; + short height; + int rb_col; + + /* unless button has own callback, it adds this callback to button */ + uiBlockSetFunc(block, gp_ui_activelayer_cb, gpd, gpl); + + /* draw header */ + { + uiBlockSetEmboss(block, UI_EMBOSSN); + + /* rounded header */ + rb_col= (gpl->flag & GP_LAYER_ACTIVE)?50:20; + uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-2, width, 24, NULL, 5.0, 0.0, 15 , rb_col-20, ""); + + /* lock toggle */ + uiDefIconButBitI(block, ICONTOG, GP_LAYER_LOCKED, B_REDR, ICON_UNLOCKED, *xco-7, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Layer cannot be modified"); + } + + /* when layer is locked or hidden, don't draw the rest of its settings */ + if (gpl->flag & (GP_LAYER_LOCKED|GP_LAYER_HIDE)) { + height= 26; + + /* draw rest of header */ + { + /* visibility button (only if hidden but not locked!) */ + if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED)) + uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer"); + + /* name */ + uiDefBut(block, LABEL, 1, gpl->info, *xco+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "Short description of what this layer is for (optional)"); + } + + /* draw backdrop */ + uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12, rb_col, ""); + + /* draw settings... (i.e. just warning for this one) */ + if (gpl->flag & GP_LAYER_HIDE) + uiDefBut(block, LABEL, 1, "Grease Pencil Layer Hidden", *xco+60, *yco-26, 205, 20, NULL, 0.0, 0.0, 0, 0, ""); + else + uiDefBut(block, LABEL, 1, "Grease Pencil Layer Locked", *xco+60, *yco-26, 205, 20, NULL, 0.0, 0.0, 0, 0, ""); + + uiBlockSetEmboss(block, UI_EMBOSS); + } + else { + height= 100; + + /* draw rest of header */ + { + /* visibility button */ + uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer"); + + uiBlockSetEmboss(block, UI_EMBOSS); + + /* name */ + but= uiDefButC(block, TEX, B_REDR, "Info:", *xco+35, *yco, 240, 20, gpl->info, 0, 127, 0, 0, "Short description of what this layer is for (optional)"); + uiButSetFunc(but, gp_ui_renamelayer_cb, gpd, gpl); + + /* delete 'button' */ + uiBlockSetEmboss(block, UI_EMBOSSN); + + but= uiDefIconBut(block, BUT, B_REDR, ICON_X, *xco+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete layer"); + uiButSetFunc(but, gp_ui_dellayer_cb, gpd, NULL); + + uiBlockSetEmboss(block, UI_EMBOSS); + } + + /* draw backdrop */ + uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12, rb_col, ""); + + /* draw settings */ + { + /* color */ + uiBlockBeginAlign(block); + uiDefButF(block, COL, B_REDR, "", *xco, *yco-26, 150, 19, gpl->color, 0, 0, 0, 0, "Color to use for all strokes on this Grease Pencil Layer"); + uiDefButF(block, NUMSLI, B_REDR, "Opacity: ", *xco,*yco-45,150,19, &gpl->color[3], 0.3, 1.0, 0, 0, "Visibility of stroke (0.3 to 1.0)"); + uiBlockEndAlign(block); + + /* stroke thickness */ + uiDefButS(block, NUMSLI, B_REDR, "Thickness:", *xco, *yco-75, 150, 20, &gpl->thickness, 1, 10, 0, 0, "Thickness of strokes (in pixels)"); + + + /* onion-skinning */ + uiBlockBeginAlign(block); + uiDefButBitI(block, TOG, GP_LAYER_ONIONSKIN, B_REDR, "Onion-Skin", *xco+160, *yco-26, 140, 20, &gpl->flag, 0, 0, 0, 0, "Ghost frames on either side of frame"); + uiDefButS(block, NUMSLI, B_REDR, "GStep:", *xco+160, *yco-46, 140, 20, &gpl->gstep, 0, 120, 0, 0, "Maximum frame range on either side of active frame to show (0 = just 'first' available frame on either side)"); + uiBlockEndAlign(block); + + /* options */ + but= uiDefBut(block, BUT, B_REDR, "Del Active Frame", *xco+160, *yco-75, 140, 20, NULL, 0, 0, 0, 0, "Erases the the active frame for this layer"); + uiButSetFunc(but, gp_ui_delframe_cb, gpd, gpl); + + but= uiDefBut(block, BUT, B_REDR, "Del Last Stroke", *xco+160, *yco-95, 140, 20, NULL, 0, 0, 0, 0, "Erases the last stroke from the active frame"); + uiButSetFunc(but, gp_ui_delstroke_cb, gpd, gpl); + + //uiDefButBitI(block, TOG, GP_LAYER_DRAWDEBUG, B_REDR, "Show Points", *xco+160, *yco-75, 130, 20, &gpl->flag, 0, 0, 0, 0, "Show points which form the strokes"); + } + } + + /* adjust height for new to start */ + (*yco) -= (height + 27); +} + +/* Draw the contents for a grease-pencil panel. This assumes several things: + * - that panel has been created, is 318 x 204. max yco is 225 + * - that a toggle for turning on/off gpencil drawing is 150 x 20, starting from (10,225) + * It will return the amount of extra space to extend the panel by + */ +short draw_gpencil_panel (uiBlock *block, bGPdata *gpd, ScrArea *sa) +{ + uiBut *but; + bGPDlayer *gpl; + short xco= 10, yco= 155; + + /* draw gpd settings first */ + { + /* show status info button */ + uiDefButBitI(block, TOG, GP_DATA_DISPINFO, B_REDR, "Show Status Info", 10, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "Display status info about current status of Grease Pencil"); + + /* add new/duplicate layer buttons */ + but= uiDefBut(block, BUT, B_REDR, "Add New Layer", 10,182,150,20, 0, 0, 0, 0, 0, "Adds a new Grease Pencil Layer"); + uiButSetFunc(but, gp_ui_addlayer_cb, gpd, NULL); + + + /* show override lmb-clicks button */ + uiDefButBitI(block, TOG, GP_DATA_EDITPAINT, B_REDR, "Draw Mode", 170, 225, 150, 20, &gpd->flag, 0, 0, 0, 0, "Interpret LMB-click as new strokes (same as holding Shift-Key per stroke)"); + + /* 'view align' button (naming depends on context) */ + if (sa->spacetype == SPACE_VIEW3D) + uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Draw in 3D", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added in 3D-space"); + else if (sa->spacetype != SPACE_SEQ) /* not available for sequencer yet */ + uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Stick to View", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added on 2d-canvas"); + + /* show edit-in-action button */ + but= uiDefButBitI(block, TOG, GP_DATA_EDITTIME, B_REDR, "Edit Timing", 170, 182, 150, 20, &gpd->flag, 0, 0, 0, 0, "Edit timing of frames for the Grease Pencil block"); + uiButSetFunc(but, gp_ui_dotime_cb, gpd, NULL); + } + + /* draw for each layer */ + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + gp_drawui_layer(block, gpd, gpl, &xco, &yco); + } + + /* return new height if necessary */ + return (yco < 0) ? (204 - yco) : 204; +} + +/* ************************************************** */ +/* GREASE PENCIL DRAWING */ + +/* flags for sflag */ +enum { + GP_DRAWDATA_NOSTATUS = (1<<0), /* don't draw status info */ + GP_DRAWDATA_ONLY3D = (1<<1), /* only draw 3d-strokes */ + GP_DRAWDATA_ONLYV2D = (1<<2), /* only draw 'canvas' strokes */ +}; + +/* draw a given stroke */ +static void gp_draw_stroke (bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, short debug, int winx, int winy) +{ + bGPDspoint *pt; + int i; + + /* error checking */ + if ((points == NULL) || (totpoints <= 0)) + return; + + /* check if stroke can be drawn */ + if ((dflag & GP_DRAWDATA_ONLY3D) && !(sflag & GP_STROKE_3DSPACE)) + return; + if (!(dflag & GP_DRAWDATA_ONLY3D) && (sflag & GP_STROKE_3DSPACE)) + return; + if ((dflag & GP_DRAWDATA_ONLYV2D) && !(sflag & GP_STROKE_2DSPACE)) + return; + if (!(dflag & GP_DRAWDATA_ONLYV2D) && (sflag & GP_STROKE_2DSPACE)) + return; + + /* if drawing a single point, draw it larger */ + if (totpoints == 1) { + /* draw point */ + if (sflag & GP_STROKE_3DSPACE) { + glBegin(GL_POINTS); + glVertex3f(points->x, points->y, points->z); + glEnd(); + } + else if (sflag & GP_STROKE_2DSPACE) { + glBegin(GL_POINTS); + glVertex2f(points->x, points->y); + glEnd(); + } + else { + const float x= (points->x / 1000 * winx); + const float y= (points->y / 1000 * winy); + + glBegin(GL_POINTS); + glVertex2f(x, y); + glEnd(); + } + } + else { + float oldpressure = 0.0f; + + /* draw stroke curve */ + glBegin(GL_LINE_STRIP); + for (i=0, pt=points; i < totpoints && pt; i++, pt++) { + float x, y, z; + + if (sflag & GP_STROKE_3DSPACE) { + x= pt->x; + y= pt->y; + z= pt->z; + } + else if (sflag & GP_STROKE_2DSPACE) { + x= pt->x; + y= pt->y; + z= 0; + } + else { + x= (pt->x / 1000 * winx); + y= (pt->y / 1000 * winy); + z= 0; + } + + if (fabs(pt->pressure - oldpressure) > 0.2f) { + glEnd(); + glLineWidth(pt->pressure * thickness); + glBegin(GL_LINE_STRIP); + + if (sflag & GP_STROKE_3DSPACE) + glVertex3f(x, y, z); + else + glVertex2f(x, y); + + oldpressure = pt->pressure; + } + else { + if (sflag & GP_STROKE_3DSPACE) + glVertex3f(x, y, z); + else + glVertex2f(x, y); + } + } + glEnd(); + + /* draw debug points of curve on top? */ + if (debug) { + glBegin(GL_POINTS); + for (i=0, pt=points; i < totpoints && pt; i++, pt++) { + if (sflag & GP_STROKE_3DSPACE) { + glVertex3f(pt->x, pt->y, pt->z); + } + else if (sflag & GP_STROKE_2DSPACE) { + glVertex2f(pt->x, pt->y); + } + else { + const float x= (pt->x / 1000 * winx); + const float y= (pt->y / 1000 * winy); + + glVertex2f(x, y); + } + } + glEnd(); + } + } +} + +/* draw grease-pencil datablock */ +static void gp_draw_data (bGPdata *gpd, int winx, int winy, int dflag) +{ + bGPDlayer *gpl, *actlay=NULL; + + /* turn on smooth lines (i.e. anti-aliasing) */ + glEnable(GL_LINE_SMOOTH); + + /* turn on alpha-blending */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + /* loop over layers, drawing them */ + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + bGPDframe *gpf; + bGPDstroke *gps; + + short debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? 1 : 0; + short lthick= gpl->thickness; + float color[4]; + + /* don't draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* if layer is active one, store pointer to it */ + if (gpl->flag & GP_LAYER_ACTIVE) + actlay= gpl; + + /* get frame to draw */ + gpf= gpencil_layer_getframe(gpl, CFRA, 0); + if (gpf == NULL) + continue; + + /* set color, stroke thickness, and point size */ + glLineWidth(lthick); + QUATCOPY(color, gpl->color); // just for copying 4 array elements + glColor4f(color[0], color[1], color[2], color[3]); + glPointSize(gpl->thickness + 2); + + /* draw 'onionskins' (frame left + right) */ + if (gpl->flag & GP_LAYER_ONIONSKIN) { + /* drawing method - only immediately surrounding (gstep = 0), or within a frame range on either side (gstep > 0)*/ + if (gpl->gstep) { + bGPDframe *gf; + short i; + + /* draw previous frames first */ + for (gf=gpf->prev, i=0; gf; gf=gf->prev, i++) { + /* check if frame is drawable */ + if ((gpf->framenum - gf->framenum) <= gpl->gstep) { + /* alpha decreases with distance from curframe index */ + glColor4f(color[0], color[1], color[2], (color[3]-(i*0.7))); + + for (gps= gf->strokes.first; gps; gps= gps->next) { + gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy); + } + } + else + break; + } + + /* now draw next frames */ + for (gf= gpf->next, i=0; gf; gf=gf->next, i++) { + /* check if frame is drawable */ + if ((gf->framenum - gpf->framenum) <= gpl->gstep) { + /* alpha decreases with distance from curframe index */ + glColor4f(color[0], color[1], color[2], (color[3]-(i*0.7))); + + for (gps= gf->strokes.first; gps; gps= gps->next) { + gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy); + } + } + else + break; + } + + /* restore alpha */ + glColor4f(color[0], color[1], color[2], color[3]); + } + else { + /* draw the strokes for the ghost frames (at half of the alpha set by user) */ + glColor4f(color[0], color[1], color[2], (color[3] / 7)); + + if (gpf->prev) { + for (gps= gpf->prev->strokes.first; gps; gps= gps->next) { + gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy); + } + } + + glColor4f(color[0], color[1], color[2], (color[3] / 4)); + if (gpf->next) { + for (gps= gpf->next->strokes.first; gps; gps= gps->next) { + gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy); + } + } + + /* restore alpha */ + glColor4f(color[0], color[1], color[2], color[3]); + } + } + + /* draw the strokes already in active frame */ + for (gps= gpf->strokes.first; gps; gps= gps->next) { + gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy); + } + + /* Check if may need to draw the active stroke cache, only if this layer is the active layer + * that is being edited. (Stroke cache is currently stored in gp-data) + */ + if ((G.f & G_GREASEPENCIL) && (gpl->flag & GP_LAYER_ACTIVE) && + (gpf->flag & GP_FRAME_PAINT)) + { + /* Buffer stroke needs to be drawn with a different linestyle to help differentiate them from normal strokes. */ + setlinestyle(2); + gp_draw_stroke(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, debug, winx, winy); + setlinestyle(0); + } + } + + /* turn off alpha blending, then smooth lines */ + glDisable(GL_BLEND); // alpha blending + glDisable(GL_LINE_SMOOTH); // smooth lines + + /* show info for debugging the status of gpencil */ + if ( ((dflag & GP_DRAWDATA_NOSTATUS)==0) && (gpd->flag & GP_DATA_DISPINFO) ) { + char printable[256]; + short xmax; + + /* get text to display */ + if (actlay) { + if (gpd->flag & GP_DATA_EDITPAINT) + BIF_ThemeColor(TH_BONE_POSE); // should be blue-ish + else if (actlay->actframe == NULL) + BIF_ThemeColor(TH_REDALERT); + else if (actlay->actframe->framenum == CFRA) + BIF_ThemeColor(TH_VERTEX_SELECT); // should be yellow + else + BIF_ThemeColor(TH_TEXT_HI); + + if (actlay->actframe) { + sprintf(printable, "GPencil: Layer ('%s'), Frame (%d) %s", + actlay->info, actlay->actframe->framenum, + ((gpd->flag & GP_DATA_EDITPAINT)?", Draw Mode On":"") ); + } + else { + sprintf(printable, "GPencil: Layer ('%s'), Frame %s", + actlay->info, ((gpd->flag & GP_DATA_EDITPAINT)?", Draw Mode On":"") ); + } + } + else { + BIF_ThemeColor(TH_REDALERT); + sprintf(printable, "GPencil: Layer "); + } + xmax= GetButStringLength(printable); + + /* only draw it if view is wide enough (assume padding of 20 is enough for now) */ + if (winx > (xmax + 20)) { + glRasterPos2i(winx-xmax, winy-20); + BMF_DrawString(G.fonts, printable); + } + } + + /* restore initial gl conditions */ + glLineWidth(1.0); + glPointSize(1.0); + glColor4f(0, 0, 0, 1); +} + +/* ----------- */ + +/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes, second time with onlyv2d=0 for screen-aligned strokes + */ +void draw_gpencil_2dview (ScrArea *sa, short onlyv2d) +{ + bGPdata *gpd; + int dflag = 0; + + /* check that we have grease-pencil stuff to draw */ + if (sa == NULL) return; + gpd= gpencil_data_getactive(sa); + if (gpd == NULL) return; + + /* draw it! */ + if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D|GP_DRAWDATA_NOSTATUS); + gp_draw_data(gpd, sa->winx, sa->winy, dflag); +} + +/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with only3d=1 to draw 3d-strokes, second time with only3d=0 for screen-aligned strokes + */ +void draw_gpencil_3dview (ScrArea *sa, short only3d) +{ + bGPdata *gpd; + int dflag = 0; + + /* check that we have grease-pencil stuff to draw */ + gpd= gpencil_data_getactive(sa); + if (gpd == NULL) return; + + /* draw it! */ + if (only3d) dflag |= (GP_DRAWDATA_ONLY3D|GP_DRAWDATA_NOSTATUS); + gp_draw_data(gpd, sa->winx, sa->winy, dflag); +} + +/* draw grease-pencil sketches to opengl render window assuming that matrices are already set correctly */ +void draw_gpencil_oglrender (View3D *v3d, int winx, int winy) +{ + bGPdata *gpd; + + /* assume gpencil data comes from v3d */ + if (v3d == NULL) return; + gpd= v3d->gpd; + if (gpd == NULL) return; + + /* pass 1: draw 3d-strokes ------------ > */ + gp_draw_data(gpd, winx, winy, (GP_DRAWDATA_NOSTATUS|GP_DRAWDATA_ONLY3D)); + + /* pass 2: draw 2d-strokes ------------ > */ + /* adjust view matrices */ + myortho2(-0.375, (float)(winx)-0.375, -0.375, (float)(winy)-0.375); + glLoadIdentity(); + + /* draw it! */ + gp_draw_data(gpd, winx, winy, GP_DRAWDATA_NOSTATUS); +} + +/* ************************************************** */ diff --git a/source/blender/src/drawnode.c b/source/blender/src/drawnode.c index 1169062fdd0..3a73ee84ead 100644 --- a/source/blender/src/drawnode.c +++ b/source/blender/src/drawnode.c @@ -37,6 +37,7 @@ #include "DNA_action_types.h" #include "DNA_color_types.h" #include "DNA_customdata_types.h" +#include "DNA_gpencil_types.h" #include "DNA_ipo_types.h" #include "DNA_ID.h" #include "DNA_image_types.h" @@ -65,8 +66,11 @@ #include "CMP_node.h" #include "SHD_node.h" +#include "BDR_gpencil.h" + #include "BIF_gl.h" #include "BIF_glutil.h" +#include "BIF_drawgpencil.h" #include "BIF_interface.h" #include "BIF_interface_icons.h" #include "BIF_language.h" @@ -3300,6 +3304,66 @@ static void node_draw_group(ScrArea *sa, SpaceNode *snode, bNode *gnode) } + +static void nodes_panel_gpencil(short cntrl) // NODES_HANDLER_GREASEPENCIL +{ + uiBlock *block; + SpaceNode *snode; + + snode= curarea->spacedata.first; + + block= uiNewBlock(&curarea->uiblocks, "nodes_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(NODES_HANDLER_GREASEPENCIL); // for close and esc + if (uiNewPanel(curarea, block, "Grease Pencil", "SpaceNode", 100, 30, 318, 204)==0) return; + + /* we can only really draw stuff if there are nodes (otherwise no events are handled */ + if (snode->nodetree == NULL) + return; + + /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */ + if (snode->flag & SNODE_DISPGP) { + if (snode->gpd == NULL) + gpencil_data_setactive(curarea, gpencil_data_addnew()); + } + + if (snode->flag & SNODE_DISPGP) { + bGPdata *gpd= snode->gpd; + short newheight; + + /* this is a variable height panel, newpanel doesnt force new size on existing panels */ + /* so first we make it default height */ + uiNewPanelHeight(block, 204); + + /* draw button for showing gpencil settings and drawings */ + uiDefButBitS(block, TOG, SNODE_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &snode->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Node Editor"); + + /* extend the panel if the contents won't fit */ + newheight= draw_gpencil_panel(block, gpd, curarea); + uiNewPanelHeight(block, newheight); + } + else { + uiDefButBitS(block, TOG, SNODE_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &snode->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Node Editor"); + uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + } +} + +static void nodes_blockhandlers(ScrArea *sa) +{ + SpaceNode *snode= sa->spacedata.first; + short a; + + for(a=0; ablockhandler[a]) { + case NODES_HANDLER_GREASEPENCIL: + nodes_panel_gpencil(snode->blockhandler[a+1]); + break; + } + } + uiDrawBlocksPanels(sa, 0); +} + void drawnodespace(ScrArea *sa, void *spacedata) { SpaceNode *snode= sa->spacedata.first; @@ -3354,13 +3418,26 @@ void drawnodespace(ScrArea *sa, void *spacedata) } } + /* draw grease-pencil ('canvas' strokes) */ + if ((snode->flag & SNODE_DISPGP) && (snode->nodetree)) + draw_gpencil_2dview(sa, 1); + /* restore viewport (not needed yet) */ mywinset(sa->win); /* ortho at pixel level curarea */ myortho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375); + + /* draw grease-pencil (screen strokes) */ + if ((snode->flag & SNODE_DISPGP) && (snode->nodetree)) + draw_gpencil_2dview(sa, 0); draw_area_emboss(sa); + + /* it is important to end a view in a transform compatible with buttons */ + bwin_scalematrix(sa->win, snode->blockscale, snode->blockscale, snode->blockscale); + nodes_blockhandlers(sa); + curarea->win_swap= WIN_BACK_OK; /* in the end, this is a delayed previewrender test, to allow buttons to be first */ diff --git a/source/blender/src/drawobject.c b/source/blender/src/drawobject.c index 045bf292446..1a469e8b366 100644 --- a/source/blender/src/drawobject.c +++ b/source/blender/src/drawobject.c @@ -1199,7 +1199,12 @@ static void drawlattice(Object *ob) int use_wcol= 0; lt= (ob==G.obedit)?editLatt:ob->data; + + /* now we default make displist, this will modifiers work for non animated case */ + if(ob->disp.first==NULL) + lattice_calc_modifiers(ob); dl= find_displist(&ob->disp, DL_VERTS); + if(ob==G.obedit) { cpack(0x004000); diff --git a/source/blender/src/drawseq.c b/source/blender/src/drawseq.c index e554b91dd52..c8c74ad8279 100644 --- a/source/blender/src/drawseq.c +++ b/source/blender/src/drawseq.c @@ -43,6 +43,7 @@ #include "IMB_imbuf_types.h" +#include "DNA_gpencil_types.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -67,6 +68,9 @@ #include "BIF_space.h" #include "BIF_interface.h" +#include "BIF_drawgpencil.h" +#include "BDR_gpencil.h" + #include "BSE_view.h" #include "BSE_drawipo.h" #include "BSE_sequence.h" @@ -98,6 +102,70 @@ static void draw_seq_text(Sequence *seq, float x1, float x2, float y1, float y2) static void draw_shadedstrip(Sequence *seq, char *col, float x1, float y1, float x2, float y2); static void draw_seq_strip(struct Sequence *seq, struct ScrArea *sa, struct SpaceSeq *sseq, int outline_tint, float pixelx); + +static void seq_panel_gpencil(short cntrl) // SEQ_HANDLER_GREASEPENCIL +{ + uiBlock *block; + SpaceSeq *sseq; + + sseq= curarea->spacedata.first; + + block= uiNewBlock(&curarea->uiblocks, "seq_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(SEQ_HANDLER_GREASEPENCIL); // for close and esc + if (uiNewPanel(curarea, block, "Grease Pencil", "SpaceSeq", 100, 30, 318, 204)==0) return; + + /* only draw settings if right mode */ + if (sseq->mainb == 0) + return; + + /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */ + if (sseq->flag & SEQ_DRAW_GPENCIL) { + if (sseq->gpd == NULL) + gpencil_data_setactive(curarea, gpencil_data_addnew()); + } + + if (sseq->flag & SEQ_DRAW_GPENCIL) { + bGPdata *gpd= sseq->gpd; + short newheight; + + /* this is a variable height panel, newpanel doesnt force new size on existing panels */ + /* so first we make it default height */ + uiNewPanelHeight(block, 204); + + /* draw button for showing gpencil settings and drawings */ + uiDefButBitI(block, TOG, SEQ_DRAW_GPENCIL, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sseq->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Sequencer View"); + + /* extend the panel if the contents won't fit */ + newheight= draw_gpencil_panel(block, gpd, curarea); + uiNewPanelHeight(block, newheight); + } + else { + uiDefButBitI(block, TOG, SEQ_DRAW_GPENCIL, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sseq->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Sequencer View"); + uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + } +} + +static void seq_blockhandlers(ScrArea *sa) +{ + SpaceSeq *sseq= sa->spacedata.first; + short a; + + /* warning; blocks need to be freed each time, handlers dont remove (for ipo moved to drawipospace) */ + uiFreeBlocksWin(&sa->uiblocks, sa->win); + + for(a=0; ablockhandler[a]) { + case SEQ_HANDLER_GREASEPENCIL: + seq_panel_gpencil(sseq->blockhandler[a+1]); + break; + } + } + uiDrawBlocksPanels(sa, 0); + +} + + static void draw_cfra_seq(void) { glColor3ub(0x30, 0x90, 0x50); @@ -907,6 +975,17 @@ static void draw_image_seq(ScrArea *sa) if (free_ibuf) { IMB_freeImBuf(ibuf); } + + /* draw grease-pencil (screen aligned) */ + if (sseq->flag & SEQ_DRAW_GPENCIL) + draw_gpencil_2dview(sa, 0); + + /* ortho at pixel level sa */ + myortho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375); + + /* it is important to end a view in a transform compatible with buttons */ + bwin_scalematrix(sa->win, sseq->blockscale, sseq->blockscale, sseq->blockscale); + seq_blockhandlers(sa); sa->win_swap= WIN_BACK_OK; } @@ -1023,24 +1102,6 @@ void seq_viewmove(SpaceSeq *sseq) window_set_cursor(win, oldcursor); } - - -static void seq_blockhandlers(ScrArea *sa) -{ - SpaceSeq *sseq= sa->spacedata.first; - short a; - - /* warning; blocks need to be freed each time, handlers dont remove (for ipo moved to drawipospace) */ - uiFreeBlocksWin(&sa->uiblocks, sa->win); - - for(a=0; ablockhandler[a+1]= 0; - } - uiDrawBlocksPanels(sa, 0); - -} - void drawprefetchseqspace(ScrArea *sa, void *spacedata) { SpaceSeq *sseq= sa->spacedata.first; diff --git a/source/blender/src/drawview.c b/source/blender/src/drawview.c index f595a101f63..14434504e7a 100644 --- a/source/blender/src/drawview.c +++ b/source/blender/src/drawview.c @@ -61,6 +61,7 @@ #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" +#include "DNA_gpencil_types.h" #include "DNA_image_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" @@ -111,6 +112,7 @@ #include "BIF_butspace.h" #include "BIF_drawimage.h" +#include "BIF_drawgpencil.h" #include "BIF_editgroup.h" #include "BIF_editarmature.h" #include "BIF_editmesh.h" @@ -137,6 +139,7 @@ #include "BDR_editobject.h" #include "BDR_vpaint.h" #include "BDR_sculptmode.h" +#include "BDR_gpencil.h" #include "BSE_drawview.h" #include "BSE_filesel.h" @@ -2498,7 +2501,7 @@ static void view3d_panel_background(short cntrl) // VIEW3D_HANDLER_BACKGROUND uiSetPanelHandler(VIEW3D_HANDLER_BACKGROUND); // for close and esc if(uiNewPanel(curarea, block, "Background Image", "View3d", 340, 10, 318, 204)==0) return; - if(G.f & G_VERTEXPAINT || G.f & G_WEIGHTPAINT || G.f & G_TEXTUREPAINT) { + if(G.f & G_VERTEXPAINT || G.f & G_WEIGHTPAINT || G.f & G_TEXTUREPAINT || G.f & G_GREASEPENCIL) { uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw } @@ -2546,7 +2549,7 @@ static void view3d_panel_properties(short cntrl) // VIEW3D_HANDLER_SETTINGS /* to force height */ uiNewPanelHeight(block, 264); - if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT)) { + if(G.f & (G_VERTEXPAINT|G_FACESELECT|G_TEXTUREPAINT|G_WEIGHTPAINT|G_GREASEPENCIL)) { uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw } @@ -2620,6 +2623,49 @@ static void view3d_panel_preview(ScrArea *sa, short cntrl) // VIEW3D_HANDLER_PRE } } +static void view3d_panel_gpencil(short cntrl) // VIEW3D_HANDLER_GREASEPENCIL +{ + uiBlock *block; + View3D *vd; + + vd= G.vd; + + block= uiNewBlock(&curarea->uiblocks, "view3d_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(VIEW3D_HANDLER_GREASEPENCIL); // for close and esc + if (uiNewPanel(curarea, block, "Grease Pencil", "View3d", 100, 30, 318, 204)==0) return; + + if (G.f & (G_VERTEXPAINT|G_WEIGHTPAINT|G_TEXTUREPAINT|G_GREASEPENCIL)) { + uiBlockSetFlag(block, UI_BLOCK_FRONTBUFFER); // force old style frontbuffer draw + } + + /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */ + if (vd->flag2 & V3D_DISPGP) { + if (vd->gpd == NULL) + gpencil_data_setactive(curarea, gpencil_data_addnew()); + } + + if (vd->flag2 & V3D_DISPGP) { + bGPdata *gpd= vd->gpd; + short newheight; + + /* this is a variable height panel, newpanel doesnt force new size on existing panels */ + /* so first we make it default height */ + uiNewPanelHeight(block, 204); + + /* draw button for showing gpencil settings and drawings */ + uiDefButBitS(block, TOG, V3D_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &vd->flag2, 0, 0, 0, 0, "Display freehand annotations overlay over this 3D View"); + + /* extend the panel if the contents won't fit */ + newheight= draw_gpencil_panel(block, gpd, curarea); + uiNewPanelHeight(block, newheight); + } + else { + uiDefButBitS(block, TOG, V3D_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &vd->flag2, 0, 0, 0, 0, "Display freehand annotations overlay over this 3D View"); + uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + } +} + static void view3d_blockhandlers(ScrArea *sa) { @@ -2634,9 +2680,7 @@ static void view3d_blockhandlers(ScrArea *sa) glDisable(GL_DEPTH_TEST); for(a=0; ablockhandler[a]) { - case VIEW3D_HANDLER_PROPERTIES: view3d_panel_properties(v3d->blockhandler[a+1]); break; @@ -2651,7 +2695,10 @@ static void view3d_blockhandlers(ScrArea *sa) break; case VIEW3D_HANDLER_TRANSFORM: view3d_panel_transform_spaces(v3d->blockhandler[a+1]); - break; + break; + case VIEW3D_HANDLER_GREASEPENCIL: + view3d_panel_gpencil(v3d->blockhandler[a+1]); + break; } /* clear action value for event */ v3d->blockhandler[a+1]= 0; @@ -3169,7 +3216,11 @@ void drawview3dspace(ScrArea *sa, void *spacedata) v3d->zbuf= FALSE; glDisable(GL_DEPTH_TEST); } - + + /* draw grease-pencil stuff */ + if (v3d->flag2 & V3D_DISPGP) + draw_gpencil_3dview(sa, 1); + persp(PERSP_WIN); // set ortho /* Draw Sculpt Mode brush */ @@ -3211,6 +3262,11 @@ void drawview3dspace(ScrArea *sa, void *spacedata) if(v3d->persp>1) drawviewborder(); if(v3d->flag2 & V3D_FLYMODE) drawviewborder_flymode(); + + /* draw grease-pencil stuff */ + if (v3d->flag2 & V3D_DISPGP) + draw_gpencil_3dview(sa, 0); + if(!(G.f & G_PLAYANIM)) drawcursor(v3d); if(U.uiflag & USER_SHOW_ROTVIEWICON) draw_view_axis(); @@ -3311,16 +3367,15 @@ void drawview3d_render(struct View3D *v3d, int winx, int winy, float winmat[][4] /* first draw set */ if(G.scene->set) { - for(SETLOOPER(G.scene->set, base)) { if(v3d->lay & base->lay) { if ELEM3(base->object->type, OB_LAMP, OB_CAMERA, OB_LATTICE); else { where_is_object(base->object); - + BIF_ThemeColorBlend(TH_WIRE, TH_BACK, 0.6f); draw_object(base, DRAW_CONSTCOLOR|DRAW_SCENESET); - + if(base->object->transflag & OB_DUPLI) { draw_dupli_objects(v3d, base); } @@ -3377,6 +3432,13 @@ void drawview3d_render(struct View3D *v3d, int winx, int winy, float winmat[][4] glDisable(GL_DEPTH_TEST); } + if(v3d->gpd) { + /* draw grease-pencil overlays + * WARNING: view matrices are altered here! + */ + draw_gpencil_oglrender(v3d, winx, winy); + } + G.f &= ~G_SIMULATION; glFlush(); diff --git a/source/blender/src/editaction.c b/source/blender/src/editaction.c index 4cc0e52ce3f..3251cb33b53 100644 --- a/source/blender/src/editaction.c +++ b/source/blender/src/editaction.c @@ -52,6 +52,7 @@ #include "DNA_mesh_types.h" #include "DNA_nla_types.h" #include "DNA_lattice_types.h" +#include "DNA_gpencil_types.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -90,6 +91,7 @@ #include "BDR_drawaction.h" #include "BDR_editobject.h" +#include "BDR_gpencil.h" #include "mydevice.h" #include "blendef.h" @@ -296,6 +298,16 @@ bActListElem *make_new_actlistelem (void *data, short datatype, void *owner, sho ale->datatype= ALE_IPO; } break; + case ACTTYPE_GPLAYER: + { + bGPDlayer *gpl= (bGPDlayer *)data; + + ale->flag= gpl->flag; + + ale->key_data= NULL; + ale->datatype= ALE_GPFRAME; + } + break; } } @@ -505,6 +517,29 @@ static void actdata_filter_shapekey (ListBase *act_data, Key *key, int filter_mo } } +static void actdata_filter_gpencil (ListBase *act_data, bGPdata *gpd, int filter_mode) +{ + bActListElem *ale; + bGPDlayer *gpl; + + /* check if filtering types are appropriate */ + if ( !(filter_mode & (ACTFILTER_IPOKEYS|ACTFILTER_ONLYICU|ACTFILTER_ACTGROUPED)) ) + { + /* loop over layers as the conditions are acceptable */ + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + /* only if selected */ + if (!(filter_mode & ACTFILTER_SEL) || SEL_GPL(gpl)) { + /* only if editable */ + if (!(filter_mode & ACTFILTER_FOREDIT) || EDITABLE_GPL(gpl)) { + /* add to list */ + ale= make_new_actlistelem(gpl, ACTTYPE_GPLAYER, NULL, ACTTYPE_NONE); + if (ale) BLI_addtail(act_data, ale); + } + } + } + } +} + /* This function filters the active data source to leave only the desired * data types. 'Public' api call. * *act_data: is a pointer to a ListBase, to which the filtered action data @@ -525,6 +560,9 @@ void actdata_filter (ListBase *act_data, int filter_mode, void *data, short data case ACTCONT_SHAPEKEY: actdata_filter_shapekey(act_data, data, filter_mode); break; + case ACTCONT_GPENCIL: + actdata_filter_gpencil(act_data, data, filter_mode); + break; } /* remove any weedy entries */ @@ -743,6 +781,10 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b bActionGroup *agrp= (bActionGroup *)ale->data; agroup_to_keylist(agrp, &act_keys, NULL, NULL); } + else if (ale->type == ACTTYPE_GPLAYER) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + gpl_to_keylist(gpl, &act_keys, NULL, NULL); + } /* loop through keyframes, finding one that was clicked on */ for (ak= act_keys.first; ak; ak= ak->next) { @@ -766,6 +808,10 @@ static void *get_nearest_action_key (float *selx, short *sel, short *ret_type, b data = ale->key_data; *ret_type= ACTTYPE_ICU; } + else if (datatype == ACTCONT_GPENCIL) { + data = ale->data; + *ret_type= ACTTYPE_GPLAYER; + } /* cleanup tempolary lists */ BLI_freelistN(&act_keys); @@ -795,17 +841,43 @@ void *get_action_context (short *datatype) act = (G.saction)? G.saction->action: NULL; key = get_action_mesh_key(); - if (act) { - *datatype= ACTCONT_ACTION; - return act; - } - else if (key) { - *datatype= ACTCONT_SHAPEKEY; - return key; + /* check mode selector */ + if (G.saction) { + switch (G.saction->mode) { + case SACTCONT_ACTION: + *datatype= ACTCONT_ACTION; + return act; + + case SACTCONT_SHAPEKEY: + *datatype= ACTCONT_SHAPEKEY; + return key; + + case SACTCONT_GPENCIL: + *datatype= ACTCONT_GPENCIL; + if (G.saction->pin) + return G.saction->gpd; + else + return gpencil_data_getetime(G.curscreen); + + default: /* includes SACTCONT_DOPESHEET for now */ + *datatype= ACTCONT_NONE; + return NULL; + } } else { - *datatype= ACTCONT_NONE; - return NULL; + /* resort to guessing based on what is available */ + if (act) { + *datatype= ACTCONT_ACTION; + return act; + } + else if (key) { + *datatype= ACTCONT_SHAPEKEY; + return key; + } + else { + *datatype= ACTCONT_NONE; + return NULL; + } } } @@ -1307,12 +1379,18 @@ void duplicate_action_keys (void) if (data == NULL) return; /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* loop through filtered data and duplicate selected keys */ for (ale= act_data.first; ale; ale= ale->next) { - duplicate_ipo_keys((Ipo *)ale->key_data); + if (ale->type == ACTTYPE_GPLAYER) + duplicate_gplayer_frames(ale->data); + else + duplicate_ipo_keys((Ipo *)ale->key_data); } /* free filtered list */ @@ -1398,7 +1476,10 @@ void snap_action_keys(short mode) } /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* snap to frame */ @@ -1408,6 +1489,8 @@ void snap_action_keys(short mode) snap_ipo_keys(ale->key_data, mode); actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); } + else if (ale->type == ACTTYPE_GPLAYER) + snap_gplayer_frames(ale->data, mode); else snap_ipo_keys(ale->key_data, mode); } @@ -1421,6 +1504,7 @@ void snap_action_keys(short mode) allqueue(REDRAWACTION, 0); allqueue(REDRAWIPO, 0); allqueue(REDRAWNLA, 0); + allqueue(REDRAWVIEW3D, 0); } /* this function is responsible for snapping keyframes to frame-times */ @@ -1456,7 +1540,10 @@ void mirror_action_keys(short mode) } /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* mirror */ @@ -1466,6 +1553,8 @@ void mirror_action_keys(short mode) mirror_ipo_keys(ale->key_data, mode); actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); } + else if (ale->type == ACTTYPE_GPLAYER) + mirror_gplayer_frames(ale->data, mode); else mirror_ipo_keys(ale->key_data, mode); } @@ -1550,6 +1639,10 @@ void insertkey_action(void) } } } + else { + /* this tool is not supported in this mode */ + return; + } BIF_undo_push("Insert Key"); allspace(REMAKEIPO, 0); @@ -1573,12 +1666,18 @@ void delete_action_keys (void) if (data == NULL) return; /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* loop through filtered data and delete selected keys */ for (ale= act_data.first; ale; ale= ale->next) { - delete_ipo_keys((Ipo *)ale->key_data); + if (ale->type == ACTTYPE_GPLAYER) + delete_gplayer_frames((bGPDlayer *)ale->data); + else + delete_ipo_keys((Ipo *)ale->key_data); } /* free filtered list */ @@ -1699,6 +1798,7 @@ void clean_action (void) 0.0000001f, 1.0, 0.001, 0.1, "Clean Threshold"); if (!ok) return; + if (datatype == ACTCONT_GPENCIL) return; /* filter data */ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_SEL | ACTFILTER_ONLYICU); @@ -1737,6 +1837,7 @@ void sample_action_keys (void) /* sanity checks */ data= get_action_context(&datatype); if (data == NULL) return; + if (datatype == ACTCONT_GPENCIL) return; /* filter data */ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_ONLYICU); @@ -2096,6 +2197,7 @@ void action_set_ipo_flags (short mode, short event) /* determine what type of data we are operating on */ data = get_action_context(&datatype); if (data == NULL) return; + if (datatype == ACTCONT_GPENCIL) return; /* determine which set of processing we are doing */ switch (mode) { @@ -2194,6 +2296,7 @@ void sethandles_action_keys (int code) /* determine what type of data we are operating on */ data = get_action_context(&datatype); if (data == NULL) return; + if (datatype == ACTCONT_GPENCIL) return; /* filter data */ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); @@ -2232,11 +2335,12 @@ static void numbuts_action () bConstraintChannel *conchan= NULL; IpoCurve *icu= NULL; KeyBlock *kb= NULL; + bGPDlayer *gpl= NULL; short mval[2]; int but=0; - char str[64]; + char str[128]; short expand, protect, mute; float slidermin, slidermax; @@ -2345,6 +2449,18 @@ static void numbuts_action () add_numbut(but++, TOG|SHO, "Expanded", 0, 24, &expand, "Action Group is Expanded"); add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Group is Protected"); } + else if (chantype == ACTTYPE_GPLAYER) { + /* Grease-Pencil Layer */ + gpl= (bGPDlayer *)act_channel; + + strcpy(str, gpl->info); + protect= (gpl->flag & GP_LAYER_LOCKED); + mute = (gpl->flag & GP_LAYER_HIDE); + + add_numbut(but++, TEX, "GP-Layer: ", 0, 128, str, "Name of Grease Pencil Layer"); + add_numbut(but++, TOG|SHO, "Hide", 0, 24, &mute, "Grease Pencil Layer is Visible"); + add_numbut(but++, TOG|SHO, "Protected", 0, 24, &protect, "Grease Pencil Layer is Protected"); + } else { /* nothing under-cursor */ return; @@ -2393,6 +2509,16 @@ static void numbuts_action () if (protect) agrp->flag |= AGRP_PROTECTED; else agrp->flag &= ~AGRP_PROTECTED; } + else if (gpl) { + strcpy(gpl->info, str); + BLI_uniquename(&( ((bGPdata *)data)->layers ), gpl, "GP_Layer", offsetof(bGPDlayer, info), 128); + + if (mute) gpl->flag |= GP_LAYER_HIDE; + else gpl->flag &= ~GP_LAYER_HIDE;; + + if (protect) gpl->flag |= GP_LAYER_LOCKED; + else gpl->flag &= ~GP_LAYER_LOCKED; + } allqueue(REDRAWACTION, 0); allspace(REMAKEIPO, 0); @@ -2524,6 +2650,31 @@ void setflag_action_channels (short mode) } } break; + case ACTTYPE_GPLAYER: + { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + + /* 'protect' and 'mute' */ + if (val == 2) { + /* mute */ + if (mode == 2) + gpl->flag &= ~GP_LAYER_HIDE; + else if (mode == 1) + gpl->flag |= GP_LAYER_HIDE; + else + gpl->flag ^= GP_LAYER_HIDE; + } + else if (val == 1) { + /* protected */ + if (mode == 2) + gpl->flag &= ~GP_LAYER_LOCKED; + else if (mode == 1) + gpl->flag |= GP_LAYER_LOCKED; + else + gpl->flag ^= GP_LAYER_LOCKED; + } + } + break; } } BLI_freelistN(&act_data); @@ -2564,7 +2715,7 @@ static void select_action_group (bAction *act, bActionGroup *agrp, int selectmod set_active_actiongroup(act, agrp, select); } -static void hilight_channel(bAction *act, bActionChannel *achan, short select) +static void hilight_channel (bAction *act, bActionChannel *achan, short select) { bActionChannel *curchan; @@ -2637,7 +2788,7 @@ void select_actionchannel_by_name (bAction *act, char *name, int select) /* exported for outliner (ton) */ /* apparently within active object context */ -int select_channel(bAction *act, bActionChannel *achan, int selectmode) +int select_channel (bAction *act, bActionChannel *achan, int selectmode) { /* Select the channel based on the selection mode */ int flag; @@ -2661,9 +2812,9 @@ int select_channel(bAction *act, bActionChannel *achan, int selectmode) return flag; } -static int select_constraint_channel(bAction *act, - bConstraintChannel *conchan, - int selectmode) +static int select_constraint_channel (bAction *act, + bConstraintChannel *conchan, + int selectmode) { /* Select the constraint channel based on the selection mode */ int flag; @@ -2684,7 +2835,7 @@ static int select_constraint_channel(bAction *act, return flag; } -int select_icu_channel(bAction *act, IpoCurve *icu, int selectmode) +int select_icu_channel (bAction *act, IpoCurve *icu, int selectmode) { /* Select the channel based on the selection mode */ int flag; @@ -2704,6 +2855,29 @@ int select_icu_channel(bAction *act, IpoCurve *icu, int selectmode) return flag; } +int select_gplayer_channel (bGPdata *gpd, bGPDlayer *gpl, int selectmode) +{ + /* Select the channel based on the selection mode */ + int flag; + + switch (selectmode) { + case SELECT_ADD: + gpl->flag |= GP_LAYER_SELECT; + break; + case SELECT_SUBTRACT: + gpl->flag &= ~GP_LAYER_SELECT; + break; + case SELECT_INVERT: + gpl->flag ^= GP_LAYER_SELECT; + break; + } + flag = (gpl->flag & GP_LAYER_SELECT) ? 1 : 0; + + gpencil_layer_setactive(gpd, gpl); + + return flag; +} + /* select only the active action-group's action channels */ void select_action_group_channels (bAction *act, bActionGroup *agrp) @@ -2848,6 +3022,8 @@ void deselect_action_channels (short mode) /* based on type */ if (datatype == ACTCONT_ACTION) deselect_actionchannels(data, mode); + else if (datatype == ACTCONT_GPENCIL) + deselect_gpencil_layers(data, mode); // should shapekey channels be allowed to do this? } @@ -2863,24 +3039,40 @@ void deselect_action_keys (short test, short sel) /* determine what type of data we are operating on */ data = get_action_context(&datatype); if (data == NULL) return; - + + /* determine type-based settings */ + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* See if we should be selecting or deselecting */ if (test) { for (ale= act_data.first; ale; ale= ale->next) { - if (is_ipo_key_selected(ale->key_data)) { - sel= 0; - break; + if (ale->type == ACTTYPE_GPLAYER) { + if (is_gplayer_frame_selected(ale->data)) { + sel= 0; + break; + } + } + else { + if (is_ipo_key_selected(ale->key_data)) { + sel= 0; + break; + } } } } /* Now set the flags */ for (ale= act_data.first; ale; ale= ale->next) { - set_ipo_key_selection(ale->key_data, sel); + if (ale->type == ACTTYPE_GPLAYER) + set_gplayer_frame_selection(ale->data, sel); + else + set_ipo_key_selection(ale->key_data, sel); } /* Cleanup */ @@ -2946,6 +3138,12 @@ void selectall_action_keys (short mval[], short mode, short select_mode) select_icu_bezier_keys(icu, select_mode); } break; + case ACTTYPE_GPLAYER: + { + bGPDlayer *gpl= (bGPDlayer *)act_channel; + select_gpencil_frames(gpl, select_mode); + } + break; } } break; @@ -2971,12 +3169,16 @@ void selectall_action_keys (short mval[], short mode, short select_mode) rectf.xmax = rectf.xmax + 0.5; /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* Now set the flags */ - for (ale= act_data.first; ale; ale= ale->next) + for (ale= act_data.first; ale; ale= ale->next) { borderselect_ipo_key(ale->key_data, rectf.xmin, rectf.xmax, select_mode); + } /* Cleanup */ BLI_freelistN(&act_data); @@ -3058,19 +3260,23 @@ void selectkeys_leftright (short leftright, short select_mode) } /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* select keys on the side where most data occurs */ for (ale= act_data.first; ale; ale= ale->next) { - if(NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) { + if (NLA_ACTION_SCALED && datatype==ACTCONT_ACTION) { actstrip_map_ipo_keys(OBACT, ale->key_data, 0, 1); borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD); actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); } - else { + else if (ale->type == ACTTYPE_GPLAYER) + borderselect_gplayer_frames(ale->data, min, max, SELECT_ADD); + else borderselect_ipo_key(ale->key_data, min, max, SELECT_ADD); - } } /* Cleanup */ @@ -3108,7 +3314,10 @@ void nextprev_action_keyframe (short dir) return; /* get list of keyframes that can be used (in global-time) */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); for (ale= act_data.first; ale; ale= ale->next) { @@ -3117,6 +3326,8 @@ void nextprev_action_keyframe (short dir) make_cfra_list(ale->key_data, &elems); actstrip_map_ipo_keys(OBACT, ale->key_data, 1, 1); } + else if (ale->type == ACTTYPE_GPLAYER) + gplayer_make_cfra_list(ale->key_data, &elems, 0); else make_cfra_list(ale->key_data, &elems); } @@ -3199,11 +3410,20 @@ void column_select_action_keys (int mode) /* build list of columns */ switch (mode) { case 1: /* list of selected keys */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); - actdata_filter(&act_data, filter, data, datatype); - - for (ale= act_data.first; ale; ale= ale->next) - make_sel_cfra_list(ale->key_data, &elems); + if (datatype == ACTCONT_GPENCIL) { + filter= (ACTFILTER_VISIBLE); + actdata_filter(&act_data, filter, data, datatype); + + for (ale= act_data.first; ale; ale= ale->next) + gplayer_make_cfra_list(ale->data, &elems, 1); + } + else { + filter= (ACTFILTER_VISIBLE | ACTFILTER_IPOKEYS); + actdata_filter(&act_data, filter, data, datatype); + + for (ale= act_data.first; ale; ale= ale->next) + make_sel_cfra_list(ale->key_data, &elems); + } BLI_freelistN(&act_data); break; @@ -3231,19 +3451,34 @@ void column_select_action_keys (int mode) /* loop through all of the keys and select additional keyframes * based on the keys found to be selected above */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_ONLYICU); actdata_filter(&act_data, filter, data, datatype); for (ale= act_data.first; ale; ale= ale->next) { for (ce= elems.first; ce; ce= ce->next) { - for (icu= ale->key_data; icu; icu= icu->next) { - BezTriple *bezt; - int verts = 0; + /* select elements with frame number matching cfraelem */ + if (ale->type == ACTTYPE_GPLAYER) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + bGPDframe *gpf; - for (bezt=icu->bezt; vertstotvert; bezt++, verts++) { - if (bezt) { - if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) ) - bezt->f2 |= 1; + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if ( (int)ce->cfra == gpf->framenum ) + gpf->flag |= GP_FRAME_SELECT; + } + } + else { + for (icu= ale->key_data; icu; icu= icu->next) { + BezTriple *bezt; + int verts = 0; + + for (bezt=icu->bezt; vertstotvert; bezt++, verts++) { + if (bezt) { + if( (int)(ce->cfra) == (int)(bezt->vec[1][0]) ) + bezt->f2 |= 1; + } } } } @@ -3272,7 +3507,7 @@ void borderselect_actionchannels (void) /* determine what type of data we are operating on */ data = get_action_context(&datatype); if (data == NULL) return; - if (datatype != ACTCONT_ACTION) return; + if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)==0) return; /* draw and handle the borderselect stuff (ui) and get the select rect */ if ( (val = get_border(&rect, 3)) ) { @@ -3344,6 +3579,16 @@ void borderselect_actionchannels (void) icu->flag &= ~IPO_SELECT; } break; + case ACTTYPE_GPLAYER: /* grease-pencil layer */ + { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + + if (selectmode == SELECT_ADD) + gpl->flag |= GP_LAYER_SELECT; + else + gpl->flag &= ~GP_LAYER_SELECT; + } + break; } /* select action-channel 'owner' */ @@ -3460,6 +3705,9 @@ void borderselect_action (void) borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode); } } + else if (ale->type == ACTTYPE_GPLAYER) { + borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode); + } break; case ACTEDIT_BORDERSEL_CHA: /* all in channel(s) */ if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) { @@ -3481,6 +3729,9 @@ void borderselect_action (void) select_ipo_bezier_keys(conchan->ipo, selectmode); } } + else if (ale->type == ACTTYPE_GPLAYER) { + select_gpencil_frames(ale->data, selectmode); + } } break; default: /* any keyframe inside region defined by region */ @@ -3503,6 +3754,9 @@ void borderselect_action (void) borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, selectmode); } } + else if (ale->type == ACTTYPE_GPLAYER) { + borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode); + } } } @@ -3533,6 +3787,8 @@ static void mouse_action (int selectmode) bActionChannel *achan= NULL; bConstraintChannel *conchan= NULL; IpoCurve *icu= NULL; + bGPdata *gpd = NULL; + bGPDlayer *gpl = NULL; TimeMarker *marker, *pmarker; void *act_channel; @@ -3543,6 +3799,7 @@ static void mouse_action (int selectmode) data = get_action_context(&datatype); if (data == NULL) return; if (datatype == ACTCONT_ACTION) act= (bAction *)data; + if (datatype == ACTCONT_GPENCIL) gpd= (bGPdata *)data; act_channel= get_nearest_action_key(&selx, &sel, &act_type, &achan); marker= find_nearest_marker(SCE_MARKERS, 1); @@ -3615,6 +3872,9 @@ static void mouse_action (int selectmode) case ACTTYPE_GROUP: agrp= (bActionGroup *)act_channel; break; + case ACTTYPE_GPLAYER: + gpl= (bGPDlayer *)act_channel; + break; default: return; } @@ -3638,6 +3898,13 @@ static void mouse_action (int selectmode) set_active_actiongroup(act, agrp, 1); } } + else if (datatype == ACTCONT_GPENCIL) { + deselect_action_channels(0); + + /* Highlight gpencil layer */ + gpl->flag |= GP_LAYER_SELECT; + gpencil_layer_setactive(gpd, gpl); + } } if (icu) @@ -3654,6 +3921,8 @@ static void mouse_action (int selectmode) select_ipo_key(conchan->ipo, selx, selectmode); } } + else if (gpl) + select_gpencil_frame(gpl, selx, selectmode); std_rmouse_transform(transform_action_keys); @@ -3670,6 +3939,7 @@ static void mouse_action (int selectmode) static void mouse_actionchannels (short mval[]) { bAction *act= G.saction->action; + bGPdata *gpd= G.saction->gpd; void *data, *act_channel; short datatype, chantype; @@ -3816,6 +4086,24 @@ static void mouse_actionchannels (short mval[]) } } break; + case ACTTYPE_GPLAYER: + { + bGPDlayer *gpl= (bGPDlayer *)act_channel; + + if (mval[0] >= (NAMEWIDTH-16)) { + /* toggle lock */ + gpl->flag ^= GP_LAYER_LOCKED; + } + else if (mval[0] >= (NAMEWIDTH-32)) { + /* toggle hide */ + gpl->flag ^= GP_LAYER_HIDE; + } + else { + /* select/deselect */ + select_gplayer_channel(gpd, gpl, SELECT_INVERT); + } + } + break; default: return; } @@ -4482,7 +4770,7 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt) case RIGHTMOUSE: /* Clicking in the channel area */ if ((G.v2d->mask.xmin) && (mval[0] < NAMEWIDTH)) { - if (datatype == ACTCONT_ACTION) { + if (ELEM(datatype, ACTCONT_ACTION, ACTCONT_GPENCIL)) { /* mouse is over action channels */ if (G.qual == LR_CTRLKEY) numbuts_action(); @@ -4832,8 +5120,12 @@ void winqreadactionspace(ScrArea *sa, void *spacedata, BWinEvent *evt) case DELKEY: case XKEY: if (okee("Erase selected")) { - if (mval[0] < NAMEWIDTH) - delete_action_channels(); + if (mval[0] < NAMEWIDTH) { + if (datatype == ACTCONT_ACTION) + delete_action_channels(); + else if (datatype == ACTCONT_GPENCIL) + delete_gpencil_layers(); + } else delete_action_keys(); diff --git a/source/blender/src/editaction_gpencil.c b/source/blender/src/editaction_gpencil.c new file mode 100644 index 00000000000..b91c5a8b332 --- /dev/null +++ b/source/blender/src/editaction_gpencil.c @@ -0,0 +1,549 @@ +/** + * $Id: editaction_gpencil.c 14881 2008-05-18 10:41:42Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "DNA_listBase.h" +#include "DNA_action_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" +#include "DNA_view2d_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" +#include "BKE_ipo.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "BIF_butspace.h" +#include "BIF_graphics.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_resources.h" +#include "BIF_space.h" +#include "BIF_screen.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BIF_editaction.h" +#include "BSE_editaction_types.h" + +#include "BDR_gpencil.h" +#include "BIF_drawgpencil.h" + +#include "BSE_drawipo.h" +#include "BSE_headerbuttons.h" +#include "BSE_time.h" +#include "BSE_view.h" + +#include "blendef.h" +#include "butspace.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" + +/* ***************************************** */ +/* NOTE ABOUT THIS FILE: + * This file contains code for editing Grease Pencil data in the Action Editor + * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings. + * Therefore, this file mostly contains functions for selecting Grease-Pencil frames. + */ +/* ***************************************** */ +/* Generics - Loopers */ + +/* Loops over the gp-frames for a gp-layer, and applies the given callback */ +short gplayer_frames_looper (bGPDlayer *gpl, short (*gpf_cb)(bGPDframe *)) +{ + bGPDframe *gpf; + + /* error checker */ + if (gpl == NULL) + return 0; + + /* do loop */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + /* execute callback */ + if (gpf_cb(gpf)) + return 1; + } + + /* nothing to return */ + return 0; +} + +/* ****************************************** */ +/* Data Conversion Tools */ + +/* make a listing all the gp-frames in a layer as cfraelems */ +void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel) +{ + bGPDframe *gpf; + CfraElem *ce; + + /* error checking */ + if (ELEM(NULL, gpl, elems)) + return; + + /* loop through gp-frames, adding */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) { + ce= MEM_callocN(sizeof(CfraElem), "CfraElem"); + + ce->cfra= gpf->framenum; + ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0; + + BLI_addtail(elems, ce); + } + } +} + +/* ***************************************** */ +/* Selection Tools */ + +/* check if one of the frames in this layer is selected */ +short is_gplayer_frame_selected (bGPDlayer *gpl) +{ + bGPDframe *gpf; + + /* error checking */ + if (gpl == NULL) + return 0; + + /* stop at the first one found */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) + return 1; + } + + /* not found */ + return 0; +} + +/* helper function - select gp-frame based on SELECT_* mode */ +static void gpframe_select (bGPDframe *gpf, short select_mode) +{ + switch (select_mode) { + case SELECT_ADD: + gpf->flag |= GP_FRAME_SELECT; + break; + case SELECT_SUBTRACT: + gpf->flag &= ~GP_FRAME_SELECT; + break; + case SELECT_INVERT: + gpf->flag ^= GP_FRAME_SELECT; + break; + } +} + +/* set all/none/invert select (like above, but with SELECT_* modes) */ +void select_gpencil_frames (bGPDlayer *gpl, short select_mode) +{ + bGPDframe *gpf; + + /* error checking */ + if (gpl == NULL) + return; + + /* handle according to mode */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + gpframe_select(gpf, select_mode); + } +} + +/* set all/none/invert select */ +void set_gplayer_frame_selection (bGPDlayer *gpl, short mode) +{ + /* error checking */ + if (gpl == NULL) + return; + + /* convert mode to select_mode */ + switch (mode) { + case 2: + mode= SELECT_INVERT; + break; + case 1: + mode= SELECT_ADD; + break; + case 0: + mode= SELECT_SUBTRACT; + break; + default: + return; + } + + /* now call the standard function */ + select_gpencil_frames (gpl, mode); +} + +void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode) +{ + bGPDframe *gpf; + + /* search through frames for a match */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->framenum == selx) + gpframe_select(gpf, select_mode); + } +} + +void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode) +{ + bGPDframe *gpf; + + /* only select those frames which are in bounds */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (IN_RANGE(gpf->framenum, min, max)) + gpframe_select(gpf, select_mode); + } +} + + +/* De-selects or inverts the selection of Layers for a grease-pencil block + * mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all + */ +void deselect_gpencil_layers (bGPdata *gpd, short mode) +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale; + int filter, sel=1; + + /* filter data */ + filter= ACTFILTER_VISIBLE; + actdata_filter(&act_data, filter, gpd, ACTCONT_GPENCIL); + + /* See if we should be selecting or deselecting */ + if (mode == 1) { + for (ale= act_data.first; ale; ale= ale->next) { + if (sel == 0) + break; + + if (ale->flag & GP_LAYER_SELECT) + sel= 0; + } + } + else + sel= 0; + + /* Now set the flags */ + for (ale= act_data.first; ale; ale= ale->next) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + + if (mode == 2) + gpl->flag ^= GP_LAYER_SELECT; + else if (sel) + gpl->flag |= GP_LAYER_SELECT; + else + gpl->flag &= ~GP_LAYER_SELECT; + + gpl->flag &= ~GP_LAYER_ACTIVE; + } + + /* Cleanup */ + BLI_freelistN(&act_data); +} + +/* ***************************************** */ +/* Frame Editing Tools */ + +void delete_gpencil_layers (void) +{ + ListBase act_data = {NULL, NULL}; + bActListElem *ale, *next; + bGPdata *gpd; + void *data; + short datatype; + int filter; + + /* determine what type of data we are operating on */ + data = get_action_context(&datatype); + if (data == NULL) return; + if (datatype != ACTCONT_GPENCIL) return; + gpd= (bGPdata *)data; + + /* filter data */ + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL); + actdata_filter(&act_data, filter, data, datatype); + + /* clean up grease-pencil layers */ + for (ale= act_data.first; ale; ale= next) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + next= ale->next; + + /* free layer and its data */ + if (SEL_GPL(gpl)) { + free_gpencil_frames(gpl); + BLI_freelinkN(&gpd->layers, gpl); + } + + /* free temp memory */ + BLI_freelinkN(&act_data, ale); + } + + BIF_undo_push("Delete GPencil Layers"); + allspace(REDRAWVIEW3D, 0); + allqueue(REDRAWACTION, 0); +} + +/* Delete selected frames */ +void delete_gplayer_frames (bGPDlayer *gpl) +{ + bGPDframe *gpf, *gpfn; + + /* error checking */ + if (gpl == NULL) + return; + + /* check for frames to delete */ + for (gpf= gpl->frames.first; gpf; gpf= gpfn) { + gpfn= gpf->next; + + if (gpf->flag & GP_FRAME_SELECT) + gpencil_layer_delframe(gpl, gpf); + } +} + +/* Duplicate selected frames from given gp-layer */ +void duplicate_gplayer_frames (bGPDlayer *gpl) +{ + bGPDframe *gpf, *gpfn; + + /* error checking */ + if (gpl == NULL) + return; + + /* duplicate selected frames */ + for (gpf= gpl->frames.first; gpf; gpf= gpfn) { + gpfn= gpf->next; + + /* duplicate this frame */ + if (gpf->flag & GP_FRAME_SELECT) { + bGPDframe *gpfd; + bGPDstroke *gps; + + /* duplicate frame, and deselect self */ + gpfd= MEM_dupallocN(gpf); + gpf->flag &= ~GP_FRAME_SELECT; + + /* duplicate list of strokes too */ + duplicatelist(&gpfd->strokes, &gpf->strokes); + + /* dupalloc only makes another copy of mem, but doesn't adjust pointers */ + for (gps= gpfd->strokes.first; gps; gps= gps->next) { + gps->points= MEM_dupallocN(gps->points); + } + + BLI_insertlinkafter(&gpl->frames, gpf, gpfd); + } + } +} + +/* -------------------------------------- */ +/* Snap Tools */ + +static short snap_gpf_nearest (bGPDframe *gpf) +{ + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum= (int)(floor(gpf->framenum+0.5)); + return 0; +} + +static short snap_gpf_nearestsec (bGPDframe *gpf) +{ + float secf = FPS; + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf); + return 0; +} + +static short snap_gpf_cframe (bGPDframe *gpf) +{ + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum= (int)CFRA; + return 0; +} + +static short snap_gpf_nearmarker (bGPDframe *gpf) +{ + if (gpf->flag & GP_FRAME_SELECT) + gpf->framenum= (int)find_nearest_marker_time(gpf->framenum); + return 0; +} + + +/* snap selected frames to ... */ +void snap_gplayer_frames (bGPDlayer *gpl, short mode) +{ + switch (mode) { + case 1: /* snap to nearest frame */ + gplayer_frames_looper(gpl, snap_gpf_nearest); + break; + case 2: /* snap to current frame */ + gplayer_frames_looper(gpl, snap_gpf_cframe); + break; + case 3: /* snap to nearest marker */ + gplayer_frames_looper(gpl, snap_gpf_nearmarker); + break; + case 4: /* snap to nearest second */ + gplayer_frames_looper(gpl, snap_gpf_nearestsec); + break; + default: /* just in case */ + gplayer_frames_looper(gpl, snap_gpf_nearest); + break; + } +} + +/* -------------------------------------- */ +/* Mirror Tools */ + +static short mirror_gpf_cframe (bGPDframe *gpf) +{ + float diff; + + if (gpf->flag & GP_FRAME_SELECT) { + diff= ((float)CFRA - gpf->framenum); + gpf->framenum= ((float)CFRA + diff); + } + + return 0; +} + +static short mirror_gpf_yaxis (bGPDframe *gpf) +{ + float diff; + + if (gpf->flag & GP_FRAME_SELECT) { + diff= (0.0f - gpf->framenum); + gpf->framenum= (0.0f + diff); + } + + return 0; +} + +static short mirror_gpf_xaxis (bGPDframe *gpf) +{ + float diff; + + if (gpf->flag & GP_FRAME_SELECT) { + diff= (0.0f - gpf->framenum); + gpf->framenum= (0.0f + diff); + } + + return 0; +} + +static short mirror_gpf_marker (bGPDframe *gpf) +{ + static TimeMarker *marker; + static short initialised = 0; + float diff; + + /* In order for this mirror function to work without + * any extra arguments being added, we use the case + * of bezt==NULL to denote that we should find the + * marker to mirror over. The static pointer is safe + * to use this way, as it will be set to null after + * each cycle in which this is called. + */ + + if (gpf) { + /* mirroring time */ + if ((gpf->flag & GP_FRAME_SELECT) && (marker)) { + diff= (marker->frame - gpf->framenum); + gpf->framenum= (marker->frame + diff); + } + } + else { + /* initialisation time */ + if (initialised) { + /* reset everything for safety */ + marker = NULL; + initialised = 0; + } + else { + /* try to find a marker */ + for (marker= G.scene->markers.first; marker; marker=marker->next) { + if (marker->flag & SELECT) { + initialised = 1; + break; + } + } + + if (initialised == 0) + marker = NULL; + } + } + + return 0; +} + + +/* mirror selected gp-frames on... */ +void mirror_gplayer_frames (bGPDlayer *gpl, short mode) +{ + switch (mode) { + case 1: /* mirror over current frame */ + gplayer_frames_looper(gpl, mirror_gpf_cframe); + break; + case 2: /* mirror over frame 0 */ + gplayer_frames_looper(gpl, mirror_gpf_yaxis); + break; + case 3: /* mirror over value 0 */ + gplayer_frames_looper(gpl, mirror_gpf_xaxis); + break; + case 4: /* mirror over marker */ + mirror_gpf_marker(NULL); + gplayer_frames_looper(gpl, mirror_gpf_marker); + mirror_gpf_marker(NULL); + break; + default: /* just in case */ + gplayer_frames_looper(gpl, mirror_gpf_yaxis); + break; + } +} + +/* ***************************************** */ diff --git a/source/blender/src/editmesh.c b/source/blender/src/editmesh.c index 9eef61e11f9..188f7476728 100644 --- a/source/blender/src/editmesh.c +++ b/source/blender/src/editmesh.c @@ -340,8 +340,9 @@ void free_editface(EditFace *efa) #endif EM_remove_selection(efa, EDITFACE); - if (G.editMesh->act_face==efa) - EM_set_actFace(NULL); + if (G.editMesh->act_face==efa) { + EM_set_actFace( G.editMesh->faces.first == efa ? NULL : G.editMesh->faces.first); + } CustomData_em_free_block(&G.editMesh->fdata, &efa->data); if(efa->fast==0) @@ -1059,7 +1060,11 @@ void make_editMesh() EM_hide_reset(); /* sets helper flags which arent saved */ EM_fgon_flags(); - + + if (EM_get_actFace(0)==NULL) { + EM_set_actFace( G.editMesh->faces.first ); /* will use the first face, this is so we alwats have an active face */ + } + /* vertex coordinates change with cache edit, need to recalc */ if(cacheedit) recalc_editnormals(); diff --git a/source/blender/src/editnode.c b/source/blender/src/editnode.c index 4c7334c55e0..5c137e67c1a 100644 --- a/source/blender/src/editnode.c +++ b/source/blender/src/editnode.c @@ -82,6 +82,7 @@ #include "BLI_storage_types.h" #include "BDR_editobject.h" +#include "BDR_gpencil.h" #include "RE_pipeline.h" #include "IMB_imbuf_types.h" @@ -2305,6 +2306,7 @@ static int node_uiDoBlocks(ScrArea *sa, short event) SpaceNode *snode= sa->spacedata.first; ListBase *lb= &sa->uiblocks; ListBase listb= *lb; + uiBlock *block; bNode *node; rctf rect; void *prev, *next; @@ -2319,13 +2321,36 @@ static int node_uiDoBlocks(ScrArea *sa, short event) return UI_NOTHING; } + /* evil hack: try to do grease-pencil floating panel (like for nodes) */ + block= uiGetBlock("nodes_panel_gpencil", sa); + if (block) { + /* try to process events here... if failed, just carry on */ + /* when there's menus, the prev pointer becomes zero! */ + prev= ((struct Link *)block)->prev; + next= ((struct Link *)block)->next; + ((struct Link *)block)->prev= NULL; + ((struct Link *)block)->next= NULL; + + lb->first= lb->last= block; + retval= uiDoBlocks(lb, event, 1); + + ((struct Link *)block)->prev= prev; + ((struct Link *)block)->next= next; + + *lb= listb; + + /* if something happened, get the heck outta here */ + if (retval != UI_NOTHING) + return retval; + } + + rect.xmin -= 2.0f; rect.ymin -= 2.0f; rect.xmax = rect.xmin + 4.0f; rect.ymax = rect.ymin + 4.0f; for(node= snode->edittree->nodes.first; node; node= node->next) { - uiBlock *block; char str[32]; /* retreive unique block name, see also drawnode.c */ @@ -2369,14 +2394,16 @@ void winqreadnodespace(ScrArea *sa, void *spacedata, BWinEvent *evt) if(snode->nodetree==NULL) return; if(val) { - - if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0; - + if( node_uiDoBlocks(sa, event)!=UI_NOTHING ) event= 0; + fromlib= (snode->id && snode->id->lib); switch(event) { case LEFTMOUSE: - if(fromlib) { + if(gpencil_do_paint(sa)) { + return; + } + else if(fromlib) { if(node_mouse_groupheader(snode)==0) node_mouse_select(snode, event); } diff --git a/source/blender/src/editobject.c b/source/blender/src/editobject.c index 2e5785eaab8..6af4f47ed11 100644 --- a/source/blender/src/editobject.c +++ b/source/blender/src/editobject.c @@ -5505,6 +5505,7 @@ void selectlinks(int nr) allqueue(REDRAWDATASELECT, 0); allqueue(REDRAWOOPS, 0); BIF_undo_push("Select linked"); + countall(); } } diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c new file mode 100644 index 00000000000..fa32b0ac7d4 --- /dev/null +++ b/source/blender/src/gpencil.c @@ -0,0 +1,1290 @@ +/** + * $Id: gpencil.c 14881 2008-05-18 10:41:42Z aligorith $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "MEM_guardedalloc.h" + +#include "BMF_Api.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "DNA_listBase.h" +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" +#include "BIF_butspace.h" +#include "BIF_graphics.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_resources.h" +#include "BIF_space.h" +#include "BIF_screen.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BDR_gpencil.h" +#include "BIF_drawgpencil.h" + +#include "BSE_drawipo.h" +#include "BSE_headerbuttons.h" +#include "BSE_view.h" + +#include "blendef.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" + +/* ************************************************** */ +/* GENERAL STUFF */ + +/* --------- Memory Management ------------ */ + +/* Free strokes belonging to a gp-frame */ +void free_gpencil_strokes (bGPDframe *gpf) +{ + bGPDstroke *gps, *gpsn; + + /* error checking */ + if (gpf == NULL) return; + + /* free strokes */ + for (gps= gpf->strokes.first; gps; gps= gpsn) { + gpsn= gps->next; + + /* free stroke memory arrays, then stroke itself */ + MEM_freeN(gps->points); + BLI_freelinkN(&gpf->strokes, gps); + } +} + +/* Free all of a gp-layer's frames */ +void free_gpencil_frames (bGPDlayer *gpl) +{ + bGPDframe *gpf, *gpfn; + + /* error checking */ + if (gpl == NULL) return; + + /* free frames */ + for (gpf= gpl->frames.first; gpf; gpf= gpfn) { + gpfn= gpf->next; + + /* free strokes and their associated memory */ + free_gpencil_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + } +} + +/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */ +void free_gpencil_layers (ListBase *list) +{ + bGPDlayer *gpl, *gpln; + + /* error checking */ + if (list == NULL) return; + + /* delete layers*/ + for (gpl= list->first; gpl; gpl= gpln) { + gpln= gpl->next; + + /* free layers and their data */ + free_gpencil_frames(gpl); + BLI_freelinkN(list, gpl); + } +} + +/* Free gp-data and all it's related data */ +void free_gpencil_data (bGPdata *gpd) +{ + /* free layers then data itself */ + free_gpencil_layers(&gpd->layers); + MEM_freeN(gpd); +} + +/* -------- Container Creation ---------- */ + +/* add a new gp-frame to the given layer */ +bGPDframe *gpencil_frame_addnew (bGPDlayer *gpl, int cframe) +{ + bGPDframe *gpf, *gf; + short state=0; + + /* error checking */ + if ((gpl == NULL) || (cframe <= 0)) + return NULL; + + /* allocate memory for this frame */ + gpf= MEM_callocN(sizeof(bGPDframe), "bGPDframe"); + gpf->framenum= cframe; + + /* find appropriate place to add frame */ + if (gpl->frames.first) { + for (gf= gpl->frames.first; gf; gf= gf->next) { + /* check if frame matches one that is supposed to be added */ + if (gf->framenum == cframe) { + state= -1; + break; + } + + /* if current frame has already exceeded the frame to add, add before */ + if (gf->framenum > cframe) { + BLI_insertlinkbefore(&gpl->frames, gf, gpf); + state= 1; + break; + } + } + } + + /* check whether frame was added successfully */ + if (state == -1) { + MEM_freeN(gpf); + printf("Error: frame (%d) existed already for this layer \n", cframe); + } + else if (state == 0) { + /* add to end then! */ + BLI_addtail(&gpl->frames, gpf); + } + + /* return frame */ + return gpf; +} + +/* add a new gp-layer and make it the active layer */ +bGPDlayer *gpencil_layer_addnew (bGPdata *gpd) +{ + bGPDlayer *gpl; + + /* check that list is ok */ + if (gpd == NULL) + return NULL; + + /* allocate memory for frame and add to end of list */ + gpl= MEM_callocN(sizeof(bGPDlayer), "bGPDlayer"); + + /* add to datablock */ + BLI_addtail(&gpd->layers, gpl); + + /* set basic settings */ + gpl->color[3]= 1.0f; + gpl->thickness = 1; + + /* auto-name */ + sprintf(gpl->info, "GP_Layer"); + BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info[0]), 128); + + /* make this one the active one */ + gpencil_layer_setactive(gpd, gpl); + + /* return layer */ + return gpl; +} + +/* add a new gp-datablock */ +bGPdata *gpencil_data_addnew (void) +{ + bGPdata *gpd; + + /* allocate memory for a new block */ + gpd= MEM_callocN(sizeof(bGPdata), "GreasePencilData"); + + /* initial settings */ + /* it is quite useful to be able to see this info, so on by default */ + gpd->flag = GP_DATA_DISPINFO; + + return gpd; +} + +/* -------- Data Duplication ---------- */ + +/* make a copy of a given gpencil datablock */ +bGPdata *gpencil_data_duplicate (bGPdata *src) +{ + bGPdata *dst; + bGPDlayer *gpld, *gpls; + bGPDframe *gpfd, *gpfs; + bGPDstroke *gps; + + /* error checking */ + if (src == NULL) + return NULL; + + /* make a copy of the base-data */ + dst= MEM_dupallocN(src); + + /* copy layers */ + duplicatelist(&dst->layers, &src->layers); + + for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls; + gpld=gpld->next, gpls=gpls->next) + { + /* copy frames */ + duplicatelist(&gpld->frames, &gpls->frames); + + for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs; + gpfd=gpfd->next, gpfs=gpfs->next) + { + /* copy strokes */ + duplicatelist(&gpfd->strokes, &gpfs->strokes); + + for (gps= gpfd->strokes.first; gps; gps= gps->next) + { + gps->points= MEM_dupallocN(gps->points); + } + } + } + + /* return new */ + return dst; +} + +/* ----------- GP-Datablock API ------------- */ + +/* get the appropriate bGPdata from the active/given context */ +bGPdata *gpencil_data_getactive (ScrArea *sa) +{ + /* error checking */ + if ((sa == NULL) && (curarea == NULL)) + return NULL; + if (sa == NULL) + sa= curarea; + + /* handle depending on spacetype */ + switch (sa->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d= sa->spacedata.first; + return v3d->gpd; + } + break; + case SPACE_NODE: + { + SpaceNode *snode= sa->spacedata.first; + return snode->gpd; + } + break; + case SPACE_SEQ: + { + SpaceSeq *sseq= sa->spacedata.first; + + /* only applicable for "Image Preview" mode */ + if (sseq->mainb) + return sseq->gpd; + } + break; + } + + /* nothing found */ + return NULL; +} + +/* set bGPdata for the active/given context, and return success/fail */ +short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd) +{ + /* error checking */ + if ((sa == NULL) && (curarea == NULL)) + return 0; + if (gpd == NULL) + return 0; + if (sa == NULL) + sa= curarea; + + /* handle depending on spacetype */ + // TODO: someday we should have multi-user data, so no need to loose old data + switch (sa->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d= sa->spacedata.first; + + /* free the existing block */ + if (v3d->gpd) + free_gpencil_data(v3d->gpd); + v3d->gpd= gpd; + + return 1; + } + break; + case SPACE_NODE: + { + SpaceNode *snode= sa->spacedata.first; + + /* free the existing block */ + if (snode->gpd) + free_gpencil_data(snode->gpd); + snode->gpd= gpd; + + /* set special settings */ + gpd->flag |= GP_DATA_VIEWALIGN; + + return 1; + } + break; + case SPACE_SEQ: + { + SpaceSeq *sseq= sa->spacedata.first; + + /* only applicable if right mode */ + if (sseq->mainb) { + /* free the existing block */ + if (sseq->gpd) + free_gpencil_data(sseq->gpd); + sseq->gpd= gpd; + + return 1; + } + } + break; + } + + /* failed to add */ + return 0; +} + +/* Find gp-data destined for editing in animation editor (for editing time) */ +bGPdata *gpencil_data_getetime (bScreen *sc) +{ + bGPdata *gpd= NULL; + ScrArea *sa; + + /* error checking */ + if (sc == NULL) + return NULL; + + /* search through areas, checking if an appropriate gp-block is available + * (this assumes that only one will have the active flag set) + */ + for (sa= sc->areabase.first; sa; sa= sa->next) { + /* handle depending on space type */ + switch (sa->spacetype) { + case SPACE_VIEW3D: /* 3d-view */ + { + View3D *v3d= sa->spacedata.first; + gpd= v3d->gpd; + } + break; + case SPACE_NODE: /* Node Editor */ + { + SpaceNode *snode= sa->spacedata.first; + gpd= snode->gpd; + } + break; + case SPACE_SEQ: /* Sequence Editor - Image Preview */ + { + SpaceSeq *sseq= sa->spacedata.first; + + if (sseq->mainb) + gpd= sseq->gpd; + else + gpd= NULL; + } + break; + + default: /* unsupported space-type */ + gpd= NULL; + break; + } + + /* check if ok */ + if ((gpd) && (gpd->flag & GP_DATA_EDITTIME)) + return gpd; + } + + /* didn't find a match */ + return NULL; +} + +/* make sure only the specified view can have gp-data for time editing + * - gpd can be NULL, if we wish to make sure no gp-data is being edited + */ +void gpencil_data_setetime (bScreen *sc, bGPdata *gpd) +{ + bGPdata *gpdn= NULL; + ScrArea *sa; + + /* error checking */ + if (sc == NULL) + return; + + /* search through areas, checking if an appropriate gp-block is available + * (this assumes that only one will have the active flag set) + */ + for (sa= sc->areabase.first; sa; sa= sa->next) { + /* handle depending on space type */ + switch (sa->spacetype) { + case SPACE_VIEW3D: /* 3d-view */ + { + View3D *v3d= sa->spacedata.first; + gpdn= v3d->gpd; + } + break; + case SPACE_NODE: /* Node Editor */ + { + SpaceNode *snode= sa->spacedata.first; + gpdn= snode->gpd; + } + break; + case SPACE_SEQ: /* Sequence Editor - Image Preview */ + { + SpaceSeq *sseq= sa->spacedata.first; + gpdn= sseq->gpd; + } + break; + + default: /* unsupported space-type */ + gpdn= NULL; + break; + } + + /* clear flag if a gp-data block found */ + if (gpdn) + gpdn->flag &= ~GP_DATA_EDITTIME; + } + + /* set active flag for this block (if it is provided) */ + if (gpd) + gpd->flag |= GP_DATA_EDITTIME; +} + +/* -------- GP-Frame API ---------- */ + +/* delete the last stroke of the given frame */ +void gpencil_frame_delete_laststroke (bGPDframe *gpf) +{ + bGPDstroke *gps= (gpf) ? gpf->strokes.last : NULL; + + /* error checking */ + if (ELEM(NULL, gpf, gps)) + return; + + /* free the stroke and its data */ + MEM_freeN(gps->points); + BLI_freelinkN(&gpf->strokes, gps); +} + +/* -------- GP-Layer API ---------- */ + +/* get the appropriate gp-frame from a given layer + * - this sets the layer's actframe var (if allowed to) + * - extension beyond range (if first gp-frame is after all frame in interest and cannot add) + */ +bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) +{ + bGPDframe *gpf = NULL; + short found = 0; + + /* error checking */ + if (gpl == NULL) return NULL; + if (cframe <= 0) cframe = 1; + + /* check if there is already an active frame */ + if (gpl->actframe) { + gpf= gpl->actframe; + + /* do not allow any changes to layer's active frame if layer is locked */ + if (gpl->flag & GP_LAYER_LOCKED) + return gpf; + /* do not allow any changes to actframe if frame has painting tag attached to it */ + if (gpf->flag & GP_FRAME_PAINT) + return gpf; + + /* try to find matching frame */ + if (gpf->framenum < cframe) { + for (; gpf; gpf= gpf->next) { + if (gpf->framenum == cframe) { + found= 1; + break; + } + else if ((gpf->next) && (gpf->next->framenum > cframe)) { + found= 1; + break; + } + } + + /* set the appropriate frame */ + if (addnew) { + if ((found) && (gpf->framenum == cframe)) + gpl->actframe= gpf; + else + gpl->actframe= gpencil_frame_addnew(gpl, cframe); + } + else if (found) + gpl->actframe= gpf; + else + gpl->actframe= gpl->frames.last; + } + else { + for (; gpf; gpf= gpf->prev) { + if (gpf->framenum <= cframe) { + found= 1; + break; + } + } + + /* set the appropriate frame */ + if (addnew) { + if ((found) && (gpf->framenum == cframe)) + gpl->actframe= gpf; + else + gpl->actframe= gpencil_frame_addnew(gpl, cframe); + } + else if (found) + gpl->actframe= gpf; + else + gpl->actframe= gpl->frames.first; + } + } + else if (gpl->frames.first) { + /* check which of the ends to start checking from */ + const int first= ((bGPDframe *)(gpl->frames.first))->framenum; + const int last= ((bGPDframe *)(gpl->frames.last))->framenum; + + if (abs(cframe-first) > abs(cframe-last)) { + /* find gp-frame which is less than or equal to cframe */ + for (gpf= gpl->frames.last; gpf; gpf= gpf->prev) { + if (gpf->framenum <= cframe) { + found= 1; + break; + } + } + } + else { + /* find gp-frame which is less than or equal to cframe */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->framenum <= cframe) { + found= 1; + break; + } + } + } + + /* set the appropriate frame */ + if (addnew) { + if ((found) && (gpf->framenum == cframe)) + gpl->actframe= gpf; + else + gpl->actframe= gpencil_frame_addnew(gpl, cframe); + } + else if (found) + gpl->actframe= gpf; + else { + /* unresolved errogenous situation! */ + printf("Error: cannot find appropriate gp-frame \n"); + } + } + else { + /* currently no frames (add if allowed to) */ + if (addnew) + gpl->actframe= gpencil_frame_addnew(gpl, cframe); + else { + /* don't do anything... this may be when no frames yet! */ + } + } + + /* return */ + return gpl->actframe; +} + +/* delete the given frame from a layer */ +void gpencil_layer_delframe (bGPDlayer *gpl, bGPDframe *gpf) +{ + /* error checking */ + if (ELEM(NULL, gpl, gpf)) + return; + + /* free the frame and its data */ + free_gpencil_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + gpl->actframe = NULL; +} + +/* get the active gp-layer for editing */ +bGPDlayer *gpencil_layer_getactive (bGPdata *gpd) +{ + bGPDlayer *gpl; + + /* error checking */ + if (ELEM(NULL, gpd, gpd->layers.first)) + return NULL; + + /* loop over layers until found (assume only one active) */ + for (gpl=gpd->layers.first; gpl; gpl=gpl->next) { + if (gpl->flag & GP_LAYER_ACTIVE) + return gpl; + } + + /* no active layer found */ + return NULL; +} + +/* set the active gp-layer */ +void gpencil_layer_setactive (bGPdata *gpd, bGPDlayer *active) +{ + bGPDlayer *gpl; + + /* error checking */ + if (ELEM3(NULL, gpd, gpd->layers.first, active)) + return; + + /* loop over layers deactivating all */ + for (gpl=gpd->layers.first; gpl; gpl=gpl->next) + gpl->flag &= ~GP_LAYER_ACTIVE; + + /* set as active one */ + active->flag |= GP_LAYER_ACTIVE; +} + +/* delete the active gp-layer */ +void gpencil_layer_delactive (bGPdata *gpd) +{ + bGPDlayer *gpl= gpencil_layer_getactive(gpd); + + /* error checking */ + if (ELEM(NULL, gpd, gpl)) + return; + + /* free layer */ + free_gpencil_frames(gpl); + BLI_freelinkN(&gpd->layers, gpl); + +} + +/* ************************************************** */ +/* GREASE-PENCIL EDITING MODE - Tools */ + +/* --------- Data Deletion ---------- */ + +/* delete the last stroke on the active layer */ +void gpencil_delete_laststroke (bGPdata *gpd) +{ + bGPDlayer *gpl= gpencil_layer_getactive(gpd); + bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0); + + gpencil_frame_delete_laststroke(gpf); +} + +/* delete the active frame */ +void gpencil_delete_actframe (bGPdata *gpd) +{ + bGPDlayer *gpl= gpencil_layer_getactive(gpd); + bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0); + + gpencil_layer_delframe(gpl, gpf); +} + + + +/* delete various grase-pencil elements + * mode: 1 - last stroke + * 2 - active frame + * 3 - active layer + */ +void gpencil_delete_operation (short mode) // unused +{ + bGPdata *gpd; + + /* get datablock to work on */ + gpd= gpencil_data_getactive(NULL); + if (gpd == NULL) return; + + switch (mode) { + case 1: /* last stroke */ + gpencil_delete_laststroke(gpd); + break; + case 2: /* active frame */ + gpencil_delete_actframe(gpd); + break; + case 3: /* active layer */ + gpencil_layer_delactive(gpd); + break; + } + + /* redraw and undo-push */ + BIF_undo_push("GPencil Delete"); + allqueue(REDRAWVIEW3D, 0); +} + +/* display a menu for deleting different grease-pencil elements */ +void gpencil_delete_menu (void) // unused +{ + short mode; + + mode= pupmenu("Erase...%t|Last Stroke%x1|Active Frame%x2|Active Layer%x3"); + if (mode <= 0) return; + + gpencil_delete_operation(mode); +} + +/* ************************************************** */ +/* GREASE-PENCIL EDITING MODE - Painting */ + +/* ---------- 'Globals' and Defines ----------------- */ + +/* maximum sizes of gp-session buffer */ +#define GP_STROKE_BUFFER_MAX 500 + +/* ------ */ + +/* Temporary 'Stroke' Operation data */ +typedef struct tGPsdata { + ScrArea *sa; /* area where painting originated */ + View2D *v2d; /* needed for GP_STROKE_2DSPACE */ + + bGPdata *gpd; /* gp-datablock layer comes from */ + bGPDlayer *gpl; /* layer we're working on */ + bGPDframe *gpf; /* frame we're working on */ + + short status; /* current status of painting */ + short paintmode; /* mode for painting (L_MOUSE or R_MOUSE for now) */ +} tGPsdata; + +/* values for tGPsdata->status */ +enum { + GP_STATUS_NORMAL = 0, /* running normally */ + GP_STATUS_ERROR, /* something wasn't correctly set up */ + GP_STATUS_DONE /* painting done */ +}; + +/* Return flags for adding points to stroke buffer */ +enum { + GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ + GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ + GP_STROKEADD_NORMAL, /* point was successfully added */ + GP_STROKEADD_FULL /* cannot add any more points to buffer */ +}; + +/* ---------- Stroke Editing ------------ */ + +/* clear the session buffers (call this before AND after a paint operation) */ +static void gp_session_validatebuffer (tGPsdata *p) +{ + bGPdata *gpd= p->gpd; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->sbuffer) + memset(gpd->sbuffer, 0, sizeof(bGPDspoint)*GP_STROKE_BUFFER_MAX); + else + gpd->sbuffer= MEM_callocN(sizeof(bGPDspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + + /* reset indices */ + gpd->sbuffer_size = 0; + + /* reset flags */ + gpd->sbuffer_sflag= 0; +} + +/* init new painting session */ +static void gp_session_initpaint (tGPsdata *p) +{ + /* clear previous data (note: is on stack) */ + memset(p, 0, sizeof(tGPsdata)); + + /* make sure the active view (at the starting time) is a 3d-view */ + if (curarea == NULL) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: No active view for painting \n"); + return; + } + switch (curarea->spacetype) { + /* supported views first */ + case SPACE_VIEW3D: + { + View3D *v3d= curarea->spacedata.first; + + /* set current area */ + p->sa= curarea; + + /* check that gpencil data is allowed to be drawn */ + if ((v3d->flag2 & V3D_DISPGP)==0) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: In active view, Grease Pencil not shown \n"); + return; + } + } + break; + case SPACE_NODE: + { + SpaceNode *snode= curarea->spacedata.first; + + /* set current area */ + p->sa= curarea; + p->v2d= &snode->v2d; + + /* check that gpencil data is allowed to be drawn */ + if ((snode->flag & SNODE_DISPGP)==0) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: In active view, Grease Pencil not shown \n"); + return; + } + } + break; + case SPACE_SEQ: + { + SpaceSeq *sseq= curarea->spacedata.first; + + /* set current area */ + p->sa= curarea; + p->v2d= &sseq->v2d; + + /* check that gpencil data is allowed to be drawn */ + if (sseq->mainb == 0) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n"); + return; + } + if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: In active view, Grease Pencil not shown \n"); + return; + } + } + break; + /* unsupported views */ + default: + { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: Active view not appropriate for Grease Pencil drawing \n"); + return; + } + break; + } + + /* get gp-data */ + p->gpd= gpencil_data_getactive(p->sa); + if (p->gpd == NULL) { + short ok; + + p->gpd= gpencil_data_addnew(); + ok= gpencil_data_setactive(p->sa, p->gpd); + + /* most of the time, the following check isn't needed */ + if (ok == 0) { + /* free gpencil data as it can't be used */ + free_gpencil_data(p->gpd); + p->gpd= NULL; + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: Could not assign newly created Grease Pencil data to active area \n"); + return; + } + } + + /* set edit flags */ + G.f |= G_GREASEPENCIL; + + /* clear out buffer (stored in gp-data) in case something contaminated it */ + gp_session_validatebuffer(p); +} + +/* cleanup after a painting session */ +static void gp_session_cleanup (tGPsdata *p) +{ + bGPdata *gpd= p->gpd; + + /* error checking */ + if (gpd == NULL) + return; + + /* free stroke buffer */ + if (gpd->sbuffer) { + MEM_freeN(gpd->sbuffer); + gpd->sbuffer= NULL; + } + + /* clear flags */ + gpd->sbuffer_size= 0; + gpd->sbuffer_sflag= 0; +} + +/* convert screen-coordinates to buffer-coordinates */ +static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[]) +{ + bGPdata *gpd= p->gpd; + + /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ + if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { + short mx=mval[0], my=mval[1]; + float *fp= give_cursor(); + float dvec[3]; + + /* method taken from editview.c - mouse_cursor() */ + project_short_noclip(fp, mval); + window_to_3d(dvec, mval[0]-mx, mval[1]-my); + VecSubf(out, fp, dvec); + } + + /* 2d - on 'canvas' (assume that p->v2d is set) */ + else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { + float x, y; + + areamouseco_to_ipoco(p->v2d, mval, &x, &y); + + out[0]= x; + out[1]= y; + } + + /* 2d - relative to screen (viewport area) */ + else { + out[0] = (float)(mval[0]) / (float)(p->sa->winx) * 1000; + out[1] = (float)(mval[1]) / (float)(p->sa->winy) * 1000; + } +} + +/* add current stroke-point to buffer (returns whether point was successfully added) */ +static short gp_stroke_addpoint (tGPsdata *p, short mval[], float pressure) +{ + bGPdata *gpd= p->gpd; + bGPDspoint *pt; + + /* check if still room in buffer */ + if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_OVERFLOW; + + + /* get pointer to destination point */ + pt= gpd->sbuffer + gpd->sbuffer_size; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, mval, &pt->x); + + /* store other settings */ + pt->pressure= pressure; + + /* increment counters */ + gpd->sbuffer_size++; + + /* check if another operation can still occur */ + if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_FULL; + else + return GP_STROKEADD_NORMAL; +} + +/* smooth a stroke (in buffer) before storing it */ +static void gp_stroke_smooth (tGPsdata *p) +{ + bGPdata *gpd= p->gpd; + int i=0, cmx=gpd->sbuffer_size; + + /* don't try if less than 2 points in buffer */ + if ((cmx <= 2) || (gpd->sbuffer == NULL)) + return; + + /* apply weighting-average (note doing this along path sequentially does introduce slight error) */ + for (i=0; i < gpd->sbuffer_size; i++) { + bGPDspoint *pc= (gpd->sbuffer + i); + bGPDspoint *pb= (i-1 > 0)?(pc-1):(pc); + bGPDspoint *pa= (i-2 > 0)?(pc-2):(pb); + bGPDspoint *pd= (i+1 < cmx)?(pc+1):(pc); + bGPDspoint *pe= (i+2 < cmx)?(pc+2):(pd); + + pc->x= (0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x); + pc->y= (0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y); + } +} + +/* make a new stroke from the buffer data */ +static void gp_stroke_newfrombuffer (tGPsdata *p) +{ + bGPdata *gpd= p->gpd; + bGPDstroke *gps; + bGPDspoint *pt, *ptc; + int i, totelem; + + /* get total number of points to allocate space for */ + totelem = gpd->sbuffer_size; + + /* exit with error if no valid points from this stroke */ + if (totelem == 0) { + if (G.f & G_DEBUG) + printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size); + return; + } + + /* allocate memory for a new stroke */ + gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + + /* allocate enough memory for a continuous array for storage points */ + pt= gps->points= MEM_callocN(sizeof(bGPDspoint)*totelem, "gp_stroke_points"); + + /* copy appropriate settings for stroke */ + gps->totpoints= totelem; + gps->thickness= p->gpl->thickness; + gps->flag= gpd->sbuffer_sflag; + + /* copy points from the buffer to the stroke */ + for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++) { + memcpy(pt, ptc, sizeof(bGPDspoint)); + pt++; + } + + /* add stroke to frame */ + BLI_addtail(&p->gpf->strokes, gps); +} + +/* ---------- 'Paint' Tool ------------ */ + +/* init new stroke */ +static void gp_paint_initstroke (tGPsdata *p) +{ + /* get active layer (or add a new one if non-existent) */ + p->gpl= gpencil_layer_getactive(p->gpd); + if (p->gpl == NULL) + p->gpl= gpencil_layer_addnew(p->gpd); + if (p->gpl->flag & GP_LAYER_LOCKED) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: Cannot paint on locked layer \n"); + return; + } + + /* get active frame (add a new one if not matching frame) */ + p->gpf= gpencil_layer_getframe(p->gpl, CFRA, 1); + if (p->gpf == NULL) { + p->status= GP_STATUS_ERROR; + if (G.f & G_DEBUG) + printf("Error: No frame created (gpencil_paint_init) \n"); + return; + } + else + p->gpf->flag |= GP_FRAME_PAINT; + + /* check if points will need to be made in 3d-space */ + if (p->gpd->flag & GP_DATA_VIEWALIGN) { + switch (p->sa->spacetype) { + case SPACE_VIEW3D: + { + float *fp= give_cursor(); + initgrabz(fp[0], fp[1], fp[2]); + + p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE; + } + break; + case SPACE_NODE: + { + p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; + } + break; + case SPACE_SEQ: + { + /* for now, this is not applicable here... */ + } + break; + } + } +} + +/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ +static void gp_paint_strokeend (tGPsdata *p) +{ + /* sanitize stroke-points in buffer */ + gp_stroke_smooth(p); + + /* transfer stroke to frame */ + gp_stroke_newfrombuffer(p); + + /* clean up buffer now */ + gp_session_validatebuffer(p); +} + +/* finish off stroke painting operation */ +static void gp_paint_cleanup (tGPsdata *p) +{ + /* finish off a stroke */ + gp_paint_strokeend(p); + + /* "unlock" frame */ + p->gpf->flag &= ~GP_FRAME_PAINT; + + /* add undo-push so stroke can be undone */ + /* FIXME: currently disabled, as it's impossible to get this working nice + * as gpenci data is on currently screen-level (which isn't saved to undo files) + */ + //BIF_undo_push("GPencil Stroke"); + + /* force redraw after drawing action */ + force_draw(0); +} + +/* -------- */ + +/* main call to paint a new stroke */ +short gpencil_paint (short mousebutton) +{ + tGPsdata p; + short prevmval[2], mval[2]; + float opressure, pressure; + short ok = GP_STROKEADD_NORMAL; + + /* init paint-data */ + gp_session_initpaint(&p); + if (p.status == GP_STATUS_ERROR) { + gp_session_cleanup(&p); + return 0; + } + gp_paint_initstroke(&p); + if (p.status == GP_STATUS_ERROR) { + gp_session_cleanup(&p); + return 0; + } + + /* set cursor to indicate drawing */ + setcursor_space(p.sa->spacetype, CURSOR_VPAINT); + + /* init drawing-device settings */ + getmouseco_areawin(mval); + pressure = get_pressure(); + + prevmval[0]= mval[0]; + prevmval[1]= mval[1]; + opressure= pressure; + + /* only allow painting of single 'dots' if: + * - pressure is not excessive (as it can be on some windows tablets) + * - draw-mode for active datablock is turned on + */ + if (!(pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { + gp_stroke_addpoint(&p, mval, pressure); + } + + /* paint loop */ + do { + /* get current user input */ + getmouseco_areawin(mval); + pressure = get_pressure(); + + /* only add current point to buffer if mouse moved (otherwise wait until it does) */ + if ((mval[0] != prevmval[0]) || (mval[1] != prevmval[1])) { + /* try to add point */ + ok= gp_stroke_addpoint(&p, mval, pressure); + + /* handle errors while adding point */ + if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { + /* finish off old stroke */ + gp_paint_strokeend(&p); + + /* start a new stroke, starting from previous point */ + gp_stroke_addpoint(&p, prevmval, opressure); + ok= gp_stroke_addpoint(&p, mval, pressure); + } + else if (ok == GP_STROKEADD_INVALID) { + /* the painting operation cannot continue... */ + error("Cannot paint stroke"); + p.status = GP_STATUS_ERROR; + + if (G.f & G_DEBUG) + printf("Error: Grease-Pencil Paint - Add Point Invalid \n"); + break; + } + force_draw(0); + + prevmval[0]= mval[0]; + prevmval[1]= mval[1]; + opressure= pressure; + } + else + BIF_wait_for_statechange(); + + /* do mouse checking at the end, so don't check twice, and potentially + * miss a short tap + */ + } while (get_mbut() & mousebutton); + + /* clear edit flags */ + G.f &= ~G_GREASEPENCIL; + + /* restore cursor to indicate end of drawing */ + setcursor_space(p.sa->spacetype, CURSOR_STD); + + /* check size of buffer before cleanup, to determine if anything happened here */ + ok= p.gpd->sbuffer_size; + + /* cleanup */ + gp_paint_cleanup(&p); + gp_session_cleanup(&p); + + /* done! return if a stroke was successfully added */ + return ok; +} + + +/* All event (loops) handling checking if stroke drawing should be initiated + * should call this function. + */ +short gpencil_do_paint (ScrArea *sa) +{ + bGPdata *gpd = gpencil_data_getactive(sa); + short mousebutton = L_MOUSE; /* for now, this is always on L_MOUSE*/ + short retval= 0; + + /* check if possible to do painting */ + if (gpd == NULL) + return 0; + + /* currently, we will only paint if: + * 1. draw-mode on gpd is set (for accessibility reasons) + * (single 'dots' are only available via this method) + * 2. if shift-modifier is held + lmb -> 'quick paint' + */ + if (gpd->flag & GP_DATA_EDITPAINT) { + /* try to paint */ + retval = gpencil_paint(mousebutton); + } + else if (G.qual == LR_SHIFTKEY) { + /* try to paint */ + retval = gpencil_paint(mousebutton); + } + + /* return result of trying to paint */ + return retval; +} + +/* ************************************************** */ diff --git a/source/blender/src/header_action.c b/source/blender/src/header_action.c index 9c7046c5111..50d343ca470 100644 --- a/source/blender/src/header_action.c +++ b/source/blender/src/header_action.c @@ -50,8 +50,11 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" #include "BIF_editaction.h" #include "BIF_interface.h" +#include "BIF_language.h" #include "BIF_poseobject.h" #include "BIF_resources.h" #include "BIF_screen.h" @@ -76,6 +79,7 @@ #include "blendef.h" #include "mydevice.h" +/* ------------------------------- */ /* enums declaring constants that are used as menu event codes */ enum { @@ -212,6 +216,16 @@ enum { ACTMENU_MARKERS_LOCALMOVE }; +/* ------------------------------- */ +/* macros for easier state testing (only for use here) */ + +/* test if active action editor is showing any markers */ +#define G_SACTION_HASMARKERS \ + ((G.saction->action && G.saction->action->markers.first) \ + || (G.scene->markers.first)) + +/* ------------------------------- */ + void do_action_buttons(unsigned short event) { Object *ob= OBACT; @@ -398,32 +412,41 @@ static uiBlock *action_viewmenu(void *arg_unused) uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_SLIDERS)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Show Sliders|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_SLIDERS, ""); - - uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOHIDE)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Show Hidden Channels|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_NOHIDE, ""); - - uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NODRAWGCOLORS)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "Use Group Colors|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_GCOLORS, ""); - - // this option may get removed in future - uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, - "Cull Out-of-View Keys (Time)|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_HORIZOPTIMISE, ""); + if (G.saction->mode == SACTCONT_GPENCIL) { + // this option may get removed in future + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, + "Cull Out-of-View Keys (Time)|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_HORIZOPTIMISE, ""); + } + else { + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_SLIDERS)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, + "Show Sliders|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_SLIDERS, ""); + + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOHIDE)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, + "Show Hidden Channels|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_NOHIDE, ""); + + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NODRAWGCOLORS)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, + "Use Group Colors|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_GCOLORS, ""); + + // this option may get removed in future + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_HORIZOPTIMISEON)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, + "Cull Out-of-View Keys (Time)|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_HORIZOPTIMISE, ""); + + uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, + "AutoMerge Keyframes|", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, + ACTMENU_VIEW_TRANSDELDUPS, ""); + } - uiDefIconTextBut(block, BUTM, 1, (G.saction->flag & SACTION_NOTRANSKEYCULL)?ICON_CHECKBOX_DEHLT:ICON_CHECKBOX_HLT, - "AutoMerge Keyframes|", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, - ACTMENU_VIEW_TRANSDELDUPS, ""); - uiDefIconTextBut(block, BUTM, 1, (G.v2d->flag & V2D_VIEWLOCK)?ICON_CHECKBOX_HLT:ICON_CHECKBOX_DEHLT, "Lock Time to Other Windows|", 0, yco-=20, @@ -476,7 +499,7 @@ static uiBlock *action_viewmenu(void *arg_unused) menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_VIEW_PREVRANGECLEAR, ""); - if (G.saction->action) { + if ((G.saction->mode == SACTCONT_ACTION) && (G.saction->action)) { uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Preview Range from Action Length|Ctrl Alt P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, @@ -550,13 +573,15 @@ static uiBlock *action_selectmenu_columnmenu(void *arg_unused) uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "On Current Frame|Ctrl K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_SEL_COLUMN_CFRA, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "On Selected Markers|Shift K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_MARKERSCOLUMN, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Between Selected Markers|Alt K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_COLUMN_MARKERSBETWEEN, ""); + if (G_SACTION_HASMARKERS) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "On Selected Markers|Shift K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_COLUMN_MARKERSCOLUMN, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Between Selected Markers|Alt K", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_COLUMN_MARKERSBETWEEN, ""); + } uiBlockSetDirection(block, UI_RIGHT); uiTextBoundsBlock(block, 60); @@ -659,14 +684,18 @@ static uiBlock *action_selectmenu(void *arg_unused) "Border Select Keys|B", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_SEL_BORDER, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Border Select Channels|B", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_BORDERC, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Border Select Markers|Ctrl B", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_BORDERM, ""); + if (G_SACTION_HASMARKERS) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Border Select Markers|Ctrl B", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_BORDERM, ""); + } + if (G.saction->mode != SACTCONT_SHAPEKEY) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Border Select Channels|B", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_BORDERC, ""); + } uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -675,14 +704,18 @@ static uiBlock *action_selectmenu(void *arg_unused) "Select/Deselect All Keys|A", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_SEL_ALL_KEYS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Select/Deselect All Markers|Ctrl A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_ALL_MARKERS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Select/Deselect All Channels|A", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_ALL_CHAN, ""); + if (G_SACTION_HASMARKERS) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Select/Deselect All Markers|Ctrl A", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_ALL_MARKERS, ""); + } + if (G.saction->mode != SACTCONT_SHAPEKEY) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Select/Deselect All Channels|A", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_ALL_CHAN, ""); + } uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -691,14 +724,18 @@ static uiBlock *action_selectmenu(void *arg_unused) "Inverse Keys|Ctrl I", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_SEL_INVERSE_KEYS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Inverse Markers|Ctrl Shift I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_INVERSE_MARKERS, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, - "Inverse All Channels|Ctrl I", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 0, - ACTMENU_SEL_INVERSE_CHANNELS, ""); + if (G_SACTION_HASMARKERS) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Inverse Markers|Ctrl Shift I", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_INVERSE_MARKERS, ""); + } + if (G.saction->mode != SACTCONT_SHAPEKEY) { + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Inverse All Channels|Ctrl I", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_SEL_INVERSE_CHANNELS, ""); + } uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); @@ -971,6 +1008,40 @@ static uiBlock *action_channelmenu(void *arg_unused) return block; } +/* note: uses do_action_channelmenu too... */ +static uiBlock *action_gplayermenu(void *arg_unused) +{ + uiBlock *block; + short yco= 0, menuwidth=120; + + block= uiNewBlock(&curarea->uiblocks, "action_gplayermenu", + UI_EMBOSSP, UI_HELV, curarea->headwin); + uiBlockSetButmFunc(block, do_action_channelmenu, NULL); + + uiDefIconTextBlockBut(block, action_channelmenu_settingsmenu, + NULL, ICON_RIGHTARROW_THIN, + "Settings", 0, yco-=20, 120, 20, ""); + + uiDefBut(block, SEPR, 0, "", 0, yco-=6, + menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Delete|X", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, ACTMENU_CHANNELS_DELETE, ""); + + if (curarea->headertype==HEADERTOP) { + uiBlockSetDirection(block, UI_DOWN); + } + else { + uiBlockSetDirection(block, UI_TOP); + uiBlockFlipOrder(block); + } + + uiTextBoundsBlock(block, 50); + + return block; +} + static void do_action_keymenu_transformmenu(void *arg, int event) { switch (event) @@ -1400,6 +1471,51 @@ static uiBlock *action_keymenu(void *arg_unused) return block; } +/* note: uses do_action_keymenu too! */ +static uiBlock *action_framemenu(void *arg_unused) +{ + uiBlock *block; + short yco= 0, menuwidth=120; + + block= uiNewBlock(&curarea->uiblocks, "action_framemenu", + UI_EMBOSSP, UI_HELV, curarea->headwin); + uiBlockSetButmFunc(block, do_action_keymenu, NULL); + + uiDefIconTextBlockBut(block, action_keymenu_transformmenu, + NULL, ICON_RIGHTARROW_THIN, "Transform", 0, yco-=20, 120, 20, ""); + + uiDefIconTextBlockBut(block, action_keymenu_snapmenu, + NULL, ICON_RIGHTARROW_THIN, "Snap", 0, yco-=20, 120, 20, ""); + + uiDefIconTextBlockBut(block, action_keymenu_mirrormenu, + NULL, ICON_RIGHTARROW_THIN, "Mirror", 0, yco-=20, 120, 20, ""); + + uiDefBut(block, SEPR, 0, "", 0, yco-=6, + menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Duplicate|Shift D", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_KEY_DUPLICATE, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, + "Delete|X", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 0, + ACTMENU_KEY_DELETE, ""); + + if(curarea->headertype==HEADERTOP) { + uiBlockSetDirection(block, UI_DOWN); + } + else { + uiBlockSetDirection(block, UI_TOP); + uiBlockFlipOrder(block); + } + + uiTextBoundsBlock(block, 50); + + return block; +} + static void do_action_markermenu(void *arg, int event) { switch(event) @@ -1461,17 +1577,19 @@ static uiBlock *action_markermenu(void *arg_unused) menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_NAME, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Marker|Ctrl G", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_MOVE, ""); - - uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); - - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Pose Marker|Shift L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALADD, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose Marker|Ctrl Shift L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALRENAME, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Pose Marker|Alt L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALDELETE, ""); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Pose Marker|Ctrl L", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALMOVE, ""); + + if (G.saction->mode == SACTCONT_ACTION) { + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Add Pose Marker|Shift L", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALADD, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Rename Pose Marker|Ctrl Shift L", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALRENAME, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Delete Pose Marker|Alt L", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALDELETE, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Grab/Move Pose Marker|Ctrl L", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, ACTMENU_MARKERS_LOCALMOVE, ""); + } if(curarea->headertype==HEADERTOP) { uiBlockSetDirection(block, UI_DOWN); @@ -1498,6 +1616,7 @@ void action_buttons(void) return; /* copied from drawactionspace.... */ + // FIXME: do for gpencil too? if (!G.saction->pin) { if (OBACT) G.saction->action = OBACT->action; @@ -1558,68 +1677,112 @@ void action_buttons(void) "Select", xco, -2, xmax-3, 24, ""); xco+= xmax; - if (G.saction->action) { + if ((G.saction->action) && (G.saction->mode==SACTCONT_ACTION)) { xmax= GetButStringLength("Channel"); uiDefPulldownBut(block, action_channelmenu, NULL, "Channel", xco, -2, xmax-3, 24, ""); xco+= xmax; } + else if ((G.saction->gpd) && (G.saction->mode==SACTCONT_GPENCIL)) { + xmax= GetButStringLength("Channel"); + uiDefPulldownBut(block, action_gplayermenu, NULL, + "Channel", xco, -2, xmax-3, 24, ""); + xco+= xmax; + } xmax= GetButStringLength("Marker"); uiDefPulldownBut(block, action_markermenu, NULL, "Marker", xco, -2, xmax-3, 24, ""); xco+= xmax; - xmax= GetButStringLength("Key"); - uiDefPulldownBut(block, action_keymenu, NULL, - "Key", xco, -2, xmax-3, 24, ""); - xco+= xmax; + if (G.saction->mode == SACTCONT_GPENCIL) { + xmax= GetButStringLength("Frame"); + uiDefPulldownBut(block, action_framemenu, NULL, + "Frame", xco, -2, xmax-3, 24, ""); + xco+= xmax; + + } + else { + xmax= GetButStringLength("Key"); + uiDefPulldownBut(block, action_keymenu, NULL, + "Key", xco, -2, xmax-3, 24, ""); + xco+= xmax; + } } uiBlockSetEmboss(block, UI_EMBOSS); - /* NAME ETC */ - ob= OBACT; - from = (ID *)ob; - - xco= std_libbuttons(block, xco, 0, B_ACTPIN, &G.saction->pin, - B_ACTIONBROWSE, ID_AC, 0, (ID*)G.saction->action, - from, &(G.saction->actnr), B_ACTALONE, - B_ACTLOCAL, B_ACTIONDELETE, 0, B_KEEPDATA); + /* MODE SELECTOR */ + uiDefButC(block, MENU, B_REDR, + "Editor Mode %t|Action Editor %x0|ShapeKey Editor %x1|Grease Pencil %x2", + xco,0,90,YIC, &(G.saction->mode), 0, 1, 0, 0, + "Editing modes for this editor"); - uiClearButLock(); - - xco += 8; - /* COPY PASTE */ - uiBlockBeginAlign(block); - if (curarea->headertype==HEADERTOP) { - uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); - } - else { - uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); - uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); - } - uiBlockEndAlign(block); - xco += (XIC + 8); + xco += (90 + 8); - /* draw AUTOSNAP */ - if (G.saction->flag & SACTION_DRAWTIME) { - uiDefButS(block, MENU, B_REDR, - "Auto-Snap Keyframes %t|No Snap %x0|Second Step %x1|Nearest Second %x2|Nearest Marker %x3", - xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0, - "Auto-snapping mode for keyframes when transforming"); + /* MODE-DEPENDENT DRAWING */ + if (G.saction->mode == SACTCONT_GPENCIL) { + char gp_name[64]; + + /* pin button */ + uiDefIconButS(block, ICONTOG, B_ACTPIN, ICON_PIN_DEHLT, xco,0,XIC,YIC, &G.saction->pin, 0, 0, 0, 0, "Keeps this view displaying the current data regardless of what Grease Pencil set is active"); + xco += (XIC + 5); + + /* just a simple string to help identify if any content */ + glRasterPos2f((float)xco, 5.0); + BIF_RasterPos((float)xco, 5.0); // stupid texture fonts + BIF_ThemeColor(TH_TEXT); + + sprintf(gp_name, (G.saction->gpd)?"Grease Pencil Data":""); + xmax= GetButStringLength(gp_name); + BIF_DrawString(uiBlockGetCurFont(block), gp_name, (U.transopts & USER_TR_BUTTONS)); + xco += xmax; } else { - uiDefButS(block, MENU, B_REDR, - "Auto-Snap Keyframes %t|No Snap %x0|Frame Step %x1|Nearest Frame %x2|Nearest Marker %x3", - xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0, - "Auto-snapping mode for keyframes when transforming"); + /* NAME ETC */ + ob= OBACT; + from = (ID *)ob; + + xco= std_libbuttons(block, xco, 0, B_ACTPIN, &G.saction->pin, + B_ACTIONBROWSE, ID_AC, 0, (ID*)G.saction->action, + from, &(G.saction->actnr), B_ACTALONE, + B_ACTLOCAL, B_ACTIONDELETE, 0, B_KEEPDATA); + + uiClearButLock(); + + xco += 8; + + /* COPY PASTE */ + uiBlockBeginAlign(block); + if (curarea->headertype==HEADERTOP) { + uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYUP, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); + uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEUP, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); + } + else { + uiDefIconBut(block, BUT, B_ACTCOPYKEYS, ICON_COPYDOWN, xco,0,XIC,YIC, 0, 0, 0, 0, 0, "Copies the selected keyframes from the selected channel(s) to the buffer"); + uiDefIconBut(block, BUT, B_ACTPASTEKEYS, ICON_PASTEDOWN, xco+=XIC,0,XIC,YIC, 0, 0, 0, 0, 0, "Pastes the keyframes from the buffer"); + } + uiBlockEndAlign(block); + xco += (XIC + 8); + + /* draw AUTOSNAP */ + if (G.saction->flag & SACTION_DRAWTIME) { + uiDefButC(block, MENU, B_REDR, + "Auto-Snap Keyframes %t|No Snap %x0|Second Step %x1|Nearest Second %x2|Nearest Marker %x3", + xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0, + "Auto-snapping mode for keyframes when transforming"); + } + else { + uiDefButC(block, MENU, B_REDR, + "Auto-Snap Keyframes %t|No Snap %x0|Frame Step %x1|Nearest Frame %x2|Nearest Marker %x3", + xco,0,70,YIC, &(G.saction->autosnap), 0, 1, 0, 0, + "Auto-snapping mode for keyframes when transforming"); + } + + xco += (70 + 8); } - xco += (70 + 8); - /* draw LOCK */ uiDefIconButS(block, ICONTOG, 1, ICON_UNLOCKED, xco, 0, XIC, YIC, &(G.saction->lock), 0, 0, 0, 0, diff --git a/source/blender/src/header_node.c b/source/blender/src/header_node.c index ec6bbc9044c..4c7b4aa80bc 100644 --- a/source/blender/src/header_node.c +++ b/source/blender/src/header_node.c @@ -110,12 +110,16 @@ static void do_node_viewmenu(void *arg, int event) case 3: /* View all */ snode_home(curarea, snode); break; + case 4: /* Grease Pencil */ + add_blockhandler(curarea, NODES_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW); + break; } allqueue(REDRAWNODE, 0); } static uiBlock *node_viewmenu(void *arg_unused) { + SpaceNode *snode= curarea->spacedata.first; uiBlock *block; short yco= 0, menuwidth=120; @@ -123,6 +127,12 @@ static uiBlock *node_viewmenu(void *arg_unused) UI_EMBOSSP, UI_HELV, curarea->headwin); uiBlockSetButmFunc(block, do_node_viewmenu, NULL); + if (snode->nodetree) { + uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Grease Pencil...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 4, ""); + + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + } + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Zoom In|NumPad +", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, ""); uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Zoom Out|NumPad -", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 2, ""); diff --git a/source/blender/src/header_seq.c b/source/blender/src/header_seq.c index 393830a61cf..e5a63b9fe45 100644 --- a/source/blender/src/header_seq.c +++ b/source/blender/src/header_seq.c @@ -100,6 +100,9 @@ static void do_seq_viewmenu(void *arg, int event) case 6: /* Draw time/frames */ sseq->flag ^= SEQ_DRAWFRAMES; break; + case 7: /* Grease Pencil */ + add_blockhandler(curarea, SEQ_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW); + break; } } @@ -111,7 +114,15 @@ static uiBlock *seq_viewmenu(void *arg_unused) block= uiNewBlock(&curarea->uiblocks, "seq_viewmenu", UI_EMBOSSP, UI_HELV, curarea->headwin); uiBlockSetButmFunc(block, do_seq_viewmenu, NULL); - + + if (sseq->mainb) { + uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, + "Grease Pencil...", 0, yco-=20, + menuwidth, 19, NULL, 0.0, 0.0, 1, 7, ""); + + uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); + } + if (sseq->mainb == 0) { uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Play Back Animation " diff --git a/source/blender/src/header_view3d.c b/source/blender/src/header_view3d.c index 0f3a46c8a8c..71bf0cd9bd4 100644 --- a/source/blender/src/header_view3d.c +++ b/source/blender/src/header_view3d.c @@ -601,6 +601,9 @@ static void do_view3d_viewmenu(void *arg, int event) break; case 20: /* Transform Space Panel */ add_blockhandler(curarea, VIEW3D_HANDLER_TRANSFORM, UI_PNL_UNSTOW); + break; + case 21: /* Grease Pencil */ + add_blockhandler(curarea, VIEW3D_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW); break; } allqueue(REDRAWVIEW3D, 1); @@ -619,6 +622,7 @@ static uiBlock *view3d_viewmenu(void *arg_unused) uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Render Preview...|Shift P", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 18, ""); uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "View Properties...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 16, ""); uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Background Image...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 15, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_MENU_PANEL, "Grease Pencil...", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 21, ""); uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, ""); diff --git a/source/blender/src/interface.c b/source/blender/src/interface.c index 4fbf92d646e..ba8649eaa86 100644 --- a/source/blender/src/interface.c +++ b/source/blender/src/interface.c @@ -618,6 +618,9 @@ void uiBoundsBlock(uiBlock *block, int addval) uiBut *bt; int xof; + if(block==NULL) + return; + if(block->buttons.first==NULL) { if(block->panel) { block->minx= 0.0; block->maxx= block->panel->sizex; @@ -5405,7 +5408,9 @@ uiBlock *uiNewBlock(ListBase *lb, char *name, short dt, short font, short win) int getsizex, getsizey; bwin_getsize(win, &getsizex, &getsizey); - block->aspect= 2.0/( (getsizex)*block->winmat[0][0]); + /* TODO - investigate why block->winmat[0][0] is negative + * in the image view when viewRedrawForce is called */ + block->aspect= 2.0/fabs( (getsizex)*block->winmat[0][0]); } uiSetCurFont(block, font); diff --git a/source/blender/src/space.c b/source/blender/src/space.c index 58420604c83..92efb477095 100644 --- a/source/blender/src/space.c +++ b/source/blender/src/space.c @@ -56,6 +56,7 @@ #include "DNA_armature_types.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" /* used for select_same_group */ +#include "DNA_gpencil_types.h" #include "DNA_image_types.h" #include "DNA_ipo_types.h" #include "DNA_mesh_types.h" @@ -159,6 +160,7 @@ #include "BDR_imagepaint.h" #include "BDR_sculptmode.h" #include "BDR_unwrapper.h" +#include "BDR_gpencil.h" #include "BLO_readfile.h" /* for BLO_blendhandle_close */ @@ -1193,13 +1195,17 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) if(event==UI_BUT_EVENT) do_butspace(val); /* temporal, view3d deserves own queue? */ - /* we consider manipulator a button, defaulting to leftmouse */ + /* - we consider manipulator a button, defaulting to leftmouse + * - grease-pencil also defaults to leftmouse + */ if(event==LEFTMOUSE) { /* run any view3d event handler script links */ - if (event && sa->scriptlink.totscript) + if (event && sa->scriptlink.totscript) { if (BPY_do_spacehandlers(sa, event, SPACEHANDLER_VIEW3D_EVENT)) return; /* return if event was processed (swallowed) by handler(s) */ - + } + + if(gpencil_do_paint(sa)) return; if(BIF_do_manipulator(sa)) return; } @@ -1207,7 +1213,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) if (U.flag & USER_LMOUSESELECT) { /* only swap mouse button for selection, in modes where it is relevant. * painting/sculpting stays on LEFTMOUSE */ - if ( !((G.f & G_SCULPTMODE) || (G.f & G_WEIGHTPAINT) || + if ( !((G.f & G_SCULPTMODE) || (G.f & G_WEIGHTPAINT) || (G.f & G_GREASEPENCIL) || (G.f & G_VERTEXPAINT) || (G.f & G_TEXTUREPAINT) || (G.f & G_PARTICLEEDIT)) || (G.obedit) ) { @@ -1945,7 +1951,7 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt) adduplicate(0, 0); } else if(G.qual==LR_CTRLKEY) { - imagestodisplist(); + imagestodisplist(); // removed } else if((G.qual==0)){ pupval= pupmenu("Draw mode%t|BoundBox %x1|Wire %x2|OpenGL Solid %x3|Shaded Solid %x4|Textured Solid %x5"); @@ -4816,6 +4822,11 @@ static void winqreadseqspace(ScrArea *sa, void *spacedata, BWinEvent *evt) if(val) { if( uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING ) event= 0; + /* grease-pencil defaults to leftmouse */ + if(event==LEFTMOUSE) { + if(gpencil_do_paint(sa)) return; + } + /* swap mouse buttons based on user preference */ if (U.flag & USER_LMOUSESELECT) { if (event == LEFTMOUSE) { @@ -4829,11 +4840,11 @@ static void winqreadseqspace(ScrArea *sa, void *spacedata, BWinEvent *evt) switch(event) { case LEFTMOUSE: - if(sseq->mainb || view2dmove(event)==0) { + if(sseq->mainb==0 && view2dmove(event)==0) { first= 1; set_special_seq_update(1); - + do { getmouseco_areawin(mval); areamouseco_to_ipoco(v2d, mval, &dx, &dy); @@ -6241,6 +6252,7 @@ void freespacelist(ScrArea *sa) if(vd->bgpic->ima) vd->bgpic->ima->id.us--; MEM_freeN(vd->bgpic); } + if(vd->gpd) free_gpencil_data(vd->gpd); if(vd->localvd) MEM_freeN(vd->localvd); if(vd->clipbb) MEM_freeN(vd->clipbb); if(vd->depths) { @@ -6284,7 +6296,12 @@ void freespacelist(ScrArea *sa) curvemapping_free(sima->cumap); } else if(sl->spacetype==SPACE_NODE) { -/* SpaceNode *snode= (SpaceNode *)sl; */ + SpaceNode *snode= (SpaceNode *)sl; + if(snode->gpd) free_gpencil_data(snode->gpd); + } + else if(sl->spacetype==SPACE_SEQ) { + SpaceSeq *sseq= (SpaceSeq *)sl; + if(sseq->gpd) free_gpencil_data(sseq->gpd); } } @@ -6314,6 +6331,7 @@ void duplicatespacelist(ScrArea *newarea, ListBase *lb1, ListBase *lb2) BIF_view3d_previewrender_free(v3d); v3d->depths= NULL; v3d->retopo_view_data= NULL; + v3d->gpd= gpencil_data_duplicate(v3d->gpd); } else if(sl->spacetype==SPACE_OOPS) { SpaceOops *so= (SpaceOops *)sl; @@ -6333,11 +6351,16 @@ void duplicatespacelist(ScrArea *newarea, ListBase *lb1, ListBase *lb2) else if(sl->spacetype==SPACE_NODE) { SpaceNode *snode= (SpaceNode *)sl; snode->nodetree= NULL; + snode->gpd= gpencil_data_duplicate(snode->gpd); } else if(sl->spacetype==SPACE_SCRIPT) { SpaceScript *sc = ( SpaceScript * ) sl; sc->but_refs = NULL; } + else if(sl->spacetype==SPACE_SEQ) { + SpaceSeq *sseq= (SpaceSeq *)sl; + sseq->gpd= gpencil_data_duplicate(sseq->gpd); + } sl= sl->next; } diff --git a/source/blender/src/transform_conversions.c b/source/blender/src/transform_conversions.c index 562d9a4934d..706b079432c 100644 --- a/source/blender/src/transform_conversions.c +++ b/source/blender/src/transform_conversions.c @@ -70,6 +70,7 @@ #include "DNA_vfont_types.h" #include "DNA_constraint_types.h" #include "DNA_listBase.h" +#include "DNA_gpencil_types.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -125,6 +126,7 @@ #include "BDR_drawaction.h" // list of keyframes in action #include "BDR_editobject.h" // reset_slowparents() +#include "BDR_gpencil.h" #include "BDR_unwrapper.h" #include "BLI_arithb.h" @@ -2474,6 +2476,96 @@ void flushTransIpoData(TransInfo *t) /* ********************* ACTION/NLA EDITOR ****************** */ +/* Called by special_aftertrans_update to make sure selected gp-frames replace + * any other gp-frames which may reside on that frame (that are not selected). + * It also makes sure gp-frames are still stored in chronological order after + * transform. + */ +static void posttrans_gpd_clean (bGPdata *gpd) +{ + bGPDlayer *gpl; + + for (gpl= gpd->layers.first; gpl; gpl= gpl->next) { + ListBase sel_buffer = {NULL, NULL}; + bGPDframe *gpf, *gpfn; + bGPDframe *gfs, *gfsn; + + /* loop 1: loop through and isolate selected gp-frames to buffer + * (these need to be sorted as they are isolated) + */ + for (gpf= gpl->frames.first; gpf; gpf= gpfn) { + gpfn= gpf->next; + + if (gpf->flag & GP_FRAME_SELECT) { + BLI_remlink(&gpl->frames, gpf); + + /* find place to add them in buffer + * - go backwards as most frames will still be in order, + * so doing it this way will be faster + */ + for (gfs= sel_buffer.last; gfs; gfs= gfs->prev) { + /* if current (gpf) occurs after this one in buffer, add! */ + if (gfs->framenum < gpf->framenum) { + BLI_insertlinkafter(&sel_buffer, gfs, gpf); + break; + } + } + if (gfs == NULL) + BLI_addhead(&sel_buffer, gpf); + } + } + + /* error checking: it is unlikely, but may be possible to have none selected */ + if (sel_buffer.first == NULL) + continue; + + /* if all were selected (i.e. gpl->frames is empty), then just transfer sel-buf over */ + if (gpl->frames.first == NULL) { + gpl->frames.first= sel_buffer.first; + gpl->frames.last= sel_buffer.last; + + continue; + } + + /* loop 2: remove duplicates of frames in buffers */ + //gfs= sel_buffer.first; + //gfsn= gfs->next; + + for (gpf= gpl->frames.first; gpf && sel_buffer.first; gpf= gpfn) { + gpfn= gpf->next; + + /* loop through sel_buffer, emptying stuff from front of buffer if ok */ + for (gfs= sel_buffer.first; gfs && gpf; gfs= gfsn) { + gfsn= gfs->next; + + /* if this buffer frame needs to go before current, add it! */ + if (gfs->framenum < gpf->framenum) { + /* transfer buffer frame to frames list (before current) */ + BLI_remlink(&sel_buffer, gfs); + BLI_insertlinkbefore(&gpl->frames, gpf, gfs); + } + /* if this buffer frame is on same frame, replace current with it and stop */ + else if (gfs->framenum == gpf->framenum) { + /* transfer buffer frame to frames list (before current) */ + BLI_remlink(&sel_buffer, gfs); + BLI_insertlinkbefore(&gpl->frames, gpf, gfs); + + /* get rid of current frame */ + gpencil_layer_delframe(gpl, gpf); + } + } + } + + /* if anything is still in buffer, append to end */ + for (gfs= sel_buffer.first; gfs; gfs= gfsn) { + gfsn= gfs->next; + + BLI_remlink(&sel_buffer, gfs); + BLI_addtail(&gpl->frames, gfs); + } + } +} + /* Called by special_aftertrans_update to make sure selected keyframes replace * any other keyframes which may reside on that frame (that is not selected). */ @@ -2698,6 +2790,26 @@ static int count_ipo_keys(Ipo *ipo, char side, float cfra) return count; } +/* fully select selected beztriples, but only include if it's on the right side of cfra */ +static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) +{ + bGPDframe *gpf; + int count = 0; + + if (gpl == NULL) + return count; + + /* only include points that occur on the right side of cfra */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(side, gpf->framenum, cfra)) + count++; + } + } + + return count; +} + /* This function assigns the information to transdata */ static void TimeToTransData(TransData *td, float *time, Object *ob) { @@ -2751,9 +2863,68 @@ static TransData *IpoToTransData(TransData *td, Ipo *ipo, Object *ob, char side, return td; } +/* helper struct for gp-frame transforms (only used here) */ +typedef struct tGPFtransdata { + float val; /* where transdata writes transform */ + int *sdata; /* pointer to gpf->framenum */ +} tGPFtransdata; + +/* This function helps flush transdata written to tempdata into the gp-frames */ +void flushTransGPactionData (TransInfo *t) +{ + tGPFtransdata *tfd; + int i; + + /* find the first one to start from */ + if (t->mode == TFM_TIME_SLIDE) + tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 ); + else + tfd= (tGPFtransdata *)(t->customData); + + /* flush data! */ + for (i = 0; i < t->total; i++, tfd++) { + *(tfd->sdata)= (int)floor(tfd->val + 0.5); + } +} + +/* This function advances the address to which td points to, so it must return + * the new address so that the next time new transform data is added, it doesn't + * overwrite the existing ones... i.e. td = GPLayerToTransData(td, ipo, ob, side, cfra); + * + * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data + * on the named side are used. + */ +static int GPLayerToTransData (TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, short side, float cfra) +{ + bGPDframe *gpf; + int count= 0; + + /* check for select frames on right side of current frame */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(side, gpf->framenum, cfra)) { + /* memory is calloc'ed, so that should zero everything nicely for us */ + td->val= &tfd->val; + td->ival= gpf->framenum; + + tfd->val= gpf->framenum; + tfd->sdata= &gpf->framenum; + + /* advance td now */ + td++; + tfd++; + count++; + } + } + } + + return count; +} + static void createTransActionData(TransInfo *t) { TransData *td = NULL; + tGPFtransdata *tfd = NULL; Object *ob= NULL; ListBase act_data = {NULL, NULL}; @@ -2771,7 +2942,10 @@ static void createTransActionData(TransInfo *t) if (data == NULL) return; /* filter data */ - filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); + if (datatype == ACTCONT_GPENCIL) + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT); + else + filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_IPOKEYS); actdata_filter(&act_data, filter, data, datatype); /* is the action scaled? if so, the it should belong to the active object */ @@ -2800,8 +2974,12 @@ static void createTransActionData(TransInfo *t) cfra = CFRA; /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ - for (ale= act_data.first; ale; ale= ale->next) - count += count_ipo_keys(ale->key_data, side, cfra); + for (ale= act_data.first; ale; ale= ale->next) { + if (ale->type == ACTTYPE_GPLAYER) + count += count_gplayer_frames(ale->data, side, cfra); + else + count += count_ipo_keys(ale->key_data, side, cfra); + } /* stop if trying to build list if nothing selected */ if (count == 0) { @@ -2812,16 +2990,38 @@ static void createTransActionData(TransInfo *t) /* allocate memory for data */ t->total= count; + t->data= MEM_callocN(t->total*sizeof(TransData), "TransData(Action Editor)"); - if (t->mode == TFM_TIME_SLIDE) + td= t->data; + + if (datatype == ACTCONT_GPENCIL) { + if (t->mode == TFM_TIME_SLIDE) { + t->customData= MEM_callocN((sizeof(float)*2)+(sizeof(tGPFtransdata)*count), "TimeSlide + tGPFtransdata"); + tfd= (tGPFtransdata *)( (float *)(t->customData) + 2 ); + } + else { + t->customData= MEM_callocN(sizeof(tGPFtransdata)*count, "tGPFtransdata"); + tfd= (tGPFtransdata *)(t->customData); + } + } + else if (t->mode == TFM_TIME_SLIDE) t->customData= MEM_callocN(sizeof(float)*2, "TimeSlide Min/Max"); - td= t->data; /* loop 2: build transdata array */ for (ale= act_data.first; ale; ale= ale->next) { - Ipo *ipo= (Ipo *)ale->key_data; - - td= IpoToTransData(td, ipo, ob, side, cfra); + if (ale->type == ACTTYPE_GPLAYER) { + bGPDlayer *gpl= (bGPDlayer *)ale->data; + int i; + + i = GPLayerToTransData(td, tfd, gpl, side, cfra); + td += i; + tfd += i; + } + else { + Ipo *ipo= (Ipo *)ale->key_data; + + td= IpoToTransData(td, ipo, ob, side, cfra); + } } /* check if we're supposed to be setting minx/maxx for TimeSlide */ @@ -3673,6 +3873,13 @@ void special_aftertrans_update(TransInfo *t) DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA); } + else if (datatype == ACTCONT_GPENCIL) { + /* remove duplicate frames and also make sure points are in order! */ + if ((cancelled == 0) || (duplicate)) + { + posttrans_gpd_clean(data); + } + } G.saction->flag &= ~SACTION_MOVING; } diff --git a/source/blender/src/transform_generics.c b/source/blender/src/transform_generics.c index 6cb7a34d1bc..c332fd723eb 100644 --- a/source/blender/src/transform_generics.c +++ b/source/blender/src/transform_generics.c @@ -278,6 +278,11 @@ void recalcData(TransInfo *t) data = get_action_context(&context); if (data == NULL) return; + /* always flush data if gpencil context */ + if (context == ACTCONT_GPENCIL) { + flushTransGPactionData(t); + } + if (G.saction->lock) { if (context == ACTCONT_ACTION) { if(ob) { @@ -753,6 +758,10 @@ void postTrans (TransInfo *t) if (G.sima->flag & SI_LIVE_UNWRAP) unwrap_lscm_live_end(t->state == TRANS_CANCEL); } + else if(t->spacetype==SPACE_ACTION) { + if (t->customData) + MEM_freeN(t->customData); + } } void applyTransObjects(TransInfo *t) diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index 4aea0df74b9..92e49ab29fa 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -883,7 +883,7 @@ void BIF_write_file(char *target) if (G.f & G_DOSCRIPTLINKS) BPY_do_pyscript(&G.scene->id, SCRIPT_ONSAVE); for (li= G.main->library.first; li; li= li->id.next) { - if (BLI_streq(li->name, target)) { + if (li->parent==NULL && BLI_streq(li->name, target)) { error("Cannot overwrite used library"); return; } diff --git a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp index 5f37de24ed6..1604dfe5cce 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp +++ b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp @@ -101,6 +101,13 @@ int KX_BlenderCanvas::GetHeight( return scrarea_get_win_height(m_area); } +RAS_Rect & +KX_BlenderCanvas:: +GetWindowArea( +){ + return m_area_rect; +} + void KX_BlenderCanvas:: SetViewPort( @@ -112,6 +119,11 @@ SetViewPort( int minx = scrarea_get_win_x(m_area); int miny = scrarea_get_win_y(m_area); + m_area_rect.SetLeft(minx + x1); + m_area_rect.SetBottom(miny + y1); + m_area_rect.SetRight(minx + x2); + m_area_rect.SetTop(miny + y2); + glViewport(minx + x1, miny + y1, vp_width, vp_height); glScissor(minx + x1, miny + y1, vp_width, vp_height); } diff --git a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.h b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.h index b155d39e149..bc202a8558c 100644 --- a/source/gameengine/BlenderRoutines/KX_BlenderCanvas.h +++ b/source/gameengine/BlenderRoutines/KX_BlenderCanvas.h @@ -117,6 +117,10 @@ public: return m_displayarea; }; + RAS_Rect & + GetWindowArea( + ); + void SetViewPort( int x1, int y1, @@ -159,6 +163,7 @@ public: private: /** Blender area the game engine is running within */ struct ScrArea* m_area; + RAS_Rect m_area_rect; }; #endif // __KX_BLENDERCANVAS diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp index f3e22cd297a..1f1ac6da119 100644 --- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp +++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp @@ -1847,7 +1847,10 @@ void BL_ConvertBlenderObjects(struct Main* maggie, int aspect_width; int aspect_height; vector inivel,iniang; - + set grouplist; // list of groups to be converted + set allblobj; // all objects converted + set groupobj; // objects from groups (never in active layer) + if (alwaysUseExpandFraming) { frame_type = RAS_FrameSettings::e_frame_extend; aspect_width = canvas->GetWidth(); @@ -1919,6 +1922,8 @@ void BL_ConvertBlenderObjects(struct Main* maggie, for (SETLOOPER(blenderscene, base)) { Object* blenderobject = base->object; + allblobj.insert(blenderobject); + KX_GameObject* gameobj = gameobject_from_blenderobject( base->object, kxscene, @@ -2046,7 +2051,9 @@ void BL_ConvertBlenderObjects(struct Main* maggie, gameobj->NodeUpdateGS(0,true); gameobj->Bucketize(); - + + if (gameobj->IsDupliGroup()) + grouplist.insert(blenderobject->dup_group); } else { @@ -2073,6 +2080,188 @@ void BL_ConvertBlenderObjects(struct Main* maggie, } + if (!grouplist.empty()) + { + // now convert the group referenced by dupli group object + // keep track of all groups already converted + set allgrouplist = grouplist; + set tempglist; + // recurse + while (!grouplist.empty()) + { + set::iterator git; + tempglist.clear(); + tempglist.swap(grouplist); + for (git=tempglist.begin(); git!=tempglist.end(); git++) + { + Group* group = *git; + GroupObject* go; + for(go=(GroupObject*)group->gobject.first; go; go=(GroupObject*)go->next) + { + Object* blenderobject = go->ob; + if (converter->FindGameObject(blenderobject) == NULL) + { + allblobj.insert(blenderobject); + groupobj.insert(blenderobject); + KX_GameObject* gameobj = gameobject_from_blenderobject( + blenderobject, + kxscene, + rendertools, + converter, + blenderscene); + + // this code is copied from above except that + // object from groups are never is active layer + bool isInActiveLayer = false; + bool addobj=true; + + if (converter->addInitFromFrame) + if (!isInActiveLayer) + addobj=false; + + if (gameobj&&addobj) + { + MT_Point3 posPrev; + MT_Matrix3x3 angor; + if (converter->addInitFromFrame) + blenderscene->r.cfra=blenderscene->r.sfra; + + MT_Point3 pos = MT_Point3( + blenderobject->loc[0]+blenderobject->dloc[0], + blenderobject->loc[1]+blenderobject->dloc[1], + blenderobject->loc[2]+blenderobject->dloc[2] + ); + MT_Vector3 eulxyz = MT_Vector3( + blenderobject->rot[0], + blenderobject->rot[1], + blenderobject->rot[2] + ); + MT_Vector3 scale = MT_Vector3( + blenderobject->size[0], + blenderobject->size[1], + blenderobject->size[2] + ); + if (converter->addInitFromFrame){//rcruiz + float eulxyzPrev[3]; + blenderscene->r.cfra=blenderscene->r.sfra-1; + update_for_newframe(); + MT_Vector3 tmp=pos-MT_Point3(blenderobject->loc[0]+blenderobject->dloc[0], + blenderobject->loc[1]+blenderobject->dloc[1], + blenderobject->loc[2]+blenderobject->dloc[2] + ); + eulxyzPrev[0]=blenderobject->rot[0]; + eulxyzPrev[1]=blenderobject->rot[1]; + eulxyzPrev[2]=blenderobject->rot[2]; + + double fps = (double) blenderscene->r.frs_sec/ + (double) blenderscene->r.frs_sec_base; + + tmp.scale(fps, fps, fps); + inivel.push_back(tmp); + tmp=eulxyz-eulxyzPrev; + tmp.scale(fps, fps, fps); + iniang.push_back(tmp); + blenderscene->r.cfra=blenderscene->r.sfra; + update_for_newframe(); + } + + gameobj->NodeSetLocalPosition(pos); + gameobj->NodeSetLocalOrientation(MT_Matrix3x3(eulxyz)); + gameobj->NodeSetLocalScale(scale); + gameobj->NodeUpdateGS(0,true); + + BL_ConvertIpos(blenderobject,gameobj,converter); + // TODO: expand to multiple ipos per mesh + Material *mat = give_current_material(blenderobject, 1); + if(mat) BL_ConvertMaterialIpos(mat, gameobj, converter); + + sumolist->Add(gameobj->AddRef()); + + BL_ConvertProperties(blenderobject,gameobj,timemgr,kxscene,isInActiveLayer); + + + gameobj->SetName(blenderobject->id.name); + + // templist to find Root Parents (object with no parents) + templist->Add(gameobj->AddRef()); + + // update children/parent hierarchy + if ((blenderobject->parent != 0)&&(!converter->addInitFromFrame)) + { + // blender has an additional 'parentinverse' offset in each object + SG_Node* parentinversenode = new SG_Node(NULL,NULL,SG_Callbacks()); + + // define a normal parent relationship for this node. + KX_NormalParentRelation * parent_relation = KX_NormalParentRelation::New(); + parentinversenode->SetParentRelation(parent_relation); + + parentChildLink pclink; + pclink.m_blenderchild = blenderobject; + pclink.m_gamechildnode = parentinversenode; + vec_parent_child.push_back(pclink); + + float* fl = (float*) blenderobject->parentinv; + MT_Transform parinvtrans(fl); + parentinversenode->SetLocalPosition(parinvtrans.getOrigin()); + parentinversenode->SetLocalOrientation(parinvtrans.getBasis()); + + parentinversenode->AddChild(gameobj->GetSGNode()); + } + + // needed for python scripting + logicmgr->RegisterGameObjectName(gameobj->GetName(),gameobj); + + // needed for dynamic object morphing + logicmgr->RegisterGameObj(gameobj, blenderobject); + for (int i = 0; i < gameobj->GetMeshCount(); i++) + logicmgr->RegisterGameMeshName(gameobj->GetMesh(i)->GetName(), blenderobject); + + converter->RegisterGameObject(gameobj, blenderobject); + // this was put in rapidly, needs to be looked at more closely + // only draw/use objects in active 'blender' layers + + logicbrick_conversionlist->Add(gameobj->AddRef()); + + if (converter->addInitFromFrame){ + posPrev=gameobj->NodeGetWorldPosition(); + angor=gameobj->NodeGetWorldOrientation(); + } + if (isInActiveLayer) + { + objectlist->Add(gameobj->AddRef()); + //tf.Add(gameobj->GetSGNode()); + + gameobj->NodeUpdateGS(0,true); + gameobj->Bucketize(); + + } + else + { + //we must store this object otherwise it will be deleted + //at the end of this function if it is not a root object + inactivelist->Add(gameobj->AddRef()); + + } + if (gameobj->IsDupliGroup()) + { + // check that the group is not already converted + if (allgrouplist.insert(blenderobject->dup_group).second) + grouplist.insert(blenderobject->dup_group); + } + if (converter->addInitFromFrame){ + gameobj->NodeSetLocalPosition(posPrev); + gameobj->NodeSetLocalOrientation(angor); + } + + } + if (gameobj) + gameobj->Release(); + } + } + } + } + } + if (blenderscene->camera) { KX_Camera *gamecamera= (KX_Camera*) converter->FindGameObject(blenderscene->camera); @@ -2081,15 +2270,18 @@ void BL_ConvertBlenderObjects(struct Main* maggie, } // Set up armatures - for(SETLOOPER(blenderscene, base)){ - if (base->object->type==OB_MESH){ - Mesh *me = (Mesh*)base->object->data; + set::iterator oit; + for(oit=allblobj.begin(); oit!=allblobj.end(); oit++) + { + Object* blenderobj = *oit; + if (blenderobj->type==OB_MESH){ + Mesh *me = (Mesh*)blenderobj->data; if (me->dvert){ - KX_GameObject *obj = converter->FindGameObject(base->object); + KX_GameObject *obj = converter->FindGameObject(blenderobj); - if (base->object->parent && base->object->parent->type==OB_ARMATURE && base->object->partype==PARSKEL){ - KX_GameObject *par = converter->FindGameObject(base->object->parent); + if (obj && blenderobj->parent && blenderobj->parent->type==OB_ARMATURE && blenderobj->partype==PARSKEL){ + KX_GameObject *par = converter->FindGameObject(blenderobj->parent); if (par) ((BL_SkinDeformer*)(((BL_DeformableGameObject*)obj)->m_pDeformer))->SetArmature((BL_ArmatureObject*) par); } @@ -2174,7 +2366,8 @@ void BL_ConvertBlenderObjects(struct Main* maggie, { meshobj = gameobj->GetMesh(0); } - BL_CreatePhysicsObjectNew(gameobj,blenderobject,meshobj,kxscene,activeLayerBitInfo,physics_engine,converter,processCompoundChildren); + int layerMask = (groupobj.find(blenderobject) == groupobj.end()) ? activeLayerBitInfo : 0; + BL_CreatePhysicsObjectNew(gameobj,blenderobject,meshobj,kxscene,layerMask,physics_engine,converter,processCompoundChildren); } processCompoundChildren = true; @@ -2189,7 +2382,8 @@ void BL_ConvertBlenderObjects(struct Main* maggie, { meshobj = gameobj->GetMesh(0); } - BL_CreatePhysicsObjectNew(gameobj,blenderobject,meshobj,kxscene,activeLayerBitInfo,physics_engine,converter,processCompoundChildren); + int layerMask = (groupobj.find(blenderobject) == groupobj.end()) ? activeLayerBitInfo : 0; + BL_CreatePhysicsObjectNew(gameobj,blenderobject,meshobj,kxscene,layerMask,physics_engine,converter,processCompoundChildren); } @@ -2311,22 +2505,25 @@ void BL_ConvertBlenderObjects(struct Main* maggie, { KX_GameObject* gameobj = static_cast(logicbrick_conversionlist->GetValue(i)); struct Object* blenderobj = converter->FindBlenderObject(gameobj); - bool isInActiveLayer = (blenderobj->lay & activeLayerBitInfo)!=0; - BL_ConvertActuators(maggie->name, blenderobj,gameobj,logicmgr,kxscene,ketsjiEngine,executePriority, activeLayerBitInfo,isInActiveLayer,rendertools,converter); + int layerMask = (groupobj.find(blenderobj) == groupobj.end()) ? activeLayerBitInfo : 0; + bool isInActiveLayer = (blenderobj->lay & layerMask)!=0; + BL_ConvertActuators(maggie->name, blenderobj,gameobj,logicmgr,kxscene,ketsjiEngine,executePriority, layerMask,isInActiveLayer,rendertools,converter); } for ( i=0;iGetCount();i++) { KX_GameObject* gameobj = static_cast(logicbrick_conversionlist->GetValue(i)); struct Object* blenderobj = converter->FindBlenderObject(gameobj); - bool isInActiveLayer = (blenderobj->lay & activeLayerBitInfo)!=0; - BL_ConvertControllers(blenderobj,gameobj,logicmgr,pythondictionary,executePriority,activeLayerBitInfo,isInActiveLayer,converter); + int layerMask = (groupobj.find(blenderobj) == groupobj.end()) ? activeLayerBitInfo : 0; + bool isInActiveLayer = (blenderobj->lay & layerMask)!=0; + BL_ConvertControllers(blenderobj,gameobj,logicmgr,pythondictionary,executePriority,layerMask,isInActiveLayer,converter); } for ( i=0;iGetCount();i++) { KX_GameObject* gameobj = static_cast(logicbrick_conversionlist->GetValue(i)); struct Object* blenderobj = converter->FindBlenderObject(gameobj); - bool isInActiveLayer = (blenderobj->lay & activeLayerBitInfo)!=0; - BL_ConvertSensors(blenderobj,gameobj,logicmgr,kxscene,keydev,executePriority,activeLayerBitInfo,isInActiveLayer,canvas,converter); + int layerMask = (groupobj.find(blenderobj) == groupobj.end()) ? activeLayerBitInfo : 0; + bool isInActiveLayer = (blenderobj->lay & layerMask)!=0; + BL_ConvertSensors(blenderobj,gameobj,logicmgr,kxscene,keydev,executePriority,layerMask,isInActiveLayer,canvas,converter); } // apply the initial state to controllers for ( i=0;iGetCount();i++) @@ -2344,5 +2541,19 @@ void BL_ConvertBlenderObjects(struct Main* maggie, // Calculate the scene btree - // too slow - commented out. //kxscene->SetNodeTree(tf.MakeTree()); + + // instantiate dupli group, we will loop trough the object + // that are in active layers. Note that duplicating group + // has the effect of adding objects at the end of objectlist. + // Only loop through the first part of the list. + int objcount = objectlist->GetCount(); + for (i=0;iGetValue(i); + if (gameobj->IsDupliGroup()) + { + kxscene->DupliGroupRecurse(gameobj, 0); + } + } } diff --git a/source/gameengine/Converter/BL_DeformableGameObject.h b/source/gameengine/Converter/BL_DeformableGameObject.h index 57a404ad72b..315ad18c42c 100644 --- a/source/gameengine/Converter/BL_DeformableGameObject.h +++ b/source/gameengine/Converter/BL_DeformableGameObject.h @@ -60,6 +60,7 @@ public: { if (m_pDeformer) m_pDeformer->Relink (map); + KX_GameObject::Relink(map); }; void ProcessReplica(KX_GameObject* replica); diff --git a/source/gameengine/Converter/BL_ShapeDeformer.cpp b/source/gameengine/Converter/BL_ShapeDeformer.cpp index eb5c1467ea5..b2e54539b19 100644 --- a/source/gameengine/Converter/BL_ShapeDeformer.cpp +++ b/source/gameengine/Converter/BL_ShapeDeformer.cpp @@ -109,6 +109,13 @@ bool BL_ShapeDeformer::ExecuteShapeDrivers(void) vector::iterator it; void *poin; int type; + // the shape drivers use the bone matrix as input. Must + // update the matrix now + Object* par_arma = m_armobj->GetArmatureObject(); + m_armobj->ApplyPose(); + where_is_pose( par_arma ); + PoseApplied(true); + for (it=m_shapeDrivers.begin(); it!=m_shapeDrivers.end(); it++) { // no need to set a specific time: this curve has a driver IpoCurve *icu = *it; diff --git a/source/gameengine/Converter/BL_SkinDeformer.cpp b/source/gameengine/Converter/BL_SkinDeformer.cpp index d3442fe5298..f96c40c098f 100644 --- a/source/gameengine/Converter/BL_SkinDeformer.cpp +++ b/source/gameengine/Converter/BL_SkinDeformer.cpp @@ -66,7 +66,8 @@ BL_SkinDeformer::BL_SkinDeformer(BL_DeformableGameObject *gameobj, m_armobj(arma), m_lastArmaUpdate(-1), m_defbase(&bmeshobj->defbase), - m_releaseobject(false) + m_releaseobject(false), + m_poseApplied(false) { Mat4CpyMat4(m_obmat, bmeshobj->obmat); }; @@ -98,32 +99,28 @@ BL_SkinDeformer::~BL_SkinDeformer() m_armobj->Release(); } -bool BL_SkinDeformer::Apply(RAS_IPolyMaterial *) +bool BL_SkinDeformer::Apply(RAS_IPolyMaterial *mat) { size_t i, j; - if (!Update()) - // no need to update the cache - return false; + // update the vertex in m_transverts + Update(); - // Update all materials at once, so we can do the above update test - // without ending up with some materials not updated - for(RAS_MaterialBucket::Set::iterator mit = m_pMeshObject->GetFirstMaterial(); - mit != m_pMeshObject->GetLastMaterial(); ++ mit) { - RAS_IPolyMaterial *mat = (*mit)->GetPolyMaterial(); + // The vertex cache can only be updated for this deformer: + // Duplicated objects with more than one ploymaterial (=multiple mesh slot per object) + // share the same mesh (=the same cache). As the rendering is done per polymaterial + // cycling through the objects, the entire mesh cache cannot be updated in one shot. + vecVertexArray& vertexarrays = m_pMeshObject->GetVertexCache(mat); - vecVertexArray& vertexarrays = m_pMeshObject->GetVertexCache(mat); + // For each array + for (i=0; iGetArmatureObject(); - where_is_pose( par_arma ); + if (!PoseApplied()){ + m_armobj->ApplyPose(); + where_is_pose( par_arma ); + } /* store verts locally */ VerifyStorage(); @@ -180,7 +179,8 @@ bool BL_SkinDeformer::Update(void) /* Update the current frame */ m_lastArmaUpdate=m_armobj->GetLastFrame(); - + /* reset for next frame */ + PoseApplied(false); /* indicate that the m_transverts and normals are up to date */ return true; } diff --git a/source/gameengine/Converter/BL_SkinDeformer.h b/source/gameengine/Converter/BL_SkinDeformer.h index f35db8273c4..d3fc5ae2a81 100644 --- a/source/gameengine/Converter/BL_SkinDeformer.h +++ b/source/gameengine/Converter/BL_SkinDeformer.h @@ -81,10 +81,13 @@ public: virtual ~BL_SkinDeformer(); bool Update (void); bool Apply (class RAS_IPolyMaterial *polymat); + bool PoseApplied() + { return m_poseApplied; } + void PoseApplied(bool applied) + { m_poseApplied = applied; } bool PoseUpdated(void) { if (m_armobj && m_lastArmaUpdate!=m_armobj->GetLastFrame()) { - m_armobj->ApplyPose(); return true; } return false; @@ -102,6 +105,7 @@ protected: ListBase* m_defbase; float m_obmat[4][4]; // the reference matrix for skeleton deform bool m_releaseobject; + bool m_poseApplied; }; diff --git a/source/gameengine/Converter/KX_ConvertActuators.cpp b/source/gameengine/Converter/KX_ConvertActuators.cpp index 1cc1b2e27a3..8739fb109fd 100644 --- a/source/gameengine/Converter/KX_ConvertActuators.cpp +++ b/source/gameengine/Converter/KX_ConvertActuators.cpp @@ -503,7 +503,7 @@ void BL_ConvertActuators(char* maggiename, case ACT_PROPERTY: { bPropertyActuator* propact = (bPropertyActuator*) bact->data; - CValue* destinationObj = NULL; + SCA_IObject* destinationObj = NULL; /* here the destinationobject is searched. problem with multiple scenes: other scenes diff --git a/source/gameengine/GameLogic/SCA_ILogicBrick.cpp b/source/gameengine/GameLogic/SCA_ILogicBrick.cpp index f6efd485adb..e73358bc1e8 100644 --- a/source/gameengine/GameLogic/SCA_ILogicBrick.cpp +++ b/source/gameengine/GameLogic/SCA_ILogicBrick.cpp @@ -82,7 +82,10 @@ void SCA_ILogicBrick::ReParent(SCA_IObject* parent) m_gameobj = parent; } - +void SCA_ILogicBrick::Relink(GEN_Map *obj_map) +{ + // nothing to do +} CValue* SCA_ILogicBrick::Calc(VALUE_OPERATOR op, CValue *val) { diff --git a/source/gameengine/GameLogic/SCA_ILogicBrick.h b/source/gameengine/GameLogic/SCA_ILogicBrick.h index 80bc6ae3b86..c28711ac0f6 100644 --- a/source/gameengine/GameLogic/SCA_ILogicBrick.h +++ b/source/gameengine/GameLogic/SCA_ILogicBrick.h @@ -32,6 +32,8 @@ #include "Value.h" #include "SCA_IObject.h" #include "BoolValue.h" +#include "GEN_Map.h" +#include "GEN_HashedPtr.h" class SCA_ILogicBrick : public CValue { @@ -59,6 +61,7 @@ public: SCA_IObject* GetParent(); virtual void ReParent(SCA_IObject* parent); + virtual void Relink(GEN_Map *obj_map); // act as a BoolValue (with value IsPositiveTrigger) virtual CValue* Calc(VALUE_OPERATOR op, CValue *val); diff --git a/source/gameengine/GameLogic/SCA_IObject.cpp b/source/gameengine/GameLogic/SCA_IObject.cpp index c5bb4a41552..25b4af696ea 100644 --- a/source/gameengine/GameLogic/SCA_IObject.cpp +++ b/source/gameengine/GameLogic/SCA_IObject.cpp @@ -306,7 +306,7 @@ const MT_Point3& SCA_IObject::ConvertPythonPylist(PyObject* pylist) } #endif -void SCA_IObject::Suspend(void) +void SCA_IObject::Suspend() { if ((!m_ignore_activity_culling) && (!m_suspended)) { diff --git a/source/gameengine/GameLogic/SCA_PropertyActuator.cpp b/source/gameengine/GameLogic/SCA_PropertyActuator.cpp index ebe1cd51863..7062f2cef6a 100644 --- a/source/gameengine/GameLogic/SCA_PropertyActuator.cpp +++ b/source/gameengine/GameLogic/SCA_PropertyActuator.cpp @@ -42,7 +42,7 @@ /* Native functions */ /* ------------------------------------------------------------------------- */ -SCA_PropertyActuator::SCA_PropertyActuator(SCA_IObject* gameobj,CValue* sourceObj,const STR_String& propname,const STR_String& expr,int acttype,PyTypeObject* T ) +SCA_PropertyActuator::SCA_PropertyActuator(SCA_IObject* gameobj,SCA_IObject* sourceObj,const STR_String& propname,const STR_String& expr,int acttype,PyTypeObject* T ) : SCA_IActuator(gameobj,T), m_type(acttype), m_propname(propname), @@ -51,14 +51,14 @@ SCA_PropertyActuator::SCA_PropertyActuator(SCA_IObject* gameobj,CValue* sourceOb { // protect ourselves against someone else deleting the source object // don't protect against ourselves: it would create a dead lock - if (m_sourceObj && m_sourceObj != GetParent()) - m_sourceObj->AddRef(); + if (m_sourceObj) + m_sourceObj->RegisterActuator(this); } SCA_PropertyActuator::~SCA_PropertyActuator() { - if (m_sourceObj && m_sourceObj != GetParent()) - m_sourceObj->Release(); + if (m_sourceObj) + m_sourceObj->UnregisterActuator(this); } bool SCA_PropertyActuator::Update() @@ -185,10 +185,31 @@ void SCA_PropertyActuator::ProcessReplica() // no need to check for self reference like in the constructor: // the replica will always have a different parent if (m_sourceObj) - m_sourceObj->AddRef(); + m_sourceObj->RegisterActuator(this); SCA_IActuator::ProcessReplica(); } +bool SCA_PropertyActuator::UnlinkObject(SCA_IObject* clientobj) +{ + if (clientobj == m_sourceObj) + { + // this object is being deleted, we cannot continue to track it. + m_sourceObj = NULL; + return true; + } + return false; +} + +void SCA_PropertyActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_sourceObj]; + if (h_obj) { + if (m_sourceObj) + m_sourceObj->UnregisterActuator(this); + m_sourceObj = (SCA_IObject*)(*h_obj); + m_sourceObj->RegisterActuator(this); + } +} /* ------------------------------------------------------------------------- */ diff --git a/source/gameengine/GameLogic/SCA_PropertyActuator.h b/source/gameengine/GameLogic/SCA_PropertyActuator.h index f8305901c51..1e435684572 100644 --- a/source/gameengine/GameLogic/SCA_PropertyActuator.h +++ b/source/gameengine/GameLogic/SCA_PropertyActuator.h @@ -52,7 +52,7 @@ class SCA_PropertyActuator : public SCA_IActuator int m_type; STR_String m_propname; STR_String m_exprtxt; - CValue* m_sourceObj; // for copy property actuator + SCA_IObject* m_sourceObj; // for copy property actuator public: @@ -60,7 +60,7 @@ public: SCA_PropertyActuator( SCA_IObject* gameobj, - CValue* sourceObj, + SCA_IObject* sourceObj, const STR_String& propname, const STR_String& expr, int acttype, @@ -74,7 +74,9 @@ public: GetReplica( ); - void ProcessReplica(); + virtual void ProcessReplica(); + virtual bool UnlinkObject(SCA_IObject* clientobj); + virtual void Relink(GEN_Map *obj_map); virtual bool Update(); diff --git a/source/gameengine/GamePlayer/common/GPC_Canvas.h b/source/gameengine/GamePlayer/common/GPC_Canvas.h index f82166dfa88..87719041f65 100644 --- a/source/gameengine/GamePlayer/common/GPC_Canvas.h +++ b/source/gameengine/GamePlayer/common/GPC_Canvas.h @@ -130,6 +130,12 @@ public: ) { return m_displayarea; }; + + RAS_Rect & + GetWindowArea( + ) { + return m_displayarea; + } void BeginFrame( diff --git a/source/gameengine/Ketsji/KX_BulletPhysicsController.cpp b/source/gameengine/Ketsji/KX_BulletPhysicsController.cpp index 70443ced7a9..c7b2a671f78 100644 --- a/source/gameengine/Ketsji/KX_BulletPhysicsController.cpp +++ b/source/gameengine/Ketsji/KX_BulletPhysicsController.cpp @@ -13,11 +13,13 @@ #include "KX_ClientObjectInfo.h" #include "PHY_IPhysicsEnvironment.h" +#include "CcdPhysicsEnvironment.h" KX_BulletPhysicsController::KX_BulletPhysicsController (const CcdConstructionInfo& ci, bool dyna) : KX_IPhysicsController(dyna,(PHY_IPhysicsController*)this), -CcdPhysicsController(ci) +CcdPhysicsController(ci), +m_savedCollisionFlags(0) { } @@ -131,9 +133,10 @@ void KX_BulletPhysicsController::getOrientation(MT_Quaternion& orn) CcdPhysicsController::getOrientation(myorn[0],myorn[1],myorn[2],myorn[3]); orn = MT_Quaternion(myorn[0],myorn[1],myorn[2],myorn[3]); } -void KX_BulletPhysicsController::setOrientation(const MT_Quaternion& orn) +void KX_BulletPhysicsController::setOrientation(const MT_Matrix3x3& orn) { - CcdPhysicsController::setOrientation(orn.x(),orn.y(),orn.z(),orn.w()); + btMatrix3x3 btmat(orn[0][0], orn[0][1], orn[1][2], orn[1][0], orn[1][1], orn[1][2], orn[2][0], orn[2][1], orn[2][2]); + CcdPhysicsController::setWorldOrientation(btmat); } void KX_BulletPhysicsController::setPosition(const MT_Point3& pos) { @@ -161,14 +164,37 @@ void KX_BulletPhysicsController::setRigidBody(bool rigid) { } -void KX_BulletPhysicsController::SuspendDynamics() +void KX_BulletPhysicsController::SuspendDynamics(bool ghost) { - GetRigidBody()->setActivationState(DISABLE_SIMULATION); - + btRigidBody *body = GetRigidBody(); + if (body->getActivationState() != DISABLE_SIMULATION) + { + btBroadphaseProxy* handle = body->getBroadphaseHandle(); + m_savedCollisionFlags = body->getCollisionFlags(); + m_savedMass = GetMass(); + m_savedCollisionFilterGroup = handle->m_collisionFilterGroup; + m_savedCollisionFilterMask = handle->m_collisionFilterMask; + body->setActivationState(DISABLE_SIMULATION); + GetPhysicsEnvironment()->updateCcdPhysicsController(this, + 0.0, + btCollisionObject::CF_STATIC_OBJECT|((ghost)?btCollisionObject::CF_NO_CONTACT_RESPONSE:(m_savedCollisionFlags&btCollisionObject::CF_NO_CONTACT_RESPONSE)), + btBroadphaseProxy::StaticFilter, + btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter); + } } + void KX_BulletPhysicsController::RestoreDynamics() { - GetRigidBody()->forceActivationState(ACTIVE_TAG); + btRigidBody *body = GetRigidBody(); + if (body->getActivationState() == DISABLE_SIMULATION) + { + GetPhysicsEnvironment()->updateCcdPhysicsController(this, + m_savedMass, + m_savedCollisionFlags, + m_savedCollisionFilterGroup, + m_savedCollisionFilterMask); + GetRigidBody()->forceActivationState(ACTIVE_TAG); + } } SG_Controller* KX_BulletPhysicsController::GetReplica(class SG_Node* destnode) diff --git a/source/gameengine/Ketsji/KX_BulletPhysicsController.h b/source/gameengine/Ketsji/KX_BulletPhysicsController.h index 0853755dffa..2efe0474b30 100644 --- a/source/gameengine/Ketsji/KX_BulletPhysicsController.h +++ b/source/gameengine/Ketsji/KX_BulletPhysicsController.h @@ -7,6 +7,11 @@ class KX_BulletPhysicsController : public KX_IPhysicsController ,public CcdPhysicsController { +private: + int m_savedCollisionFlags; + short int m_savedCollisionFilterGroup; + short int m_savedCollisionFilterMask; + MT_Scalar m_savedMass; public: @@ -30,7 +35,7 @@ public: virtual void SetAngularVelocity(const MT_Vector3& ang_vel,bool local); virtual void SetLinearVelocity(const MT_Vector3& lin_vel,bool local); virtual void getOrientation(MT_Quaternion& orn); - virtual void setOrientation(const MT_Quaternion& orn); + virtual void setOrientation(const MT_Matrix3x3& orn); virtual void setPosition(const MT_Point3& pos); virtual void setScaling(const MT_Vector3& scaling); virtual MT_Scalar GetMass(); @@ -39,7 +44,7 @@ public: virtual void resolveCombinedVelocities(float linvelX,float linvelY,float linvelZ,float angVelX,float angVelY,float angVelZ); - virtual void SuspendDynamics(); + virtual void SuspendDynamics(bool ghost); virtual void RestoreDynamics(); virtual SG_Controller* GetReplica(class SG_Node* destnode); diff --git a/source/gameengine/Ketsji/KX_CameraActuator.cpp b/source/gameengine/Ketsji/KX_CameraActuator.cpp index 27f4870de10..0a97b6f0a2f 100644 --- a/source/gameengine/Ketsji/KX_CameraActuator.cpp +++ b/source/gameengine/Ketsji/KX_CameraActuator.cpp @@ -49,7 +49,7 @@ STR_String KX_CameraActuator::Y_AXIS_STRING = "y"; KX_CameraActuator::KX_CameraActuator( SCA_IObject* gameobj, - CValue *obj, + SCA_IObject *obj, MT_Scalar hght, MT_Scalar minhght, MT_Scalar maxhght, @@ -63,11 +63,14 @@ KX_CameraActuator::KX_CameraActuator( m_maxHeight (maxhght), m_x (xytog) { + if (m_ob) + m_ob->RegisterActuator(this); } KX_CameraActuator::~KX_CameraActuator() { - //nothing to do + if (m_ob) + m_ob->UnregisterActuator(this); } CValue* @@ -81,9 +84,36 @@ GetReplica( return replica; }; +void KX_CameraActuator::ProcessReplica() +{ + if (m_ob) + m_ob->RegisterActuator(this); + SCA_IActuator::ProcessReplica(); +} +bool KX_CameraActuator::UnlinkObject(SCA_IObject* clientobj) +{ + if (clientobj == m_ob) + { + // this object is being deleted, we cannot continue to track it. + m_ob = NULL; + return true; + } + return false; +} +void KX_CameraActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_ob]; + if (h_obj) { + if (m_ob) + m_ob->UnregisterActuator(this); + m_ob = (SCA_IObject*)(*h_obj); + m_ob->RegisterActuator(this); + } +} + /* three functions copied from blender arith... don't know if there's an equivalent */ static float Kx_Normalize(float *n) @@ -181,8 +211,14 @@ static void Kx_VecUpMat3(float *vec, float mat[][3], short axis) bool KX_CameraActuator::Update(double curtime, bool frame) { - bool result = true; + /* wondering... is it really neccesary/desirable to suppress negative */ + /* events here? */ + bool bNegativeEvent = IsNegativeEvent(); + RemoveAllEvents(); + if (bNegativeEvent || !m_ob) + return false; + KX_GameObject *obj = (KX_GameObject*) GetParent(); MT_Point3 from = obj->NodeGetWorldPosition(); MT_Matrix3x3 frommat = obj->NodeGetWorldOrientation(); @@ -195,13 +231,6 @@ bool KX_CameraActuator::Update(double curtime, bool frame) float mindistsq, maxdistsq, distsq; float mat[3][3]; - /* wondering... is it really neccesary/desirable to suppress negative */ - /* events here? */ - bool bNegativeEvent = IsNegativeEvent(); - RemoveAllEvents(); - - if (bNegativeEvent) return false; - /* The rules: */ /* CONSTRAINT 1: not implemented */ /* CONSTRAINT 2: can camera see actor? */ @@ -315,7 +344,7 @@ bool KX_CameraActuator::Update(double curtime, bool frame) actormat[2][0]= mat[0][2]; actormat[2][1]= mat[1][2]; actormat[2][2]= mat[2][2]; obj->NodeSetLocalOrientation(actormat); - return result; + return true; } CValue *KX_CameraActuator::findObject(char *obName) @@ -404,7 +433,11 @@ PyObject* KX_CameraActuator::PySetObject(PyObject* self, PyObject* gameobj; if (PyArg_ParseTuple(args, "O!", &KX_GameObject::Type, &gameobj)) { - m_ob = (CValue*)gameobj; + if (m_ob) + m_ob->UnregisterActuator(this); + m_ob = (SCA_IObject*)gameobj; + if (m_ob) + m_ob->RegisterActuator(this); Py_Return; } PyErr_Clear(); @@ -412,10 +445,13 @@ PyObject* KX_CameraActuator::PySetObject(PyObject* self, char* objectname; if (PyArg_ParseTuple(args, "s", &objectname)) { - CValue *object = (CValue*)SCA_ILogicBrick::m_sCurrentLogicManager->GetGameObjectByName(STR_String(objectname)); + SCA_IObject *object = (SCA_IObject*)SCA_ILogicBrick::m_sCurrentLogicManager->GetGameObjectByName(STR_String(objectname)); if(object) { + if (m_ob != NULL) + m_ob->UnregisterActuator(this); m_ob = object; + m_ob->RegisterActuator(this); Py_Return; } } diff --git a/source/gameengine/Ketsji/KX_CameraActuator.h b/source/gameengine/Ketsji/KX_CameraActuator.h index eb007e403ec..488b36922b0 100644 --- a/source/gameengine/Ketsji/KX_CameraActuator.h +++ b/source/gameengine/Ketsji/KX_CameraActuator.h @@ -49,7 +49,7 @@ class KX_CameraActuator : public SCA_IActuator Py_Header; private : /** Object that will be tracked. */ - CValue *m_ob; + SCA_IObject *m_ob; /** height (float), */ //const MT_Scalar m_height; @@ -87,7 +87,7 @@ private : SCA_IObject *gameobj, //const CValue *ob, - CValue *ob, + SCA_IObject *ob, MT_Scalar hght, MT_Scalar minhght, MT_Scalar maxhght, @@ -103,6 +103,7 @@ private : /** Methods Inherited from CValue */ CValue* GetReplica(); + virtual void ProcessReplica(); /** Methods inherited from SCA_IActuator */ @@ -110,7 +111,10 @@ private : double curtime, bool frame ); + virtual bool UnlinkObject(SCA_IObject* clientobj); + /** Methods inherited from SCA_ILogicBrick */ + virtual void Relink(GEN_Map *obj_map); /* --------------------------------------------------------------------- */ /* Python interface ---------------------------------------------------- */ diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp index 80288a72485..e0b2efb3a25 100644 --- a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp +++ b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp @@ -178,16 +178,18 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame) direction[2] = rotation[2][1]; axis = 1; break; - case KX_ACT_CONSTRAINT_ORIZ: + default: direction[0] = rotation[0][2]; direction[1] = rotation[1][2]; direction[2] = rotation[2][2]; axis = 2; break; } - // apply damping on the direction if (m_posDampTime) { + // apply damping on the direction direction = filter*direction + (1.0-filter)*m_refDirection; + } else { + direction = m_refDirection; } obj->AlignAxisToVect(direction, axis); result = true; diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 2ac4f909077..d40fa1e2446 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -61,6 +61,8 @@ typedef unsigned long uint_ptr; #include "KX_RayCast.h" #include "KX_PythonInit.h" #include "KX_PyMath.h" +#include "SCA_IActuator.h" +#include "SCA_ISensor.h" // This file defines relationships between parents and children // in the game engine. @@ -222,6 +224,10 @@ void KX_GameObject::SetParent(KX_Scene *scene, KX_GameObject* obj) RemoveParent(scene); obj->GetSGNode()->AddChild(GetSGNode()); + if (m_pPhysicsController1) + { + m_pPhysicsController1->SuspendDynamics(true); + } // Set us to our new scale, position, and orientation scale1[0] = scale1[0]/scale2[0]; scale1[1] = scale1[1]/scale2[1]; @@ -258,6 +264,10 @@ void KX_GameObject::RemoveParent(KX_Scene *scene) if (!rootlist->SearchValue(this)) // object was not in root list, add it now and increment ref count rootlist->Add(AddRef()); + if (m_pPhysicsController1) + { + m_pPhysicsController1->RestoreDynamics(); + } } } @@ -714,8 +724,12 @@ MT_Vector3 KX_GameObject::GetAngularVelocity(bool local) void KX_GameObject::NodeSetLocalPosition(const MT_Point3& trans) { - if (m_pPhysicsController1) + if (m_pPhysicsController1 && (!GetSGNode() || !GetSGNode()->GetSGParent())) { + // don't update physic controller if the object is a child: + // 1) the transformation will not be right + // 2) in this case, the physic controller is necessarily a static object + // that is updated from the normal kinematic synchronization m_pPhysicsController1->setPosition(trans); } @@ -727,25 +741,22 @@ void KX_GameObject::NodeSetLocalPosition(const MT_Point3& trans) void KX_GameObject::NodeSetLocalOrientation(const MT_Matrix3x3& rot) { - if (m_pPhysicsController1) + if (m_pPhysicsController1 && (!GetSGNode() || !GetSGNode()->GetSGParent())) { - m_pPhysicsController1->setOrientation(rot.getRotation()); + // see note above + m_pPhysicsController1->setOrientation(rot); } if (GetSGNode()) GetSGNode()->SetLocalOrientation(rot); - else - { - int i; - i=0; - } } void KX_GameObject::NodeSetLocalScale(const MT_Vector3& scale) { - if (m_pPhysicsController1) + if (m_pPhysicsController1 && (!GetSGNode() || !GetSGNode()->GetSGParent())) { + // see note above m_pPhysicsController1->setScaling(scale); } @@ -832,7 +843,7 @@ void KX_GameObject::Resume(void) } } -void KX_GameObject::Suspend(void) +void KX_GameObject::Suspend() { if ((!m_ignore_activity_culling) && (!m_suspended)) { @@ -875,6 +886,8 @@ PyMethodDef KX_GameObject::Methods[] = { {"getParent", (PyCFunction)KX_GameObject::sPyGetParent,METH_NOARGS}, {"setParent", (PyCFunction)KX_GameObject::sPySetParent,METH_O}, {"removeParent", (PyCFunction)KX_GameObject::sPyRemoveParent,METH_NOARGS}, + {"getChildren", (PyCFunction)KX_GameObject::sPyGetChildren,METH_NOARGS}, + {"getChildrenRecursive", (PyCFunction)KX_GameObject::sPyGetChildrenRecursive,METH_NOARGS}, {"getMesh", (PyCFunction)KX_GameObject::sPyGetMesh,METH_VARARGS}, {"getPhysicsId", (PyCFunction)KX_GameObject::sPyGetPhysicsId,METH_NOARGS}, {"getPropertyNames", (PyCFunction)KX_GameObject::sPyGetPropertyNames,METH_NOARGS}, @@ -1292,6 +1305,43 @@ PyObject* KX_GameObject::PyRemoveParent(PyObject* self) Py_RETURN_NONE; } + +static void walk_children(SG_Node* node, PyObject *list, bool recursive) +{ + NodeList& children = node->GetSGChildren(); + + for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit) + { + SG_Node* childnode = (*childit); + KX_GameObject* childobj = (KX_GameObject*)childnode->GetSGClientObject(); + if (childobj != NULL) // This is a GameObject + { + // add to the list + PyList_Append(list, (PyObject *)childobj); + } + + // if the childobj is NULL then this may be an inverse parent link + // so a non recursive search should still look down this node. + if (recursive || childobj==NULL) { + walk_children(childnode, list, recursive); + } + } +} + +PyObject* KX_GameObject::PyGetChildren(PyObject* self) +{ + PyObject * list = PyList_New(0); + walk_children(m_pSGNode, list, 0); + return list; +} + +PyObject* KX_GameObject::PyGetChildrenRecursive(PyObject* self) +{ + PyObject * list = PyList_New(0); + walk_children(m_pSGNode, list, 1); + return list; +} + PyObject* KX_GameObject::PyGetMesh(PyObject* self, PyObject* args, PyObject* kwds) @@ -1660,6 +1710,20 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, * --------------------------------------------------------------------- */ void KX_GameObject::Relink(GEN_Map *map_parameter) { - /* intentionally empty ? */ + // we will relink the sensors and actuators that use object references + // if the object is part of the replicated hierarchy, use the new + // object reference instead + SCA_SensorList& sensorlist = GetSensors(); + SCA_SensorList::iterator sit; + for (sit=sensorlist.begin(); sit != sensorlist.end(); sit++) + { + (*sit)->Relink(map_parameter); + } + SCA_ActuatorList& actuatorlist = GetActuators(); + SCA_ActuatorList::iterator ait; + for (ait=actuatorlist.begin(); ait != actuatorlist.end(); ait++) + { + (*ait)->Relink(map_parameter); + } } diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index ddbf863aa1a..6051cf850b5 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -47,6 +47,7 @@ #include "KX_Scene.h" #include "KX_KetsjiEngine.h" /* for m_anim_framerate */ #include "KX_IPhysicsController.h" /* for suspend/resume */ +#include "DNA_object_types.h" #define KX_OB_DYNAMIC 1 @@ -392,6 +393,13 @@ public: m_pBlenderObject = obj; } + bool IsDupliGroup() + { + return (m_pBlenderObject && + (m_pBlenderObject->transflag & OB_DUPLIGROUP) && + m_pBlenderObject->dup_group != NULL) ? true : false; + } + /** * Set the Scene graph node for this game object. * warning - it is your responsibility to make sure @@ -738,6 +746,8 @@ public: KX_PYMETHOD_NOARGS(KX_GameObject,GetParent); KX_PYMETHOD_O(KX_GameObject,SetParent); KX_PYMETHOD_NOARGS(KX_GameObject,RemoveParent); + KX_PYMETHOD_NOARGS(KX_GameObject,GetChildren); + KX_PYMETHOD_NOARGS(KX_GameObject,GetChildrenRecursive); KX_PYMETHOD(KX_GameObject,GetMesh); KX_PYMETHOD_NOARGS(KX_GameObject,GetPhysicsId); KX_PYMETHOD_NOARGS(KX_GameObject,GetPropertyNames); diff --git a/source/gameengine/Ketsji/KX_IPhysicsController.h b/source/gameengine/Ketsji/KX_IPhysicsController.h index 2ec66a883eb..ecfdb8c4275 100644 --- a/source/gameengine/Ketsji/KX_IPhysicsController.h +++ b/source/gameengine/Ketsji/KX_IPhysicsController.h @@ -71,14 +71,15 @@ public: virtual void resolveCombinedVelocities(float linvelX,float linvelY,float linvelZ,float angVelX,float angVelY,float angVelZ) = 0; virtual void getOrientation(MT_Quaternion& orn)=0; - virtual void setOrientation(const MT_Quaternion& orn)=0; + virtual void setOrientation(const MT_Matrix3x3& orn)=0; + //virtual void setOrientation(const MT_Quaternion& orn)=0; virtual void setPosition(const MT_Point3& pos)=0; virtual void setScaling(const MT_Vector3& scaling)=0; virtual MT_Scalar GetMass()=0; virtual MT_Vector3 getReactionForce()=0; virtual void setRigidBody(bool rigid)=0; - virtual void SuspendDynamics()=0; + virtual void SuspendDynamics(bool ghost=false)=0; virtual void RestoreDynamics()=0; virtual SG_Controller* GetReplica(class SG_Node* destnode)=0; diff --git a/source/gameengine/Ketsji/KX_OdePhysicsController.cpp b/source/gameengine/Ketsji/KX_OdePhysicsController.cpp index 4e45ce484e3..05feb11a2bc 100644 --- a/source/gameengine/Ketsji/KX_OdePhysicsController.cpp +++ b/source/gameengine/Ketsji/KX_OdePhysicsController.cpp @@ -133,8 +133,9 @@ void KX_OdePhysicsController::SetLinearVelocity(const MT_Vector3& lin_vel,bool l ODEPhysicsController::SetLinearVelocity(lin_vel[0],lin_vel[1],lin_vel[2],local); } -void KX_OdePhysicsController::setOrientation(const MT_Quaternion& orn) +void KX_OdePhysicsController::setOrientation(const MT_Matrix3x3& rot) { + MT_Quaternion orn = rot.getRotation(); ODEPhysicsController::setOrientation(orn[0],orn[1],orn[2],orn[3]); } @@ -177,7 +178,7 @@ void KX_OdePhysicsController::setRigidBody(bool rigid) } -void KX_OdePhysicsController::SuspendDynamics() +void KX_OdePhysicsController::SuspendDynamics(bool) { ODEPhysicsController::SuspendDynamics(); } diff --git a/source/gameengine/Ketsji/KX_OdePhysicsController.h b/source/gameengine/Ketsji/KX_OdePhysicsController.h index 07a0bee9775..18f9edc6835 100644 --- a/source/gameengine/Ketsji/KX_OdePhysicsController.h +++ b/source/gameengine/Ketsji/KX_OdePhysicsController.h @@ -67,14 +67,14 @@ public: virtual void SetLinearVelocity(const MT_Vector3& lin_vel,bool local); virtual void resolveCombinedVelocities(float linvelX,float linvelY,float linvelZ,float angVelX,float angVelY,float angVelZ); virtual void getOrientation(MT_Quaternion& orn); - virtual void setOrientation(const MT_Quaternion& orn); + virtual void setOrientation(const MT_Matrix3x3& orn); virtual void setPosition(const MT_Point3& pos); virtual void setScaling(const MT_Vector3& scaling); virtual MT_Scalar GetMass(); virtual MT_Vector3 getReactionForce(); virtual void setRigidBody(bool rigid); - virtual void SuspendDynamics(); + virtual void SuspendDynamics(bool); virtual void RestoreDynamics(); diff --git a/source/gameengine/Ketsji/KX_ParentActuator.cpp b/source/gameengine/Ketsji/KX_ParentActuator.cpp index 8b379bcd44f..fd1b56838e2 100644 --- a/source/gameengine/Ketsji/KX_ParentActuator.cpp +++ b/source/gameengine/Ketsji/KX_ParentActuator.cpp @@ -46,19 +46,22 @@ KX_ParentActuator::KX_ParentActuator(SCA_IObject *gameobj, int mode, - CValue *ob, + SCA_IObject *ob, PyTypeObject* T) : SCA_IActuator(gameobj, T), m_mode(mode), m_ob(ob) { + if (m_ob) + m_ob->RegisterActuator(this); } KX_ParentActuator::~KX_ParentActuator() { - /* intentionally empty */ + if (m_ob) + m_ob->UnregisterActuator(this); } @@ -73,6 +76,36 @@ CValue* KX_ParentActuator::GetReplica() return replica; } +void KX_ParentActuator::ProcessReplica() +{ + if (m_ob) + m_ob->RegisterActuator(this); + SCA_IActuator::ProcessReplica(); +} + + +bool KX_ParentActuator::UnlinkObject(SCA_IObject* clientobj) +{ + if (clientobj == m_ob) + { + // this object is being deleted, we cannot continue to track it. + m_ob = NULL; + return true; + } + return false; +} + +void KX_ParentActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_ob]; + if (h_obj) { + if (m_ob) + m_ob->UnregisterActuator(this); + m_ob = (SCA_IObject*)(*h_obj); + m_ob->RegisterActuator(this); + } +} + bool KX_ParentActuator::Update() @@ -87,7 +120,8 @@ bool KX_ParentActuator::Update() KX_Scene *scene = PHY_GetActiveScene(); switch (m_mode) { case KX_PARENT_SET: - obj->SetParent(scene, (KX_GameObject*)m_ob); + if (m_ob) + obj->SetParent(scene, (KX_GameObject*)m_ob); break; case KX_PARENT_REMOVE: obj->RemoveParent(scene); @@ -148,7 +182,11 @@ PyObject* KX_ParentActuator::PySetObject(PyObject* self, PyObject* args, PyObjec PyObject* gameobj; if (PyArg_ParseTuple(args, "O!", &KX_GameObject::Type, &gameobj)) { - m_ob = (CValue*)gameobj; + if (m_ob != NULL) + m_ob->UnregisterActuator(this); + m_ob = (SCA_IObject*)gameobj; + if (m_ob) + m_ob->RegisterActuator(this); Py_Return; } PyErr_Clear(); @@ -156,10 +194,13 @@ PyObject* KX_ParentActuator::PySetObject(PyObject* self, PyObject* args, PyObjec char* objectname; if (PyArg_ParseTuple(args, "s", &objectname)) { - CValue *object = (CValue*)SCA_ILogicBrick::m_sCurrentLogicManager->GetGameObjectByName(STR_String(objectname)); + SCA_IObject *object = (SCA_IObject*)SCA_ILogicBrick::m_sCurrentLogicManager->GetGameObjectByName(STR_String(objectname)); if(object) { + if (m_ob != NULL) + m_ob->UnregisterActuator(this); m_ob = object; + m_ob->RegisterActuator(this); Py_Return; } } diff --git a/source/gameengine/Ketsji/KX_ParentActuator.h b/source/gameengine/Ketsji/KX_ParentActuator.h index 86dcd4e6c12..93b07cd424b 100644 --- a/source/gameengine/Ketsji/KX_ParentActuator.h +++ b/source/gameengine/Ketsji/KX_ParentActuator.h @@ -47,7 +47,7 @@ class KX_ParentActuator : public SCA_IActuator int m_mode; /** Object to set as parent */ - CValue *m_ob; + SCA_IObject *m_ob; @@ -62,12 +62,15 @@ class KX_ParentActuator : public SCA_IActuator KX_ParentActuator(class SCA_IObject* gameobj, int mode, - CValue *ob, + SCA_IObject *ob, PyTypeObject* T=&Type); virtual ~KX_ParentActuator(); virtual bool Update(); virtual CValue* GetReplica(); + virtual void ProcessReplica(); + virtual void Relink(GEN_Map *obj_map); + virtual bool UnlinkObject(SCA_IObject* clientobj); /* --------------------------------------------------------------------- */ /* Python interface ---------------------------------------------------- */ diff --git a/source/gameengine/Ketsji/KX_RadarSensor.cpp b/source/gameengine/Ketsji/KX_RadarSensor.cpp index 9dab09f8f2a..de4979ac4c9 100644 --- a/source/gameengine/Ketsji/KX_RadarSensor.cpp +++ b/source/gameengine/Ketsji/KX_RadarSensor.cpp @@ -80,10 +80,7 @@ CValue* KX_RadarSensor::GetReplica() { KX_RadarSensor* replica = new KX_RadarSensor(*this); replica->m_colliders = new CListValue(); - replica->m_bCollision = false; - replica->m_bTriggered= false; - replica->m_hitObject = NULL; - replica->m_bLastTriggered = false; + replica->Init(); // this will copy properties and so on... CValue::AddDataToReplica(replica); @@ -179,8 +176,10 @@ void KX_RadarSensor::SynchronizeTransform() if (m_physCtrl) { - m_physCtrl->setPosition(trans.getOrigin().x(),trans.getOrigin().y(),trans.getOrigin().z()); - m_physCtrl->setOrientation(trans.getRotation().x(),trans.getRotation().y(),trans.getRotation().z(),trans.getRotation().w()); + MT_Quaternion orn = trans.getRotation(); + MT_Point3 pos = trans.getOrigin(); + m_physCtrl->setPosition(pos[0],pos[1],pos[2]); + m_physCtrl->setOrientation(orn[0],orn[1],orn[2],orn[3]); m_physCtrl->calcXform(); } diff --git a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp index e5ee4cbddf1..e36891b56f4 100644 --- a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp +++ b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp @@ -137,6 +137,17 @@ bool KX_SCA_AddObjectActuator::UnlinkObject(SCA_IObject* clientobj) return false; } +void KX_SCA_AddObjectActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_OriginalObject]; + if (h_obj) { + if (m_OriginalObject) + m_OriginalObject->UnregisterActuator(this); + m_OriginalObject = (SCA_IObject*)(*h_obj); + m_OriginalObject->RegisterActuator(this); + } +} + /* ------------------------------------------------------------------------- */ /* Python functions */ diff --git a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.h b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.h index 42123b94a68..1359f39278d 100644 --- a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.h +++ b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.h @@ -95,6 +95,9 @@ public: virtual bool UnlinkObject(SCA_IObject* clientobj); + virtual void + Relink(GEN_Map *obj_map); + virtual bool Update(); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 065800379d8..c374b9d6fd1 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -66,6 +66,8 @@ #include "SG_Controller.h" #include "SG_IObject.h" #include "SG_Tree.h" +#include "DNA_group_types.h" +#include "BKE_anim.h" #include "KX_SG_NodeRelationships.h" @@ -429,6 +431,11 @@ void KX_Scene::RemoveNodeDestructObject(class SG_IObject* node,class CValue* gam KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CValue* gameobj) { + // for group duplication, limit the duplication of the hierarchy to the + // objects that are part of the group. + if (!IsObjectInGroup(gameobj)) + return NULL; + KX_GameObject* orgobj = (KX_GameObject*)gameobj; KX_GameObject* newobj = (KX_GameObject*)orgobj->GetReplica(); m_map_gameobject_to_replica.insert(orgobj, newobj); @@ -506,6 +513,11 @@ KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CVal // hierarchy that's because first ALL bricks must exist in the new // replica of the hierarchy in order to make cross-links work properly // ! +// It is VERY important that the order of sensors and actuators in +// the replicated object is preserved: it is is used to reconnect the logic. +// This method is more robust then using the bricks name in case of complex +// group replication. The replication of logic bricks is done in +// SCA_IObject::ReParentLogic(), make sure it preserves the order of the bricks. void KX_Scene::ReplicateLogic(KX_GameObject* newobj) { // also relink the controller to sensors/actuators @@ -528,37 +540,38 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj) for (vector::iterator its = linkedsensors.begin();!(its==linkedsensors.end());its++) { SCA_ISensor* oldsensor = (*its); - STR_String name = oldsensor->GetName(); - //find this name in the list - SCA_ISensor* newsensor = newobj->FindSensor(name); + SCA_IObject* oldsensorobj = oldsensor->GetParent(); + SCA_IObject* newsensorobj = NULL; - if (newsensor) + // the original owner of the sensor has been replicated? + void **h_obj = m_map_gameobject_to_replica[oldsensorobj]; + if (h_obj) + newsensorobj = (SCA_IObject*)(*h_obj); + if (!newsensorobj) { - // relink this newsensor to the controller - m_logicmgr->RegisterToSensor(cont,newsensor); + // no, then the sensor points outside the hierachy, keep it the same + if (m_objectlist->SearchValue(oldsensorobj)) + // only replicate links that points to active objects + m_logicmgr->RegisterToSensor(cont,oldsensor); } else { - // it can be linked somewhere in the hierarchy or... - for (vector::iterator git = m_logicHierarchicalGameObjects.begin(); - !(git==m_logicHierarchicalGameObjects.end());++git) - { - newsensor = (*git)->FindSensor(name); - if (newsensor) - break; - } + // yes, then the new sensor has the same position + SCA_SensorList& sensorlist = oldsensorobj->GetSensors(); + SCA_SensorList::iterator sit; + SCA_ISensor* newsensor = NULL; + int sensorpos; - if (newsensor) - { - // relink this newsensor to the controller somewhere else within this - // hierarchy - m_logicmgr->RegisterToSensor(cont,newsensor); - } - else + for (sensorpos=0, sit=sensorlist.begin(); sit!=sensorlist.end(); sit++, sensorpos++) { - // must be an external sensor, so... - m_logicmgr->RegisterToSensor(cont,oldsensor); + if ((*sit) == oldsensor) + { + newsensor = newsensorobj->GetSensors().at(sensorpos); + break; + } } + assert(newsensor != NULL); + m_logicmgr->RegisterToSensor(cont,newsensor); } } @@ -566,38 +579,40 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj) for (vector::iterator ita = linkedactuators.begin();!(ita==linkedactuators.end());ita++) { SCA_IActuator* oldactuator = (*ita); - STR_String name = oldactuator->GetName(); - //find this name in the list - SCA_IActuator* newactuator = newobj->FindActuator(name); - if (newactuator) + SCA_IObject* oldactuatorobj = oldactuator->GetParent(); + SCA_IObject* newactuatorobj = NULL; + + // the original owner of the sensor has been replicated? + void **h_obj = m_map_gameobject_to_replica[oldactuatorobj]; + if (h_obj) + newactuatorobj = (SCA_IObject*)(*h_obj); + + if (!newactuatorobj) { - // relink this newsensor to the controller - m_logicmgr->RegisterToActuator(cont,newactuator); - newactuator->SetUeberExecutePriority(m_ueberExecutionPriority); + // no, then the sensor points outside the hierachy, keep it the same + if (m_objectlist->SearchValue(oldactuatorobj)) + // only replicate links that points to active objects + m_logicmgr->RegisterToActuator(cont,oldactuator); } else { - // it can be linked somewhere in the hierarchy or... - for (vector::iterator git = m_logicHierarchicalGameObjects.begin(); - !(git==m_logicHierarchicalGameObjects.end());++git) - { - newactuator= (*git)->FindActuator(name); - if (newactuator) - break; - } + // yes, then the new sensor has the same position + SCA_ActuatorList& actuatorlist = oldactuatorobj->GetActuators(); + SCA_ActuatorList::iterator ait; + SCA_IActuator* newactuator = NULL; + int actuatorpos; - if (newactuator) + for (actuatorpos=0, ait=actuatorlist.begin(); ait!=actuatorlist.end(); ait++, actuatorpos++) { - // relink this actuator to the controller somewhere else within this - // hierarchy - m_logicmgr->RegisterToActuator(cont,newactuator); - newactuator->SetUeberExecutePriority(m_ueberExecutionPriority); - } - else - { - // must be an external actuator, so... - m_logicmgr->RegisterToActuator(cont,oldactuator); + if ((*ait) == oldactuator) + { + newactuator = newactuatorobj->GetActuators().at(actuatorpos); + break; + } } + assert(newactuator != NULL); + m_logicmgr->RegisterToActuator(cont,newactuator); + newactuator->SetUeberExecutePriority(m_ueberExecutionPriority); } } } @@ -605,6 +620,150 @@ void KX_Scene::ReplicateLogic(KX_GameObject* newobj) newobj->ResetState(); } +void KX_Scene::DupliGroupRecurse(CValue* obj, int level) +{ + KX_GameObject* groupobj = (KX_GameObject*) obj; + KX_GameObject* replica; + KX_GameObject* gameobj; + Object* blgroupobj = groupobj->GetBlenderObject(); + Group* group; + GroupObject *go; + vector duplilist; + + if (!groupobj->IsDupliGroup() || + level>MAX_DUPLI_RECUR) + return; + + // we will add one group at a time + m_logicHierarchicalGameObjects.clear(); + m_map_gameobject_to_replica.clear(); + m_ueberExecutionPriority++; + // for groups will do something special: + // we will force the creation of objects to those in the group only + // Again, this is match what Blender is doing (it doesn't care of parent relationship) + m_groupGameObjects.clear(); + + group = blgroupobj->dup_group; + for(go=(GroupObject*)group->gobject.first; go; go=(GroupObject*)go->next) + { + Object* blenderobj = go->ob; + if (blgroupobj == blenderobj) + // this check is also in group_duplilist() + continue; + gameobj = m_sceneConverter->FindGameObject(blenderobj); + if (gameobj == NULL) + { + // this object has not been converted!!! + // Should not happen as dupli group are created automatically + continue; + } + if (blenderobj->lay & group->layer==0) + { + // object is not visible in the 3D view, will not be instantiated + continue; + } + m_groupGameObjects.insert(gameobj); + } + + set::iterator oit; + for (oit=m_groupGameObjects.begin(); oit != m_groupGameObjects.end(); oit++) + { + gameobj = (KX_GameObject*)(*oit); + if (gameobj->GetParent() != NULL) + { + // this object is not a top parent. Either it is the child of another + // object in the group and it will be added automatically when the parent + // is added. Or it is the child of an object outside the group and the group + // is inconsistent, skip it anyway + continue; + } + replica = (KX_GameObject*) AddNodeReplicaObject(NULL,gameobj); + // add to 'rootparent' list (this is the list of top hierarchy objects, updated each frame) + m_parentlist->Add(replica->AddRef()); + + // recurse replication into children nodes + NodeList& children = gameobj->GetSGNode()->GetSGChildren(); + + replica->GetSGNode()->ClearSGChildren(); + for (NodeList::iterator childit = children.begin();!(childit==children.end());++childit) + { + SG_Node* orgnode = (*childit); + SG_Node* childreplicanode = orgnode->GetSGReplica(); + if (childreplicanode) + replica->GetSGNode()->AddChild(childreplicanode); + } + // don't replicate logic now: we assume that the objects in the group can have + // logic relationship, even outside parent relationship + // In order to match 3D view, the position of groupobj is used as a + // transformation matrix instead of the new position. This means that + // the group reference point is 0,0,0 + + // get the rootnode's scale + MT_Vector3 newscale = groupobj->NodeGetWorldScaling(); + // set the replica's relative scale with the rootnode's scale + replica->NodeSetRelativeScale(newscale); + + MT_Matrix3x3 newori = groupobj->NodeGetWorldOrientation() * gameobj->NodeGetWorldOrientation(); + replica->NodeSetLocalOrientation(newori); + + MT_Point3 newpos = groupobj->NodeGetWorldPosition() + + newscale*(groupobj->NodeGetWorldOrientation() * gameobj->NodeGetWorldPosition()); + replica->NodeSetLocalPosition(newpos); + + if (replica->GetPhysicsController()) + { + // not required, already done in NodeSetLocalOrientation.. + //replica->GetPhysicsController()->setPosition(newpos); + //replica->GetPhysicsController()->setOrientation(newori.getRotation()); + // Scaling has been set relatively hereabove, this does not + // set the scaling of the controller. I don't know why it's just the + // relative scale and not the full scale that has to be put here... + replica->GetPhysicsController()->setScaling(newscale); + } + + replica->GetSGNode()->UpdateWorldData(0); + replica->GetSGNode()->SetBBox(gameobj->GetSGNode()->BBox()); + replica->GetSGNode()->SetRadius(gameobj->GetSGNode()->Radius()); + // done with replica + replica->Release(); + } + + // the logic must be replicated first because we need + // the new logic bricks before relinking + vector::iterator git; + for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) + { + (*git)->ReParentLogic(); + } + + // relink any pointers as necessary, sort of a temporary solution + for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) + { + // this will also relink the actuator to objects within the hierarchy + (*git)->Relink(&m_map_gameobject_to_replica); + // add the object in the layer of the parent + (*git)->SetLayer(groupobj->GetLayer()); + } + + // replicate crosslinks etc. between logic bricks + for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) + { + ReplicateLogic((*git)); + } + + // now look if object in the hierarchy have dupli group and recurse + for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) + { + if ((*git) != groupobj && (*git)->IsDupliGroup()) + // can't instantiate group immediately as it destroys m_logicHierarchicalGameObjects + duplilist.push_back((*git)); + } + + for (git = duplilist.begin(); !(git == duplilist.end()); ++git) + { + DupliGroupRecurse((*git), level+1); + } +} SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, @@ -614,6 +773,7 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, m_logicHierarchicalGameObjects.clear(); m_map_gameobject_to_replica.clear(); + m_groupGameObjects.clear(); // todo: place a timebomb in the object, for temporarily objects :) // lifespan of zero means 'this object lives forever' @@ -647,24 +807,26 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, { SG_Node* orgnode = (*childit); SG_Node* childreplicanode = orgnode->GetSGReplica(); - replica->GetSGNode()->AddChild(childreplicanode); + if (childreplicanode) + replica->GetSGNode()->AddChild(childreplicanode); } - // relink any pointers as necessary, sort of a temporary solution + // now replicate logic vector::iterator git; for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) { + (*git)->ReParentLogic(); + } + + // relink any pointers as necessary, sort of a temporary solution + for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) + { + // this will also relink the actuators in the hierarchy (*git)->Relink(&m_map_gameobject_to_replica); // add the object in the layer of the parent (*git)->SetLayer(parentobj->GetLayer()); } - // now replicate logic - for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) - { - (*git)->ReParentLogic(); - } - // replicate crosslinks etc. between logic bricks for (git = m_logicHierarchicalGameObjects.begin();!(git==m_logicHierarchicalGameObjects.end());++git) { @@ -685,8 +847,9 @@ SCA_IObject* KX_Scene::AddReplicaObject(class CValue* originalobject, if (replica->GetPhysicsController()) { - replica->GetPhysicsController()->setPosition(newpos); - replica->GetPhysicsController()->setOrientation(newori.getRotation()); + // not needed, already done in NodeSetLocalPosition() + //replica->GetPhysicsController()->setPosition(newpos); + //replica->GetPhysicsController()->setOrientation(newori.getRotation()); replica->GetPhysicsController()->setScaling(newscale); } diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index 28dee1b5893..80a2abe287a 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -216,6 +216,16 @@ protected: */ std::vector m_logicHierarchicalGameObjects; + /** + * This temporary variable will contain the list of + * object that can be added during group instantiation. + * objects outside this list will not be added (can + * happen with children that are outside the group). + * Used in AddReplicaObject. If the list is empty, it + * means don't care. + */ + std::set m_groupGameObjects; + /** * Pointer to system variable passed in in constructor * only used in constructor so we do not need to keep it @@ -291,6 +301,12 @@ public: * Update all transforms according to the scenegraph. */ void UpdateParents(double curtime); + void DupliGroupRecurse(CValue* gameobj, int level); + bool IsObjectInGroup(CValue* gameobj) + { + return (m_groupGameObjects.empty() || + m_groupGameObjects.find(gameobj) != m_groupGameObjects.end()); + } SCA_IObject* AddReplicaObject(CValue* gameobj, CValue* locationobj, int lifespan=0); diff --git a/source/gameengine/Ketsji/KX_SceneActuator.cpp b/source/gameengine/Ketsji/KX_SceneActuator.cpp index 8f7cffd506f..d6164dc812a 100644 --- a/source/gameengine/Ketsji/KX_SceneActuator.cpp +++ b/source/gameengine/Ketsji/KX_SceneActuator.cpp @@ -58,13 +58,16 @@ KX_SceneActuator::KX_SceneActuator(SCA_IObject *gameobj, m_KetsjiEngine=ketsjiEngine; m_camera = camera; m_nextSceneName = nextSceneName; + if (m_camera) + m_camera->RegisterActuator(this); } /* End of constructor */ KX_SceneActuator::~KX_SceneActuator() { - // there's nothing to be done here, really.... + if (m_camera) + m_camera->UnregisterActuator(this); } /* end of destructor */ @@ -79,6 +82,34 @@ CValue* KX_SceneActuator::GetReplica() return replica; } +void KX_SceneActuator::ProcessReplica() +{ + if (m_camera) + m_camera->RegisterActuator(this); + SCA_IActuator::ProcessReplica(); +} + +bool KX_SceneActuator::UnlinkObject(SCA_IObject* clientobj) +{ + if (clientobj == (SCA_IObject*)m_camera) + { + // this object is being deleted, we cannot continue to track it. + m_camera = NULL; + return true; + } + return false; +} + +void KX_SceneActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_camera]; + if (h_obj) { + if (m_camera) + m_camera->UnregisterActuator(this); + m_camera = (KX_Camera*)(*h_obj); + m_camera->RegisterActuator(this); + } +} bool KX_SceneActuator::Update() @@ -332,7 +363,11 @@ PyObject* KX_SceneActuator::PySetCamera(PyObject* self, PyObject *cam; if (PyArg_ParseTuple(args, "O!", &KX_Camera::Type, &cam)) { + if (m_camera) + m_camera->UnregisterActuator(this); m_camera = (KX_Camera*) cam; + if (m_camera) + m_camera->RegisterActuator(this); Py_Return; } PyErr_Clear(); @@ -345,7 +380,13 @@ PyObject* KX_SceneActuator::PySetCamera(PyObject* self, } KX_Camera *camOb = FindCamera(camName); - if (camOb) m_camera = camOb; + if (camOb) + { + if (m_camera) + m_camera->UnregisterActuator(this); + m_camera = camOb; + m_camera->RegisterActuator(this); + } Py_Return; } diff --git a/source/gameengine/Ketsji/KX_SceneActuator.h b/source/gameengine/Ketsji/KX_SceneActuator.h index cfc79b93f8e..55aaf629d7c 100644 --- a/source/gameengine/Ketsji/KX_SceneActuator.h +++ b/source/gameengine/Ketsji/KX_SceneActuator.h @@ -82,6 +82,9 @@ class KX_SceneActuator : public SCA_IActuator virtual ~KX_SceneActuator(); virtual CValue* GetReplica(); + virtual void ProcessReplica(); + virtual bool UnlinkObject(SCA_IObject* clientobj); + virtual void Relink(GEN_Map *obj_map); virtual bool Update(); diff --git a/source/gameengine/Ketsji/KX_SumoPhysicsController.cpp b/source/gameengine/Ketsji/KX_SumoPhysicsController.cpp index 6ea5461dbaa..4032a795ce3 100644 --- a/source/gameengine/Ketsji/KX_SumoPhysicsController.cpp +++ b/source/gameengine/Ketsji/KX_SumoPhysicsController.cpp @@ -101,7 +101,7 @@ void KX_SumoPhysicsController::SetSumoTransform(bool nondynaonly) } -void KX_SumoPhysicsController::SuspendDynamics() +void KX_SumoPhysicsController::SuspendDynamics(bool) { SumoPhysicsController::SuspendDynamics(); } @@ -170,8 +170,9 @@ void KX_SumoPhysicsController::setMargin(float collisionMargin) } -void KX_SumoPhysicsController::setOrientation(const MT_Quaternion& orn) +void KX_SumoPhysicsController::setOrientation(const MT_Matrix3x3& rot) { + MT_Quaternion orn = rot.getRotation(); SumoPhysicsController::setOrientation( orn[0],orn[1],orn[2],orn[3]); diff --git a/source/gameengine/Ketsji/KX_SumoPhysicsController.h b/source/gameengine/Ketsji/KX_SumoPhysicsController.h index 8c061ae4056..1dd930bf3d9 100644 --- a/source/gameengine/Ketsji/KX_SumoPhysicsController.h +++ b/source/gameengine/Ketsji/KX_SumoPhysicsController.h @@ -76,10 +76,10 @@ public: void resolveCombinedVelocities(float linvelX,float linvelY,float linvelZ,float angVelX,float angVelY,float angVelZ); - void SuspendDynamics(); + void SuspendDynamics(bool); void RestoreDynamics(); virtual void getOrientation(MT_Quaternion& orn); - virtual void setOrientation(const MT_Quaternion& orn); + virtual void setOrientation(const MT_Matrix3x3& orn); virtual void setPosition(const MT_Point3& pos); virtual void setScaling(const MT_Vector3& scaling); diff --git a/source/gameengine/Ketsji/KX_TrackToActuator.cpp b/source/gameengine/Ketsji/KX_TrackToActuator.cpp index 731a610c2eb..c580aa4d4e5 100644 --- a/source/gameengine/Ketsji/KX_TrackToActuator.cpp +++ b/source/gameengine/Ketsji/KX_TrackToActuator.cpp @@ -210,6 +210,18 @@ bool KX_TrackToActuator::UnlinkObject(SCA_IObject* clientobj) return false; } +void KX_TrackToActuator::Relink(GEN_Map *obj_map) +{ + void **h_obj = (*obj_map)[m_object]; + if (h_obj) { + if (m_object) + m_object->UnregisterActuator(this); + m_object = (SCA_IObject*)(*h_obj); + m_object->RegisterActuator(this); + } +} + + bool KX_TrackToActuator::Update(double curtime, bool frame) { bool result = false; diff --git a/source/gameengine/Ketsji/KX_TrackToActuator.h b/source/gameengine/Ketsji/KX_TrackToActuator.h index a03aa115baa..1d1cf46d21b 100644 --- a/source/gameengine/Ketsji/KX_TrackToActuator.h +++ b/source/gameengine/Ketsji/KX_TrackToActuator.h @@ -68,6 +68,7 @@ class KX_TrackToActuator : public SCA_IActuator virtual void ProcessReplica(); virtual bool UnlinkObject(SCA_IObject* clientobj); + virtual void Relink(GEN_Map *obj_map); virtual bool Update(double curtime, bool frame); /* Python part */ diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp index e444c4c73be..5c70b071661 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.cpp @@ -337,12 +337,33 @@ void CcdPhysicsController::setOrientation(float quatImag0,float quatImag1,float { m_body->setCollisionFlags(m_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); } - - m_MotionState->setWorldOrientation(quatImag0,quatImag1,quatImag2,quatReal); + // not required + //m_MotionState->setWorldOrientation(quatImag0,quatImag1,quatImag2,quatReal); btTransform xform = m_body->getCenterOfMassTransform(); xform.setRotation(btQuaternion(quatImag0,quatImag1,quatImag2,quatReal)); m_body->setCenterOfMassTransform(xform); - m_bulletMotionState->setWorldTransform(xform); + // not required + //m_bulletMotionState->setWorldTransform(xform); + } + +} + +void CcdPhysicsController::setWorldOrientation(const btMatrix3x3& orn) +{ + if (m_body) + { + m_body->activate(true); + if (m_body->isStaticObject()) + { + m_body->setCollisionFlags(m_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); + } + // not required + //m_MotionState->setWorldOrientation(quatImag0,quatImag1,quatImag2,quatReal); + btTransform xform = m_body->getCenterOfMassTransform(); + xform.setBasis(orn); + m_body->setCenterOfMassTransform(xform); + // not required + //m_bulletMotionState->setWorldTransform(xform); } } @@ -356,12 +377,13 @@ void CcdPhysicsController::setPosition(float posX,float posY,float posZ) { m_body->setCollisionFlags(m_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); } - - m_MotionState->setWorldPosition(posX,posY,posZ); + // not required, this function is only used to update the physic controller + //m_MotionState->setWorldPosition(posX,posY,posZ); btTransform xform = m_body->getCenterOfMassTransform(); xform.setOrigin(btVector3(posX,posY,posZ)); m_body->setCenterOfMassTransform(xform); - m_bulletMotionState->setWorldTransform(xform); + // not required + //m_bulletMotionState->setWorldTransform(xform); } diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsController.h b/source/gameengine/Physics/Bullet/CcdPhysicsController.h index 37fa465351f..64f1876e199 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsController.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsController.h @@ -106,6 +106,7 @@ class CcdPhysicsController : public PHY_IPhysicsController btRigidBody* m_body; class PHY_IMotionState* m_MotionState; btMotionState* m_bulletMotionState; + friend class CcdPhysicsEnvironment; // needed when updating the controller void* m_newClientInfo; @@ -115,6 +116,9 @@ class CcdPhysicsController : public PHY_IPhysicsController void CreateRigidbody(); + protected: + void setWorldOrientation(const btMatrix3x3& mat); + public: int m_collisionDelay; @@ -194,7 +198,6 @@ class CcdPhysicsController : public PHY_IPhysicsController return m_cci.m_collisionFilterMask; } - virtual void calcXform() {} ; virtual void SetMargin(float margin) {}; virtual float GetMargin() const {return 0.f;}; diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp index c9d346e316d..b773f40650b 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp @@ -466,6 +466,38 @@ void CcdPhysicsEnvironment::removeCcdPhysicsController(CcdPhysicsController* ctr } +void CcdPhysicsEnvironment::updateCcdPhysicsController(CcdPhysicsController* ctrl, btScalar newMass, int newCollisionFlags, short int newCollisionGroup, short int newCollisionMask) +{ + // this function is used when the collisionning group of a controller is changed + // remove and add the collistioning object + btRigidBody* body = ctrl->GetRigidBody(); + btVector3 inertia; + + m_dynamicsWorld->removeCollisionObject(body); + body->setCollisionFlags(newCollisionFlags); + body->getCollisionShape()->calculateLocalInertia(newMass, inertia); + body->setMassProps(newMass, inertia); + m_dynamicsWorld->addCollisionObject(body, newCollisionGroup, newCollisionMask); + // to avoid nasty interaction, we must update the property of the controller as well + ctrl->m_cci.m_mass = newMass; + ctrl->m_cci.m_collisionFilterGroup = newCollisionGroup; + ctrl->m_cci.m_collisionFilterMask = newCollisionMask; + ctrl->m_cci.m_collisionFlags = newCollisionFlags; +} + +void CcdPhysicsEnvironment::enableCcdPhysicsController(CcdPhysicsController* ctrl) +{ + std::vector::iterator i = + std::find(m_controllers.begin(), m_controllers.end(), ctrl); + if (i == m_controllers.end()) + { + btRigidBody* body = ctrl->GetRigidBody(); + m_dynamicsWorld->addCollisionObject(body, + ctrl->GetCollisionFilterGroup(), ctrl->GetCollisionFilterMask()); + } +} + + void CcdPhysicsEnvironment::beginFrame() { diff --git a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h index 9f14cf6cbef..453749b27b3 100644 --- a/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h +++ b/source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h @@ -183,6 +183,15 @@ protected: void removeCcdPhysicsController(CcdPhysicsController* ctrl); + void updateCcdPhysicsController(CcdPhysicsController* ctrl, btScalar newMass, int newCollisionFlags, short int newCollisionGroup, short int newCollisionMask); + + void disableCcdPhysicsController(CcdPhysicsController* ctrl) + { + removeCcdPhysicsController(ctrl); + } + + void enableCcdPhysicsController(CcdPhysicsController* ctrl); + btBroadphaseInterface* getBroadphase(); diff --git a/source/gameengine/PyDoc/KX_GameObject.py b/source/gameengine/PyDoc/KX_GameObject.py index 37c188b7f22..8f17cf26f15 100644 --- a/source/gameengine/PyDoc/KX_GameObject.py +++ b/source/gameengine/PyDoc/KX_GameObject.py @@ -214,6 +214,18 @@ class KX_GameObject: """ Removes this objects parent. """ + def getChildren(): + """ + Return a list of immediate children of this object. + @rtype: list + @return: a list of all this objects children. + """ + def getChildrenRecursive(): + """ + Return a list of children of this object, including all their childrens children. + @rtype: list + @return: a list of all this objects children recursivly. + """ def getMesh(mesh): """ Gets the mesh object for this object. diff --git a/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp b/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp index 958fead33ce..ef206332057 100644 --- a/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp +++ b/source/gameengine/Rasterizer/RAS_2DFilterManager.cpp @@ -43,6 +43,7 @@ #include "STR_String.h" #include "RAS_ICanvas.h" +#include "RAS_Rect.h" #include "RAS_2DFilterManager.h" #include @@ -158,7 +159,8 @@ void RAS_2DFilterManager::AnalyseShader(int passindex, vector& propN texflag[passindex] = 0; if(glGetUniformLocationARB(m_filters[passindex], "bgl_DepthTexture") != -1) { - texflag[passindex] |= 0x1; + if(GLEW_ARB_depth_texture) + texflag[passindex] |= 0x1; } if(glGetUniformLocationARB(m_filters[passindex], "bgl_LuminanceTexture") != -1) { @@ -261,7 +263,7 @@ void RAS_2DFilterManager::SetupTextures(bool depth, bool luminance) glGenTextures(1, (GLuint*)&texname[0]); glBindTexture(GL_TEXTURE_2D, texname[0]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texturewidth, textureheight, 0, GL_RGB, + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texturewidth, textureheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -293,10 +295,13 @@ void RAS_2DFilterManager::SetupTextures(bool depth, bool luminance) } } -void RAS_2DFilterManager::UpdateOffsetMatrix(int width, int height) +void RAS_2DFilterManager::UpdateOffsetMatrix(RAS_ICanvas* canvas) { - canvaswidth = texturewidth = width; - canvasheight = textureheight = height; + RAS_Rect canvas_rect = canvas->GetWindowArea(); + canvaswidth = canvas->GetWidth(); + canvasheight = canvas->GetHeight(); + texturewidth = canvaswidth + canvas_rect.GetLeft(); + textureheight = canvasheight + canvas_rect.GetBottom(); GLint i,j; i = 0; @@ -352,7 +357,7 @@ void RAS_2DFilterManager::RenderFilters(RAS_ICanvas* canvas) if(canvaswidth != canvas->GetWidth() || canvasheight != canvas->GetHeight()) { - UpdateOffsetMatrix(canvas->GetWidth(), canvas->GetHeight()); + UpdateOffsetMatrix(canvas); SetupTextures(need_depth, need_luminance); } GLuint viewport[4]={0}; @@ -360,19 +365,21 @@ void RAS_2DFilterManager::RenderFilters(RAS_ICanvas* canvas) if(need_depth){ glActiveTextureARB(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texname[1]); - glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT, 0,0, texturewidth,textureheight, 0); + glCopyTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT, 0, 0, texturewidth,textureheight, 0); } if(need_luminance){ glActiveTextureARB(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, texname[2]); - glCopyTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16, 0,0, texturewidth,textureheight, 0); + glCopyTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16, 0, 0 , texturewidth,textureheight, 0); } glGetIntegerv(GL_VIEWPORT,(GLint *)viewport); - glViewport(0, 0, texturewidth, textureheight); + glViewport(0,0, texturewidth, textureheight); glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); diff --git a/source/gameengine/Rasterizer/RAS_2DFilterManager.h b/source/gameengine/Rasterizer/RAS_2DFilterManager.h index 9d8326b96de..f5998e1f093 100644 --- a/source/gameengine/Rasterizer/RAS_2DFilterManager.h +++ b/source/gameengine/Rasterizer/RAS_2DFilterManager.h @@ -42,7 +42,7 @@ private: void SetupTextures(bool depth, bool luminance); void FreeTextures(); - void UpdateOffsetMatrix(int width, int height); + void UpdateOffsetMatrix(RAS_ICanvas* canvas); float textureoffsets[18]; float view[4]; diff --git a/source/gameengine/Rasterizer/RAS_ICanvas.h b/source/gameengine/Rasterizer/RAS_ICanvas.h index d799dc9c9bb..f3f817a943d 100644 --- a/source/gameengine/Rasterizer/RAS_ICanvas.h +++ b/source/gameengine/Rasterizer/RAS_ICanvas.h @@ -136,6 +136,14 @@ public: GetDisplayArea( ) = 0; + /** + * Used to get canvas area within blender. + */ + virtual + RAS_Rect & + GetWindowArea( + ) = 0; + /** * Set the visible vieport */ diff --git a/source/gameengine/SceneGraph/SG_IObject.cpp b/source/gameengine/SceneGraph/SG_IObject.cpp index c347bbc6d9a..d0bdac5c8f0 100644 --- a/source/gameengine/SceneGraph/SG_IObject.cpp +++ b/source/gameengine/SceneGraph/SG_IObject.cpp @@ -104,7 +104,7 @@ SetSGClientObject( } - void + bool SG_IObject:: ActivateReplicationCallback( SG_IObject *replica @@ -112,8 +112,10 @@ ActivateReplicationCallback( if (m_callbacks.m_replicafunc) { // Call client provided replication func - m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo); + if (m_callbacks.m_replicafunc(replica,m_SGclientObject,m_SGclientInfo) == NULL) + return false; } + return true; }; void diff --git a/source/gameengine/SceneGraph/SG_IObject.h b/source/gameengine/SceneGraph/SG_IObject.h index 438ab48c556..7f6bdfbbb1c 100644 --- a/source/gameengine/SceneGraph/SG_IObject.h +++ b/source/gameengine/SceneGraph/SG_IObject.h @@ -202,7 +202,7 @@ public: protected : - void + bool ActivateReplicationCallback( SG_IObject *replica ); diff --git a/source/gameengine/SceneGraph/SG_Node.cpp b/source/gameengine/SceneGraph/SG_Node.cpp index 4e90d7c4653..8de7ac83477 100644 --- a/source/gameengine/SceneGraph/SG_Node.cpp +++ b/source/gameengine/SceneGraph/SG_Node.cpp @@ -68,7 +68,7 @@ SG_Node* SG_Node::GetSGReplica() SG_Node* replica = new SG_Node(*this); if (replica == NULL) return NULL; - ProcessSGReplica(replica); + ProcessSGReplica(&replica); return replica; } @@ -76,25 +76,42 @@ SG_Node* SG_Node::GetSGReplica() void SG_Node:: ProcessSGReplica( - SG_Node* replica + SG_Node** replica ){ // Apply the replication call back function. - ActivateReplicationCallback(replica); + if (!ActivateReplicationCallback(*replica)) + { + delete (*replica); + *replica = NULL; + return; + } // clear the replica node of it's parent. - static_cast(replica)->m_SGparent = NULL; + static_cast(*replica)->m_SGparent = NULL; if (m_children.begin() != m_children.end()) { // if this node has children, the replica has too, so clear and clone children - replica->ClearSGChildren(); + (*replica)->ClearSGChildren(); NodeList::iterator childit; for (childit = m_children.begin();childit!=m_children.end();++childit) { - replica->AddChild((*childit)->GetSGReplica()); + SG_Node* childnode = (*childit)->GetSGReplica(); + if (childnode) + (*replica)->AddChild(childnode); } } + // Nodes without children and without client object are + // not worth to keep, they will just take up CPU + // This can happen in partial replication of hierarchy + // during group duplication. + if ((*replica)->m_children.empty() && + (*replica)->GetSGClientObject() == NULL) + { + delete (*replica); + *replica = NULL; + } } diff --git a/source/gameengine/SceneGraph/SG_Node.h b/source/gameengine/SceneGraph/SG_Node.h index f86e3046d93..ffaaad861e2 100644 --- a/source/gameengine/SceneGraph/SG_Node.h +++ b/source/gameengine/SceneGraph/SG_Node.h @@ -205,7 +205,7 @@ private: void ProcessSGReplica( - SG_Node* replica + SG_Node** replica ); /** -- cgit v1.2.3