Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Susano Pinto <andresusanopinto@gmail.com>2008-07-22 16:02:57 +0400
committerAndre Susano Pinto <andresusanopinto@gmail.com>2008-07-22 16:02:57 +0400
commitaec6b78deda32d7aa1631ec76efda5bcea83a8c9 (patch)
tree789a1bf4ac15a3793fbe88874853282cb97f1083
parent06f67dd53178d3729f9b30d018d1a0715287b825 (diff)
svn merge -r 15590:15688 https://svn.blender.org/svnroot/bf-blender/trunk/blender
-rw-r--r--source/blender/blenkernel/BKE_global.h2
-rw-r--r--source/blender/blenkernel/intern/collision.c3
-rw-r--r--source/blender/blenkernel/intern/lattice.c5
-rw-r--r--source/blender/blenkernel/intern/particle_system.c5
-rw-r--r--source/blender/blenlib/intern/arithb.c1
-rw-r--r--source/blender/blenloader/intern/readfile.c64
-rw-r--r--source/blender/blenloader/intern/writefile.c34
-rw-r--r--source/blender/include/BDR_drawaction.h3
-rw-r--r--source/blender/include/BDR_gpencil.h76
-rw-r--r--source/blender/include/BIF_drawgpencil.h44
-rw-r--r--source/blender/include/BIF_editaction.h28
-rw-r--r--source/blender/include/BIF_space.h8
-rw-r--r--source/blender/include/BSE_editaction_types.h6
-rw-r--r--source/blender/include/transform.h1
-rw-r--r--source/blender/makesdna/DNA_action_types.h20
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h141
-rw-r--r--source/blender/makesdna/DNA_space_types.h8
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h15
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/python/api2_2x/Blender.c2
-rw-r--r--source/blender/python/api2_2x/Library.c69
-rw-r--r--source/blender/python/api2_2x/Particle.c472
-rw-r--r--source/blender/python/api2_2x/doc/LibData.py42
-rw-r--r--source/blender/python/api2_2x/doc/Particle.py229
-rw-r--r--source/blender/python/api2_2x/doc/Render.py4
-rw-r--r--source/blender/python/api2_2x/sceneRender.c2
-rw-r--r--source/blender/render/intern/source/rendercore.c27
-rw-r--r--source/blender/render/intern/source/shadeoutput.c2
-rw-r--r--source/blender/src/drawaction.c124
-rw-r--r--source/blender/src/drawgpencil.c653
-rw-r--r--source/blender/src/drawnode.c77
-rw-r--r--source/blender/src/drawobject.c5
-rw-r--r--source/blender/src/drawseq.c97
-rw-r--r--source/blender/src/drawview.c80
-rw-r--r--source/blender/src/editaction.c396
-rw-r--r--source/blender/src/editaction_gpencil.c549
-rw-r--r--source/blender/src/editmesh.c11
-rw-r--r--source/blender/src/editnode.c37
-rw-r--r--source/blender/src/editobject.c1
-rw-r--r--source/blender/src/gpencil.c1290
-rw-r--r--source/blender/src/header_action.c377
-rw-r--r--source/blender/src/header_node.c10
-rw-r--r--source/blender/src/header_seq.c13
-rw-r--r--source/blender/src/header_view3d.c4
-rw-r--r--source/blender/src/interface.c7
-rw-r--r--source/blender/src/space.c39
-rw-r--r--source/blender/src/transform_conversions.c223
-rw-r--r--source/blender/src/transform_generics.c9
-rw-r--r--source/blender/src/usiblender.c2
-rw-r--r--source/gameengine/BlenderRoutines/KX_BlenderCanvas.cpp12
-rw-r--r--source/gameengine/BlenderRoutines/KX_BlenderCanvas.h5
-rw-r--r--source/gameengine/Converter/BL_BlenderDataConversion.cpp243
-rw-r--r--source/gameengine/Converter/BL_DeformableGameObject.h1
-rw-r--r--source/gameengine/Converter/BL_ShapeDeformer.cpp7
-rw-r--r--source/gameengine/Converter/BL_SkinDeformer.cpp48
-rw-r--r--source/gameengine/Converter/BL_SkinDeformer.h6
-rw-r--r--source/gameengine/Converter/KX_ConvertActuators.cpp2
-rw-r--r--source/gameengine/GameLogic/SCA_ILogicBrick.cpp5
-rw-r--r--source/gameengine/GameLogic/SCA_ILogicBrick.h3
-rw-r--r--source/gameengine/GameLogic/SCA_IObject.cpp2
-rw-r--r--source/gameengine/GameLogic/SCA_PropertyActuator.cpp33
-rw-r--r--source/gameengine/GameLogic/SCA_PropertyActuator.h8
-rw-r--r--source/gameengine/GamePlayer/common/GPC_Canvas.h6
-rw-r--r--source/gameengine/Ketsji/KX_BulletPhysicsController.cpp40
-rw-r--r--source/gameengine/Ketsji/KX_BulletPhysicsController.h9
-rw-r--r--source/gameengine/Ketsji/KX_CameraActuator.cpp62
-rw-r--r--source/gameengine/Ketsji/KX_CameraActuator.h8
-rw-r--r--source/gameengine/Ketsji/KX_ConstraintActuator.cpp6
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp86
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h10
-rw-r--r--source/gameengine/Ketsji/KX_IPhysicsController.h5
-rw-r--r--source/gameengine/Ketsji/KX_OdePhysicsController.cpp5
-rw-r--r--source/gameengine/Ketsji/KX_OdePhysicsController.h4
-rw-r--r--source/gameengine/Ketsji/KX_ParentActuator.cpp51
-rw-r--r--source/gameengine/Ketsji/KX_ParentActuator.h7
-rw-r--r--source/gameengine/Ketsji/KX_RadarSensor.cpp11
-rw-r--r--source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp11
-rw-r--r--source/gameengine/Ketsji/KX_SCA_AddObjectActuator.h3
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp279
-rw-r--r--source/gameengine/Ketsji/KX_Scene.h16
-rw-r--r--source/gameengine/Ketsji/KX_SceneActuator.cpp45
-rw-r--r--source/gameengine/Ketsji/KX_SceneActuator.h3
-rw-r--r--source/gameengine/Ketsji/KX_SumoPhysicsController.cpp5
-rw-r--r--source/gameengine/Ketsji/KX_SumoPhysicsController.h4
-rw-r--r--source/gameengine/Ketsji/KX_TrackToActuator.cpp12
-rw-r--r--source/gameengine/Ketsji/KX_TrackToActuator.h1
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsController.cpp34
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsController.h5
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.cpp32
-rw-r--r--source/gameengine/Physics/Bullet/CcdPhysicsEnvironment.h9
-rw-r--r--source/gameengine/PyDoc/KX_GameObject.py12
-rw-r--r--source/gameengine/Rasterizer/RAS_2DFilterManager.cpp25
-rw-r--r--source/gameengine/Rasterizer/RAS_2DFilterManager.h2
-rw-r--r--source/gameengine/Rasterizer/RAS_ICanvas.h8
-rw-r--r--source/gameengine/SceneGraph/SG_IObject.cpp6
-rw-r--r--source/gameengine/SceneGraph/SG_IObject.h2
-rw-r--r--source/gameengine/SceneGraph/SG_Node.cpp29
-rw-r--r--source/gameengine/SceneGraph/SG_Node.h2
98 files changed, 5732 insertions, 817 deletions
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; y<pa->disprect.ymax; y++) {
- for(x=pa->disprect.xmin; x<pa->disprect.xmax; x++, zrect++, rgbrect+=4) {
+ for(x=pa->disprect.xmin; x<pa->disprect.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; sample<totsample; sample++) {
+ VECCOPY((float*)(rlpp[sample]->rectf + 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <None> %s",
+ actlay->info, ((gpd->flag & GP_DATA_EDITPAINT)?", Draw Mode On":"") );
+ }
+ }
+ else {
+ BIF_ThemeColor(TH_REDALERT);
+ sprintf(printable, "GPencil: Layer <None>");
+ }
+ 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; a<SPACE_MAXHANDLER; a+=2) {
+ /* clear action value for event */
+ switch(snode->blockhandler[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; a<SPACE_MAXHANDLER; a+=2) {
+ switch(sseq->blockhandler[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; a<SPACE_MAXHANDLER; a+=2) {
- /* clear action value for event */
- sseq->blockhandler[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; a<SPACE_MAXHANDLER; a+=2) {
-
switch(v3d->blockhandler[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; verts<icu->totvert; 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; verts<icu->totvert; 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#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":"<None>");
+ 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<MT_Vector3> inivel,iniang;
-
+ set<Group*> grouplist; // list of groups to be converted
+ set<Object*> allblobj; // all objects converted
+ set<Object*> 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<Group*> allgrouplist = grouplist;
+ set<Group*> tempglist;
+ // recurse
+ while (!grouplist.empty())
+ {
+ set<Group*>::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<Object*>::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<KX_GameObject*>(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;i<logicbrick_conversionlist->GetCount();i++)
{
KX_GameObject* gameobj = static_cast<KX_GameObject*>(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;i<logicbrick_conversionlist->GetCount();i++)
{
KX_GameObject* gameobj = static_cast<KX_GameObject*>(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;i<logicbrick_conversionlist->GetCount();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;i<objcount;i++)
+ {
+ KX_GameObject* gameobj = (KX_GameObject*) objectlist->GetValue(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<IpoCurve*>::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; i<vertexarrays.size(); i++) {
+ KX_VertexArray& vertexarray = (*vertexarrays[i]);
- // For each array
- for (i=0; i<vertexarrays.size(); i++) {
- KX_VertexArray& vertexarray = (*vertexarrays[i]);
-
- // For each vertex
- // copy the untransformed data from the original mvert
- for (j=0; j<vertexarray.size(); j++) {
- RAS_TexVert& v = vertexarray[j];
- v.SetXYZ(m_transverts[v.getOrigIndex()]);
- }
+ // For each vertex
+ // copy the untransformed data from the original mvert
+ for (j=0; j<vertexarray.size(); j++) {
+ RAS_TexVert& v = vertexarray[j];
+ v.SetXYZ(m_transverts[v.getOrigIndex()]);
}
}
@@ -153,9 +150,11 @@ bool BL_SkinDeformer::Update(void)
/* XXX note: where_is_pose() (from BKE_armature.h) calculates all matrices needed to start deforming */
/* but it requires the blender object pointer... */
-
Object* par_arma = m_armobj->GetArmatureObject();
- 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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<SCA_ISensor*>::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<KX_GameObject*>::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<SCA_IActuator*>::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<KX_GameObject*>::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<KX_GameObject*> 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<CValue*>::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<KX_GameObject*>::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<KX_GameObject*>::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<KX_GameObject*> 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<CValue*> 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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<GEN_HashedPtr, void*> *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<CcdPhysicsController*>::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 <iostream>
@@ -158,7 +159,8 @@ void RAS_2DFilterManager::AnalyseShader(int passindex, vector<STR_String>& 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
@@ -137,6 +137,14 @@ public:
) = 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<SG_Node*>(replica)->m_SGparent = NULL;
+ static_cast<SG_Node*>(*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
);
/**