#ifdef HAVE_CONFIG_H #include <config.h> #endif Just need to finish cpp files now :) Kent -- mein@cs.umn.edu
758 lines
17 KiB
C
758 lines
17 KiB
C
/** 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"
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
/************************************************************************
|
|
* 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;
|
|
}
|
|
|