diff options
Diffstat (limited to 'source/blender/bpython/intern/b_interface.c')
-rw-r--r-- | source/blender/bpython/intern/b_interface.c | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/source/blender/bpython/intern/b_interface.c b/source/blender/bpython/intern/b_interface.c new file mode 100644 index 00000000000..ec89680ad91 --- /dev/null +++ b/source/blender/bpython/intern/b_interface.c @@ -0,0 +1,753 @@ +/** Interfacing with Blender + * + * ***** BEGIN GPL/BL DUAL 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. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + * + * $Id$ + * + * This code is currently messy and an attempt to restructure + * some Blender kernel level code. + * Hopefully a template for a future C-API... + * + * + */ + + +#include "BLI_blenlib.h" // mallocs +#include "BLI_arithb.h" + +#include "BKE_library.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_mesh.h" +#include "BKE_ipo.h" + +#include "MEM_guardedalloc.h" + +#include "Python.h" +#include "BPY_macros.h" +#include "structmember.h" + +#include "BDR_editobject.h" + +#include "b_interface.h" + +/************************************************************************ + * Generic low level routines + * + */ + +/** This just returns a pointer to the global struct. + * + * Mainly introduced for debugging purposes.. + * + */ + +Global *getGlobal(void) +{ + return &G; +} + +/** define list getters: + These functions return a linked list pointer (ListBase *) from the global + Blender-object list. + + Example: + oblist = getObjectList(); + firstobject = oblist->first; + + */ + +/* +DEF_GETLIST(Scene, scene) +DEF_GETLIST(Object, object) +DEF_GETLIST(Mesh, mesh) +DEF_GETLIST(Camera, camera) +DEF_GETLIST(Material, mat) +DEF_GETLIST(Lamp, lamp) +DEF_GETLIST(World, world) +DEF_GETLIST(Ipo, ipo) +DEF_GETLIST(Image, image) +DEF_GETLIST(Texture, tex) +DEF_GETLIST(Text, text) +DEF_GETLIST(Key, key) +*/ + +/* gets a datablock object from the ID list by name */ +ID *getFromList(ListBase *list, char *name) +{ + ID *id = list->first; + + while (id) { + if(STREQ(name, getIDName(id))) break; + id= id->next; + } + return id; +} + +void printList(ListBase *list) +{ + ID *walk = list->first; + ID *lastwalk = 0; + printf("List: %s\n", walk->name); + while (walk) { + printf(" %s\n", walk->name); + lastwalk = walk; + walk= walk->next; + } + if (list->last != lastwalk) + { + printf("****: listbase->last pointing to wrong end!\n"); + // list->last = lastwalk; + } +} + + +/** (future) garbage collector subroutine */ + + +int gc_mainlist(ListBase *lb) +{ + ID *id = (ID *) lb->first; + + while (id) { + if (getIDUsers(id) == 0) { + switch(GET_ID_TYPE(id)) { + case ID_OB: + BPY_debug(("free [Object %s]\n", getIDName(id))); + unlink_object((Object *) id); + free_libblock(lb, id); + break; + default: break; + } + } + id = id->next; + } + return 1; +} + + /** Garbage collection function. EXPERIMENTAL! + * This should free Blender from all unreferenced Objects (i.e. + * user count == 0). + * Don't expect too much yet -- User counting isn't done + * consequently in Blender. Neither parenting or bevelcurves + * etc. respect user counts...therefore, no circular references + * show up -- which are in fact possible; example: + * + * A BevelCurve is parenting its BevelObject: so there is a + * reference from the BevelObject to the BevelCurve, and a + * reference back from the Bevelcurve to the BevelObject. + * + * There are a lot of cleanup functions in Blender taking care + * of updating (invalidating) references to deleted objects. + * See unlink_object() for more details. + * + * This function must not be called inside a script, so don't go + * and create a wrapper for it :-) + * In a hopefully later implementation, the Python garbage collection + * might be used. For the moment, this is better than 'Save and Reload' + */ + + int garbage_collect(Main *m) + { + /* Remember, all descriptor objects must BOB_DECUSER on their raw + Blender Datablock in their __del__ method (C-API: dealloc function) */ + + gc_mainlist(&m->object); + + /* TODO proper kernel level functions for safely freeing these objects + * must first be implemented... + gc_mainlist(&m->mesh); + gc_mainlist(&m->mat); + gc_mainlist(&m->lamp); + gc_mainlist(&m->camera); + + .. and this list is far from being complete. + */ + + return 1; +} + +/** expands pointer array of length 'oldsize' to length 'newsize'. + * A pointer to the (void *) array must be passed as first argument + * The array pointer content can be NULL, in this case a new array of length + * 'newsize' is created. + */ + +static int expandPtrArray(void **p, int oldsize, int newsize) +{ + void *newarray; + + if (newsize < oldsize) { + return 0; + } + newarray = MEM_callocN(newsize * sizeof(void *), "PtrArray"); + if (*p) { + memcpy(newarray, *p, oldsize); + MEM_freeN(*p); + } + *p = newarray; + return 1; +} + +/************************************************************************ + * Material object low level routines + * + */ + +/* MAXMAT = maximum number of materials per object/ object data */ + +#define MATINDEX_CHECK(x) \ + if ((x) < 0 || (x) >= MAXMAT) { printf("illegal matindex!\n"); return 0; } + +/** Returns a new material list (material pointer array) of length 'len' + * + */ + +Material **newMaterialList(int len) +{ + Material **matlist = + (Material **) MEM_mallocN(len * sizeof(Material *), "MaterialList"); + return matlist; +} + +/** releases material list and decrements user count on materials */ + +int releaseMaterialList(Material **matlist, int len) +{ + int i; + Material *m; + + MATINDEX_CHECK(len); + + for (i= 0; i < len; i++) { + + m = matlist[i]; + BOB_XDECUSER((ID *) m); + } + MEM_freeN(matlist); + return 1; +} + +/** Synchronizes Object <-> data material lists. Blender just wants it. */ + +int synchronizeMaterialLists(Object *object, void *data) +{ + // get pointer to data's material array: + // and number of data materials + // ... because they will need modification. + + Material ***p_dataMaterials = give_matarar(object); + short *nmaterials = give_totcolp(object); + + if (object->totcol > *nmaterials){ // more object mats than data mats + *nmaterials = object->totcol; + return expandPtrArray((void *) p_dataMaterials, *nmaterials, object->totcol); + } else if (object->totcol < *nmaterials) { + object->totcol = *nmaterials; + return expandPtrArray((void *) &object->mat, object->totcol, *nmaterials); + } + return 1; // in this case, no synchronization needed; they're of equal + // length +} + +/************************************************************************ + * Object low level routines + * + */ + +/** creates a new empty object of type OB_ (TODO: should be enum) + * + */ + + +Object *object_new(int type) +{ + Object *object; + char name[32]; + + Global *g = getGlobal(); + + switch(type) { + case OB_MESH: strcpy(name, "Mesh"); break; + case OB_CURVE: strcpy(name, "Curve"); break; + case OB_SURF: strcpy(name, "Surf"); break; + case OB_FONT: strcpy(name, "Text"); break; + case OB_MBALL: strcpy(name, "Mball"); break; + case OB_CAMERA: strcpy(name, "Camera"); break; + case OB_LAMP: strcpy(name, "Lamp"); break; + case OB_IKA: strcpy(name, "Ika"); break; + case OB_LATTICE: strcpy(name, "Lattice"); break; + case OB_WAVE: strcpy(name, "Wave"); break; + case OB_ARMATURE: strcpy(name,"Armature");break; + default: strcpy(name, "Empty"); + } + + object = alloc_libblock(getObjectList(), ID_OB, name); + + /* user count is set to 1 by alloc_libblock, we just reset it to 0... */ + BOB_USERCOUNT((ID*) object) = 0; // it's a new object, so no user yet + object->flag = 0; + object->type = type; + + /* transforms */ + QuatOne(object->quat); + QuatOne(object->dquat); + + object->col[3]= 1.0; // alpha + + object->size[0] = object->size[1] = object->size[2] = 1.0; + object->loc[0] = object->loc[1] = object->loc[2] = 0.0; + Mat4One(object->parentinv); + Mat4One(object->obmat); + object->dt = OB_SHADED; // drawtype + + object_setdefaults(object); + + object->lay = 1; // Layer, by default visible + + switch(type) { + case OB_MESH: object->data= add_mesh(); g->totmesh++; break; + case OB_CAMERA: object->data= add_camera(); break; + case OB_LAMP: object->data= add_lamp(); g->totlamp++; break; + + // TODO the following types will be supported later + // case OB_CURVE: object->data= add_curve(OB_CURVE); g->totcurve++; break; + // case OB_SURF: object->data= add_curve(OB_SURF); g->totcurve++; break; + // case OB_FONT: object->data= add_curve(OB_FONT); break; + // case OB_MBALL: object->data= add_mball(); break; + // case OB_IKA: object->data= add_ika(); object->dt= OB_WIRE; break; + // case OB_LATTICE: object->data= (void *)add_lattice(); object->dt= OB_WIRE; break; + // case OB_WAVE: object->data= add_wave(); break; + // case OB_ARMATURE: object->data=add_armature();break; + } + + g->totobj++; // gee, I *hate* G + return object; +} + +/* returns new Base */ +Base *object_newBase(Object *object) +{ + Base *base; + base = MEM_callocN(sizeof(Base), "newbase"); + if (!base) + return 0; + base->object = object; + base->lay = object->lay; + base->flag = object->flag; + return base; +} + +Object *object_copy(Object *object) +{ + Object *new; + + new = copy_object(object); + BOB_USERCOUNT((ID*) new) = 0; // it's a new object, so no user yet + return new; +} + +/* Set draw mode of object */ +void object_setDrawMode(Object *object, int modebits) +{ + object->dt = (modebits & 0xff); + object->dtx = (modebits >> 8); +} + +int object_getDrawMode(Object *object) +{ + return (((int) object->dtx) << 8 ) + object->dt; +} + +/* link data to Object object */ +int object_linkdata(Object *object, void *data) +{ + ID *oldid, *id; + int valid; + + if (!data) return 0; + + oldid = (ID*) object->data; + id = (ID*) data; + + valid = 0; + +#define _CASE(objtype, idtype) \ + case objtype:\ + if (GET_ID_TYPE(id) == idtype) \ + valid = 1; \ + break; + + switch (object->type) { + _CASE(OB_MESH, ID_ME) + _CASE(OB_CAMERA, ID_CA) + _CASE(OB_LAMP, ID_LA) + default: // not supported + return 0; + } + if (valid) { + object->data = data; + BOB_INCUSER(id); + if (oldid) + BOB_DECUSER(oldid); // dec users + + // extra check for multi materials on meshes: + // This is a hack to check whether object material lists are of same + // length as their data material lists.. + //if (GET_ID_TYPE(id) == ID_ME) { + //test_object_materials(id); + //} + return 1; + } + return 0; +} + +/* release data from object object */ + +int object_unlinkdata(Object *object) +{ + ID *id = object->data; + + BOB_XDECUSER(id); + return 1; +} + +/** set Object materials: + * takes a list of Material pointers of maximum length MAXMAT + */ + +int object_setMaterials(Object *object, Material **matlist, int len) +{ + int i; + + MATINDEX_CHECK(len) + if (object->mat) { + releaseMaterialList(object->mat, len); + } + // inc user count on all materials + for (i = 0; i < len; i++) { + BOB_XINCUSER( (ID *) matlist[i]); + } + + object->mat = matlist; + object->totcol = len; + object->actcol = len - 1; // XXX + // workaround: blender wants the object's data material list + // to be of the same length, otherwise colourful fun happens. + // so, we synchronize this here: + + switch (object->type) + { + case OB_MESH: + case OB_CURVE: + case OB_FONT: + case OB_SURF: + case OB_MBALL: + synchronizeMaterialLists(object, object->data); + break; + default: + return 0; + } + return 1; +} + +/** make 'object' the parent of the object 'child' + * + * mode = 1: set parent inverse matrix to _1_ ('clear inverse') + * fast = 1: Don't update scene base (hierarchy). In this case, + * sort_baselist() needs to be called explicitely before redraw. + */ + +int object_makeParent(Object *parent, Object *child, int mode, int fast) +{ + if (test_parent_loop(parent, child)) { + PyErr_SetString(PyExc_RuntimeError, "parenting loop detected!"); + return 0; + } + child->partype = PAROBJECT; + child->parent = parent; + if (mode == 1) { + Mat4One(child->parentinv); // parent inverse = unity + child->loc[0] = 0.0; child->loc[1] = 0.0; child->loc[2] = 0.0; + } else { + what_does_parent(child); + Mat4Invert(child->parentinv, parent->obmat); // save inverse + } + + /* This is some bad global thing again -- we should determine + the current scene + another way. Later. */ + if (!fast) + sort_baselist(getGlobal()->scene); + + return 1; +} + +/** Unlink parenting hierarchy: + * + * mode = 2: keep transform + * fast = 1: don't update scene bases. see makeParent() + */ + +int object_clrParent(Object *child, int mode, int fast) +{ + Object *par; + + par = child->parent; + child->parent = 0; + if (mode == 2) { // keep transform + apply_obmat(child); + } + if (!fast) + sort_baselist(getGlobal()->scene); + return 1; +} + +/** Set object's defaults */ + +int object_setdefaults(Object *ob) +{ + if(U.flag & MAT_ON_OB) ob->colbits= -1; + switch(ob->type) { + case OB_CAMERA: + case OB_LAMP: + ob->trackflag = OB_NEGZ; + ob->upflag = OB_POSY; + break; + default: + ob->trackflag = OB_POSY; + ob->upflag = OB_POSZ; + } + ob->ipoflag = OB_OFFS_OB + OB_OFFS_PARENT; + + /* duplivert settings */ + + ob->dupon = 1; ob->dupoff = 0; + ob->dupsta = 1; ob->dupend = 100; + + /* Gameengine defaults*/ + ob->mass= ob->inertia= 1.0; + ob->formfactor= 0.4; + ob->damping= 0.04; + ob->rdamping= 0.1; + ob->anisotropicFriction[0] = 1.0; + ob->anisotropicFriction[1] = 1.0; + ob->anisotropicFriction[2] = 1.0; + + /* default to not use fh in new system */ + ob->gameflag= OB_PROP; /*|OB_DO_FH; */ + + return 1; +} + +/************************************************************************ + * Creation of new data blocks + * + * We [ab|re]use the blender kernel functions, but set the user count to 0, + * because the object does not have users yet. + * Currently, the user count is abused as reference count which should be + * separate in future + */ + +Material *material_new(void) +{ + Material *m = add_material("Material"); + BOB_USERCOUNT((ID*) m) = 0; // set 'refcount' to 0, because + // it's a free material + return m; +} + +Lamp *lamp_new() +{ + Lamp *la; + + la = add_lamp(); + BOB_USERCOUNT((ID*) la) = 0; + + return la; +} + +Camera *camera_new() +{ + Camera *cam; + + cam = add_camera(); + BOB_USERCOUNT((ID*) cam) = 0; + return cam; +} + +Ipo *ipo_new(int type, char *name) +{ + Ipo *ipo; + + ipo = add_ipo(name, type); + BOB_USERCOUNT((ID*) ipo) = 0; + return ipo; +} + + +/* Finds the ipo curve with channel code 'code' in the datablock 'ipo' + and returns it, if found (NULL otherwise) */ + +IpoCurve *ipo_findcurve(Ipo *ipo, int code) +{ + IpoCurve *ipocurve; + + ipocurve = ipo->curve.first; + while(ipocurve) { + if (ipocurve->adrcode == code) break; + ipocurve = ipocurve->next; + } + return ipocurve; +} + + +/** Returns a new Ipo curve */ +IpoCurve *ipocurve_new() +{ + IpoCurve *curve; + + curve = MEM_callocN(sizeof(IpoCurve), "new_ipocurve"); + curve->flag = IPO_VISIBLE; + return curve; +} + +IpoCurve *ipocurve_copy(IpoCurve *curve) +{ + IpoCurve *newcurve; + + newcurve = MEM_callocN(sizeof(IpoCurve), "new_ipocurve"); + memcpy(newcurve, curve, sizeof(IpoCurve)); + // copy bez triples: + newcurve->bezt= MEM_mallocN(curve->totvert*sizeof(BezTriple), "ipocurve_copy"); + memcpy(newcurve->bezt, curve->bezt, curve->totvert*sizeof(BezTriple)); + return newcurve; +} + +/** Assign ipo to object object */ + +/* macros, see b_interface.h */ + +DEF_ASSIGN_IPO(object, Object) // defines object_assignIpo() + +DEF_ASSIGN_IPO(camera, Camera) + +DEF_ASSIGN_IPO(lamp, Lamp) + +DEF_ASSIGN_IPO(material, Material) + +/************************************************************************ + * Mesh object low level routines + * + */ + +/** Returns a new, free (non owned) mesh. + * add_mesh() automatically returns a mesh object with users = 1, + * so we set it to 0. Hack, hack. + */ + +Mesh *mesh_new(void) +{ + Mesh *me = add_mesh(); + ((ID *) me)->us = 0; + return me; +} + +/** updates drawing properties etc. of mesh */ + +void mesh_update(Mesh *mesh) +{ + edge_drawflags_mesh(mesh); + tex_space_mesh(mesh); +} + +/************************************************************************ + * Scene object low level routines + * + */ + +/** Returns current Scene */ + +Scene *scene_getCurrent(void) +{ + return getGlobal()->scene; +} + +/* returns base of object 'object' in Scene 'scene', 0 if nonexistant + * A base is basically an visual instantiation of an 3D object (Object) + * in a Scene. See scene_linkObject() + * + */ + +Base *scene_getObjectBase(Scene *scene, Object *object) +{ + Base *base; + base = scene->base.first; + while (base) + { + if (object == base->object) // it exists + return base; + base = base->next; + } + return NULL; +} + +/* links an object into a scene */ +int scene_linkObject(Scene *scene, Object *object) +{ + Base *base, *b; + b = scene_getObjectBase(scene, object); + if (b) + return 0; + base = object_newBase(object); + if (!base) { + return 0; + } + BOB_INCUSER((ID *) object); // incref the object + BLI_addhead(&scene->base, base); + return 1; +} + +/* unlinks an object from a scene */ +int scene_unlinkObject(Scene *scene, Object *object) +{ + Base *base; + base = scene_getObjectBase(scene, object); + if (base) { + BLI_remlink(&scene->base, base); + BOB_DECUSER((ID *) object); + MEM_freeN(base); + scene->basact = 0; // make sure the just deleted object has no longer an + // active base (which happens if it was selected + return 1; + } + else return 0; +} + |