/* Datablock handling code. This handles the generic low level access to Blender Datablocks. */ /* * $Id$ * * ***** 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 ***** */ /************************************************************************** * This code provides low level, generalized access to the Blender Datablock * objects. It basically creates a descriptor Python Object of type 'DataBlock' * for each requested Blender datablock. * This introduces the question of synchronization, for example: * What happens if an Object is deleted? * * Blender Objects have their own 'reference counting', e.g. a Mesh datablock * used by two Objects has a user count of 2. Datablocks with user count of 0 * are not saved to Disk -- this is the current way Blender does * 'garbage collection' * Therefore, an object should normally not be deleted by Python, but rather * unlinked from its parent object. * Still, for other objects like Scene or Text objects, deletion from 'Main' * is desired. * The current workaround: * Some objects can be explicitely deleted (not recommended, but possible) -- * they have a user count of 1, even if they are used by objects in some way, * for example Text objects which are used by any other Blender object * through a ScriptLink. * * Objects that are deleted through Python end up with a 'dead' descriptor; * accessing the descriptor after deletion causes a Python exception. * * NASTY UGLY DIRTY, VUILE, DRECKIGES AND STILL REMAINING PROBLEM: * * It is (in the current API) possible to construct the case, that an * Object is deleted in Blender, but the Python descriptor does not know * about this. Accessing the descriptor (which simply contains a pointer to * the raw datablock struct) will most probably end in colourful joy. * * TODO: * possible solutions: * - rewrite datablock handling that way, that the descriptor uses an id * tag to retrieve that pointer through a getPointerbyID() function * (if the object exists!) on each access. Slow. * - make sure that deletion always happends by the descriptor and never * delete the raw datastructure. This solution would imply a major * redesign of user action handling (GUI actions calling python). * Not likely to happen...better fusion raw and python object in this case. * After all, still somewhat dirty. * - make sure that no deletion can happen in Blender while a script * still accesses the raw data - i.e. implement user counting of raw * objects with descriptors. This would need an implementation of * garbage collection in Blender. This might sound like the most feasible * solution... */ #include "Python.h" #include "BPY_macros.h" #include "opy_datablock.h" #include "opy_nmesh.h" #include "opy_vector.h" /* matrix datatypes */ #include "BPY_tools.h" #include "BPY_types.h" #include "BPY_main.h" #include "MEM_guardedalloc.h" #include "b_interface.h" /* needed for most of the DNA datatypes */ /* ---------------------------------------------------------------------- */ /*********************/ /* Camera Datablocks */ DATABLOCK_GET(Cameramodule, camera, getCameraList()) static char Cameramodule_New_doc[] = "() - returns new Camera object"; PyObject *Cameramodule_New (PyObject *self, PyObject *args) { Camera *obj; obj = camera_new(); return DataBlock_fromData(obj); } #ifdef FUTURE_PYTHON_API DataBlockProperty Camera_Properties[]= { {"lens", "lens", DBP_TYPE_FLO, 0, 1.0, 250.0}, {"clipStart","clipsta", DBP_TYPE_FLO, 0, 0.0, 100.0}, {"clipEnd", "clipend", DBP_TYPE_FLO, 0, 1.0, 5000.0}, {"type", "type", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"mode", "flag", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #else DataBlockProperty Camera_Properties[]= { {"Lens", "lens", DBP_TYPE_FLO, 0, 1.0, 250.0}, {"ClSta", "clipsta", DBP_TYPE_FLO, 0, 0.0, 100.0}, {"ClEnd", "clipend", DBP_TYPE_FLO, 0, 1.0, 5000.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #endif static struct PyMethodDef Cameramodule_methods[] = { {"New", Cameramodule_New, METH_VARARGS, Cameramodule_New_doc}, {"get", Cameramodule_get, METH_VARARGS, Cameramodule_get_doc}, {NULL, NULL} }; DATABLOCK_ASSIGN_IPO(Camera, camera) // defines Camera_assignIpo static struct PyMethodDef Camera_methods[] = { {"clrIpo", Camera_clrIpo, METH_VARARGS, Camera_clrIpo_doc}, {"assignIpo", Camera_assignIpo, METH_VARARGS, Camera_assignIpo_doc}, {NULL, NULL} }; /***********************/ /* Material Datablocks */ /** returns a pointer to a new (malloced) material list created from * a Python material list */ Material **newMaterialList_fromPyList(PyObject *list) { int i, len; DataBlock *block = 0; Material *mat; Material **matlist; len = PySequence_Length(list); if (len > 16) len = 16; matlist = newMaterialList(len); for (i= 0; i < len; i++) { block= (DataBlock *) PySequence_GetItem(list, i); if (DataBlock_isType(block, ID_MA)) { mat = (Material *) block->data; matlist[i] = mat; } else { // error; illegal type in material list Py_DECREF(block); MEM_freeN(matlist); return NULL; } Py_DECREF(block); } return matlist; } /** Return Python List from material pointer list 'matlist' with length * 'len' * */ PyObject *PyList_fromMaterialList(Material **matlist, int len) { PyObject *list; int i; list = PyList_New(0); if (!matlist) return list; for (i = 0; i < len; i++) { Material *mat= matlist[i]; PyObject *ob; if (mat) { ob = DataBlock_fromData(mat); PyList_Append(list, ob); Py_DECREF(ob); // because Append increfs! } } return list; } DATABLOCK_GET(Materialmodule, material, getMaterialList()) DATABLOCK_NEW(Materialmodule, Material, material_new()) static struct PyMethodDef Materialmodule_methods[] = { {"get", Materialmodule_get, METH_VARARGS, Materialmodule_get_doc}, {"New", Materialmodule_New, METH_VARARGS, Materialmodule_New_doc}, {NULL, NULL} }; DATABLOCK_ASSIGN_IPO(Material, material) static struct PyMethodDef Material_methods[] = { {"clrIpo", Material_clrIpo, METH_VARARGS, Material_clrIpo_doc}, {"assignIpo", Material_assignIpo, METH_VARARGS, Material_assignIpo_doc}, {NULL, NULL} }; #ifdef FUTURE_PYTHON_API DataBlockProperty Material_Properties[]= { {"R", "r", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"G", "g", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"B", "b", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"specR", "specr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"specG", "specg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"specB", "specb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"mirR", "mirr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"mirG", "mirg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"mirB", "mirb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ref", "ref", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"alpha", "alpha", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"emit", "emit", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"amb", "amb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"spec", "spec", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"specTransp", "spectra", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"haloSize", "hasize", DBP_TYPE_FLO, 0, 0.0, 10000.0}, {"mode", "mode", DBP_TYPE_INT, 0, 0.0, 0.0}, {"hard", "har", DBP_TYPE_SHO, 0, 1.0, 128.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #else DataBlockProperty Material_Properties[]= { {"R", "r", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"G", "g", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"B", "b", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"SpecR", "specr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"SpecG", "specg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"SpecB", "specb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"MirR", "mirr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"MirG", "mirg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"MirB", "mirb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Ref", "ref", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Alpha", "alpha", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Emit", "emit", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Amb", "amb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Spec", "spec", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"SpTra", "spectra", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"HaSize", "hasize", DBP_TYPE_FLO, 0, 0.0, 10000.0}, {"Mode", "mode", DBP_TYPE_INT, 0, 0.0, 0.0}, {"Hard", "har", DBP_TYPE_SHO, 0, 1.0, 128.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #endif /*******************/ /* Lamp Datablocks */ DATABLOCK_GET(Lampmodule, lamp, getLampList()) // DATABLOCK_NEW(Lampmodule, Lamp, lamp_new()) static char Lampmodule_New_doc[] = "() - returns new Lamp object"; PyObject *Lampmodule_New (PyObject *self, PyObject *args) { Lamp *obj; obj = lamp_new(); return DataBlock_fromData(obj); } #ifdef FUTURE_PYTHON_API DataBlockProperty Lamp_Properties[]= { {"mode", "mode", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"type", "type", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"R", "r", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"G", "g", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"B", "b", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"energy", "energy", DBP_TYPE_FLO, 0, 0.0, 10.0}, {"dist", "dist", DBP_TYPE_FLO, 0, 0.01, 5000.0}, {"spotSize", "spotsize", DBP_TYPE_FLO, 0, 1.0, 180.0}, {"spotBlend", "spotblend", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"haloInt", "haint", DBP_TYPE_FLO, 0, 0.0, 5.0}, {"quad1", "att1", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"quad2", "att2", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"bufferSize", "bufsize", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"samples", "samp", DBP_TYPE_SHO, 0, 1.0, 16.0}, {"haloStep", "shadhalostep", DBP_TYPE_SHO, 0, 0.0, 12.0}, {"clipStart", "clipsta", DBP_TYPE_FLO, 0, 0.1, 5000.0}, {"clipEnd", "clipend", DBP_TYPE_FLO, 0, 0.1, 5000.0}, {"bias", "bias", DBP_TYPE_FLO, 0, 0.01, 5.0}, {"softness", "soft", DBP_TYPE_FLO, 0, 1.00, 100.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #else DataBlockProperty Lamp_Properties[]= { {"mode", "mode", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"type", "type", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"R", "r", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"G", "g", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"B", "b", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Energ", "energy", DBP_TYPE_FLO, 0, 0.0, 10.0}, {"Dist", "dist", DBP_TYPE_FLO, 0, 0.01, 5000.0}, {"SpotSi", "spotsize", DBP_TYPE_FLO, 0, 1.0, 180.0}, {"SpotBl", "spotblend", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"HaloInt", "haint", DBP_TYPE_FLO, 0, 1.0, 5.0}, {"Quad1", "att1", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Quad2", "att2", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #endif static struct PyMethodDef Lampmodule_methods[] = { {"New", Lampmodule_New, METH_VARARGS, Lampmodule_New_doc}, {"get", Lampmodule_get, METH_VARARGS, Lampmodule_get_doc}, #ifdef CURRENT_PYTHON_API {"Get", Lampmodule_get, METH_VARARGS, Lampmodule_get_doc}, #endif {NULL, NULL} }; DATABLOCK_ASSIGN_IPO(Lamp, lamp) // defines Lamp_assignIpo static struct PyMethodDef Lamp_methods[] = { {"clrIpo", Lamp_clrIpo, METH_VARARGS, Lamp_clrIpo_doc}, {"assignIpo", Lamp_assignIpo, METH_VARARGS, Lamp_assignIpo_doc}, {NULL, NULL} }; /********************/ /* World Datablocks */ DATABLOCK_GET(Worldmodule, world, getWorldList() ) #ifdef FUTURE_PYTHON_API DataBlockProperty World_Properties[]= { {"mode", "mode", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"skyType", "skytype", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"mistType", "mistype", DBP_TYPE_SHO, 0, 0.0, 0.0}, {"horR", "horr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"horG", "horg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"horB", "horb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ambR", "ambr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ambG", "ambg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ambB", "ambb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"zenR", "zenr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"zenG", "zeng", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"zenB", "zenb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"exposure", "exposure", DBP_TYPE_FLO, 0, 0.0, 5.0}, {"mistStart", "miststa", DBP_TYPE_FLO, 0, 0.0, 1000.0}, {"mistDepth", "mistdist", DBP_TYPE_FLO, 0, 0.0, 1000.0}, {"mistHeight", "misthi", DBP_TYPE_FLO, 0, 0.0, 100.0}, {"starDensity", "stardist", DBP_TYPE_FLO, 0, 2.0, 1000.0}, {"starMinDist", "starmindist", DBP_TYPE_FLO, 0, 0.0, 1000.0}, {"starSize", "starsize", DBP_TYPE_FLO, 0, 0.0, 10.0}, {"starColNoise", "starcolsize", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"gravity", "gravity", DBP_TYPE_FLO, 0, 0.0, 25.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #else DataBlockProperty World_Properties[]= { {"HorR", "horr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"HorG", "horg", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"HorB", "horb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ZenR", "zenr", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ZenG", "zeng", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"ZenB", "zenb", DBP_TYPE_FLO, 0, 0.0, 1.0}, {"Expos", "exposure", DBP_TYPE_FLO, 0, 0.0, 5.0}, {"MisSta", "miststa", DBP_TYPE_FLO, 0, 0.0, 1000.0}, {"MisDi", "mistdist", DBP_TYPE_FLO, 0, 0.0, 1000.0}, {"MisHi", "misthi", DBP_TYPE_FLO, 0, 0.0, 100.0}, {"StarDi", "stardist", DBP_TYPE_FLO, 0, 2.0, 1000.0}, {"StarSi", "starsize", DBP_TYPE_FLO, 0, 0.0, 10.0}, {"ipo", "*ipo", DBP_TYPE_FUN, 0, 0.0, 0.0, {0}, {0}, 0, 0, get_DataBlock_func}, {NULL} }; #endif static char Worldmodule_getActive_doc[]="() - Returns the active world"; static PyObject *Worldmodule_getActive (PyObject *self, PyObject *args) { if (scene_getCurrent()->world) return DataBlock_fromData(scene_getCurrent()->world); else return BPY_incr_ret(Py_None); } static struct PyMethodDef Worldmodule_methods[] = { // these for compatibility... {"get", Worldmodule_get, METH_VARARGS, Worldmodule_get_doc}, #ifdef CURRENT_PYTHON_API {"Get", Worldmodule_get, METH_VARARGS, Worldmodule_get_doc}, #endif {"getCurrent", Worldmodule_getActive, METH_VARARGS, Worldmodule_getActive_doc}, {NULL, NULL} }; /* XXX these should go elsewhere */ PyObject *BPY_PyList_FromIDList(ListBase *list, DBConvertfunc convertfunc) { PyObject *pylist= PyList_New(BLI_countlist(list)); ID *id = list->first; int i=0; while (id) { PyObject *ob= convertfunc(id); if (!ob) { Py_DECREF(pylist); return NULL; } PyList_SetItem(pylist, i, ob); id = id->next; i++; } return pylist; } PyObject *py_find_from_list(ListBase *list, PyObject *args) { char *name= NULL; ID *id = list->first; BPY_TRY(PyArg_ParseTuple(args, "|s", &name)); if (name) { while (id) { if (strcmp(name, getIDName(id))==0) return DataBlock_fromData(id); id= id->next; } return BPY_incr_ret(Py_None); } else return BPY_PyList_FromIDList(list, DataBlock_fromData); } PyObject *named_enum_get(int val, NamedEnum *enums) { while (enums->name) { if (enums->num == val) return PyString_FromString(enums->name); enums++; } PyErr_SetString(PyExc_AttributeError, "Internal error, Unknown enumerated type"); return NULL; } int named_enum_set(char *name, NamedEnum *enums) { while (enums->name) { if (STREQ(enums->name, name)) return enums->num; enums++; } return -1; } static int calc_offset_subsize(int *dlist, int *idx, int *subsize) { int n= *dlist; if (n<=0) { *subsize= -n; return 0; } else { int ss; int off= calc_offset_subsize(dlist+1, idx+1, &ss); *subsize= n*ss; return off + (*idx)*ss; } } static int calc_offset(int *dlist, int *idx) { int subsize; return calc_offset_subsize(dlist, idx, &subsize); } static void *get_db_ptr(DataBlockProperty *prop, char *structname, void *struct_ptr) { int offset= BLO_findstruct_offset(structname, prop->struct_name); void *ptr= struct_ptr; if (offset==-1) { BPY_warn(("Internal error, Invalid prop entry\n")); return NULL; } ptr= (void *) (((char *)ptr) + offset); offset= calc_offset(prop->dlist, prop->idx); ptr= (void *) (((char *)ptr) + offset); return ptr; } PyObject *datablock_getattr(DataBlockProperty *props, char *structname, char *name, void *struct_ptr) { if (STREQ(name, "properties") || STREQ(name, "__members__")) { PyObject *l= PyList_New(0); DataBlockProperty *p= props; while (p->public_name) { PyList_Append(l, PyString_FromString(p->public_name)); p++; } return l; } while (props->public_name) { if (STREQ(name, props->public_name)) { void *ptr = struct_ptr; int val; DBPtrToObFP conv_fp; if (props->handling==DBP_HANDLING_NONE || props->handling==DBP_HANDLING_NENM) { ptr= get_db_ptr(props, structname, struct_ptr); if (!ptr) return NULL; } else if (props->handling==DBP_HANDLING_FUNC) { DBGetPtrFP fp= (DBGetPtrFP) props->extra1; ptr= fp(struct_ptr, props->struct_name, 0); if (!ptr) return NULL; } switch(props->type) { case DBP_TYPE_CHA: val= *((char *)ptr); if (props->handling==DBP_HANDLING_NENM) return named_enum_get(val, props->extra1); else return PyInt_FromLong(val); case DBP_TYPE_SHO: val= *((short *)ptr); if (props->handling==DBP_HANDLING_NENM) return named_enum_get(val, props->extra1); else return PyInt_FromLong(val); case DBP_TYPE_INT: val= *((int *)ptr); if (props->handling==DBP_HANDLING_NENM) return named_enum_get(val, props->extra1); else return PyInt_FromLong(val); case DBP_TYPE_FLO: return PyFloat_FromDouble ( *((float *)ptr) ); case DBP_TYPE_VEC: return newVectorObject ( ((float *)ptr), (int) props->min ); case DBP_TYPE_FUN: conv_fp= (DBPtrToObFP) props->extra2; return conv_fp( ptr ); default: PyErr_SetString(PyExc_AttributeError, "Internal error, Unknown prop type"); return NULL; } } props++; } PyErr_SetString(PyExc_AttributeError, name); return NULL; } int datablock_setattr(DataBlockProperty *props, char *structname, char *name, void *struct_ptr, PyObject *setto) { while (props->public_name) { if (STREQ(props->public_name, name)) { void *ptr = NULL; int type; DBSetPtrFP conv_fp; int clamp= props->min!=props->max; int enum_val= -1; char cha_data; short sho_data; int int_data; float flo_data; type= props->stype; if (type==DBP_TYPE_NON) type= props->type; if (props->handling==DBP_HANDLING_NONE) { ptr= get_db_ptr(props, structname, struct_ptr); if (!ptr) return 0; } else if (props->handling==DBP_HANDLING_FUNC) { if (type!=DBP_TYPE_FUN) { DBGetPtrFP fp= (DBGetPtrFP) props->extra1; ptr= fp(struct_ptr, props->struct_name, 1); if (!ptr) return 0; } } else if (props->handling==DBP_HANDLING_NENM) { char *str; if (!PyArg_Parse(setto, "s", &str)) return -1; ptr= get_db_ptr(props, structname, struct_ptr); if (!ptr) return 0; enum_val= named_enum_set(str, props->extra1); if (enum_val==-1) return py_err_ret_int(PyExc_AttributeError, "invalid setting for field"); } switch(type) { case DBP_TYPE_CHA: if (enum_val==-1) { if (!PyArg_Parse(setto, "b", &cha_data)) return -1; } else cha_data= (char) enum_val; if (clamp) { CLAMP(cha_data, (char) props->min, (char) props->max); } *((char *)ptr)= cha_data; return 0; case DBP_TYPE_SHO: if (enum_val==-1) { if (!PyArg_Parse(setto, "h", &sho_data)) return -1; } else sho_data= (short) enum_val; if (clamp) { CLAMP(sho_data, (short) props->min, (short) props->max); } *((short *)ptr)= sho_data; return 0; case DBP_TYPE_INT: if (enum_val==-1) { if (!PyArg_Parse(setto, "i", &int_data)) return -1; } else int_data= (int) enum_val; if (clamp) { CLAMP(int_data, (int) props->min, (int) props->max); } *((int *)ptr)= int_data; return 0; case DBP_TYPE_FLO: if (!PyArg_Parse(setto, "f", &flo_data)) return -1; if (clamp) { CLAMP(flo_data, (float) props->min, (float) props->max); } *((float *)ptr)= flo_data; return 0; case DBP_TYPE_VEC: /* this is very dangerous!! TYPE_VEC also can contain non floats; see * ipo curve attribute h1t, etc. */ if (props->min == 3.0 ) { // vector triple return BPY_parsefloatvector(setto, (float *) ptr, 3); } else { return py_err_ret_int(PyExc_AttributeError, "cannot directly assign, use slice assignment instead"); } return 0; case DBP_TYPE_FUN: conv_fp= (DBSetPtrFP) props->extra3; if (conv_fp) return conv_fp( struct_ptr, props->struct_name, setto ); else return py_err_ret_int(PyExc_AttributeError, "cannot directly assign to item"); default: PyErr_SetString(PyExc_AttributeError, "Internal error, Unknown prop type"); return -1; } } props++; } PyErr_SetString(PyExc_AttributeError, name); return -1; } PyObject *datablock_assignIpo(DataBlock *block, DataBlock *ipoblock) { Ipo **ipoptr; Ipo *ipo; if (!DataBlock_isType(ipoblock, ID_IP)) { PyErr_SetString(PyExc_TypeError, "expects Ipo object"); return 0; } ipo = PYBLOCK_AS_IPO(ipoblock); if (DataBlock_type(block) != ipo->blocktype) { PyErr_SetString(PyExc_TypeError, "Ipo type does not match object type!"); return 0; } ipoptr = get_db_ptr(block->properties, "ipo", block->data); if (!ipoptr) { PyErr_SetString(PyExc_RuntimeError, "Object does not have an ipo!"); return 0; } *ipoptr = ipo; Py_INCREF(Py_None); return Py_None; } /* deallocates a Python Datablock object */ void DataBlock_dealloc(DataBlock *self) { #ifdef REF_USERCOUNT BOB_XDECUSER(DATABLOCK_ID(self)); // XXX abuse for ref count #endif PyMem_DEL(self); } PyObject *DataBlock_repr(DataBlock *self) { static char s[256]; if (self->data) sprintf (s, "[%.32s %.32s]", self->type, getIDName((ID*)self->data)); else sprintf (s, "[%.32s %.32s]", self->type, ""); return Py_BuildValue("s", s); } /* ************************************************************************* */ /* datablock linking */ /** Link data to Object */ static PyObject *link_Data_toObject(DataBlock *objectblk, DataBlock *datablk) { Object *object = PYBLOCK_AS_OBJECT(objectblk); void *data = datablk->data; if (!object_linkdata(object, data)) { PyErr_SetString(PyExc_TypeError, "Object type different from Data type or linking for this type\ not supported"); return NULL; } Py_INCREF(Py_None); return Py_None; } #ifdef USE_NMESH /** Special function to link NMesh data to an Object */ static PyObject *link_NMesh_toObject(DataBlock *objectblk, NMesh *nmesh) { int retval; Mesh *mesh = nmesh->mesh; Object *obj = PYBLOCK_AS_OBJECT(objectblk); // if mesh was not created yet (mesh == 0), then do so: if (!mesh) { mesh = Mesh_fromNMesh(nmesh); // create and convert data nmesh->mesh = mesh; nmesh_updateMaterials(nmesh); } retval = object_linkdata(obj, mesh); if (!retval) { PyErr_SetString(PyExc_RuntimeError, "failed to link NMesh data"); if (!mesh) printf("mesh data was null\n"); // XXX return NULL; } synchronizeMaterialLists(obj, obj->data); return Py_BuildValue("i", retval); } #endif /** This is the generic function for linking objects with each other. * It can be called on any DataBlock, as long as this makes sense. * Example: * * from Blender import Object, Scene, NMesh * ob = Object.get("Plane") * scene = Scene.get("2") * ob.link(scene) * * or * * nmesh = NMesh.GetRaw('Mesh') * ob.link(nmesh) # instanciate mesh * */ static char DataBlock_link_doc[]= "(object) - Links 'self' with the specified object.\n\ Only the following object types can be linked to each other:\n\ Scene -> Object\n\ Object -> Data (Mesh, Curve, etc.)\n\ Object -> Materials: [Material1, Material2, ...]\n\ \n\ The order of linking does not matter, i.e. the following both expressions\n\ are valid:\n\ \n\ scene.link(object)\n\ \n\ object.link(scene)\n\ "; PyObject *DataBlock_link(PyObject *self, PyObject *args) { DataBlock *blockA= (DataBlock*) self; PyObject *with; DataBlock *blockB; #ifdef USE_NMESH BPY_TRY(PyArg_ParseTuple(args, "O", &with)); blockB = (DataBlock *) with; #else BPY_TRY(PyArg_ParseTuple(args, "O!", &DataBlock_Type, &blockB)); #endif switch (DataBlock_type(blockA)) { case ID_OB: // NMesh is no datablock object, so needs special treatment: #ifdef USE_NMESH if (NMesh_Check(with)) { return link_NMesh_toObject(blockA, (NMesh *) with); } #endif if (!DataBlock_Check(blockB)) { PyErr_SetString(PyExc_TypeError, "Argument must be a DataBlock object!"); return NULL; } return link_Data_toObject(blockA, blockB); default: PyErr_SetString(PyExc_TypeError, "FATAL: implementation error, illegal link method"); return NULL; } } /* unlinking currently disabled, but might me needed later for other object types... static char DataBlock_unlink_doc[]= "(object) - unlinks 'self' from the specified object.\n\ See documentation for link() for valid object types."; static PyObject *DataBlock_unlink(PyObject *self, PyObject *args) { DataBlock *blockA= (DataBlock*) self; DataBlock *blockB; BPY_TRY(PyArg_ParseTuple(args, "O!", &DataBlock_Type, &blockB)); switch (DataBlock_type(blockA)) { case ID_SCE: switch(DataBlock_type(blockB)) { case ID_OB: return unlink_Object_fromScene(blockA, blockB); default: PyErr_SetString(PyExc_TypeError, "Scene unlink: invalid Object type"); return NULL; } default: PyErr_SetString(PyExc_TypeError, "cannot unlink: invalid object type"); return NULL; } } */ /** These are the methods common to each datablock */ static struct PyMethodDef commonDataBlock_methods[] = { {"link", DataBlock_link, METH_VARARGS, DataBlock_link_doc}, // {"unlink", DataBlock_unlink, METH_VARARGS, DataBlock_unlink_doc}, {NULL} }; PyObject *DataBlock_getattr(PyObject *self, char *name) { DataBlock *block= (DataBlock*) self; PyObject *ret = NULL; CHECK_VALIDDATA(block, "block was deleted!") // Check for common attributes: if (STREQ(name, "name")) return PyString_FromString((((ID*)block->data)->name)+2); else if (STREQ(name, "block_type")) return PyString_FromString(block->type); else if (STREQ(name, "users")) return PyInt_FromLong(((ID*)block->data)->us); // // the following datablock types have methods: switch (DataBlock_type(block)) { case ID_OB: ret = Py_FindMethod(Object_methods, self, name); break; case ID_IP: ret = Py_FindMethod(Ipo_methods, self, name); break; case ID_CA: ret = Py_FindMethod(Camera_methods, self, name); break; case ID_MA: ret = Py_FindMethod(Material_methods, self, name); break; case ID_LA: ret = Py_FindMethod(Lamp_methods, self, name); break; case ID_TXT: ret = Py_FindMethod(Text_methods, self, name); break; } if (ret) return ret; PyErr_Clear(); // no method found, clear error // try common datablock methods ret = Py_FindMethod(commonDataBlock_methods, (PyObject*)self, name); if (ret) return ret; PyErr_Clear(); // try attributes from property list ret = datablock_getattr(block->properties, block->type, name, block->data); return ret; } int DataBlock_setattr(PyObject *self, char *name, PyObject *ob) { DataBlock *block= (DataBlock*) self; CHECK_VALIDDATA(block, "block was deleted!") if (STREQ(name, "name")) { if (!PyArg_Parse(ob, "s", &name)) return -1; new_id(block->type_list, (ID*)block->data, name); return 0; } return datablock_setattr(block->properties, block->type, name, block->data, ob); } PyTypeObject DataBlock_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "Block", /*tp_name*/ sizeof(DataBlock), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor) DataBlock_dealloc, /*tp_dealloc*/ (printfunc) 0, /*tp_print*/ (getattrfunc) DataBlock_getattr, /*tp_getattr*/ (setattrfunc) DataBlock_setattr, /*tp_setattr*/ (cmpfunc) 0, /*tp_compare*/ (reprfunc) DataBlock_repr, /*tp_repr*/ }; /**************************************************************************/ /**********************/ /* Texture Datablocks */ /* DATABLOCK_GET(Texturemodule, texture, getTextureList()) static struct PyMethodDef Texture_methods[] = { {"Get", Texture_Get, 1, Texture_Get_doc}, {NULL, NULL} }; */ /* ---------------------------------------------------------------------- */ int DataBlock_type(DataBlock *block) { return (GET_ID_TYPE((ID *) block->data)); } int ObjectDataIDType(DataBlock *block) { Object *ob; if (!DataBlock_isType(block, ID_OB)) return -1; ob = (Object *) block->data; return GET_ID_TYPE((ID *) ob->data); } int DataBlock_isType(DataBlock *block, int type) { ID *id; if (!DataBlock_Check(block)) return 0; id= (ID *) block->data; return (GET_ID_TYPE(id))==type; } /** This function creates a Python datablock descriptor object from * the specified data pointer. This pointer must point to a structure * with a valid ID header. */ PyObject *DataBlock_fromData(void *data) { DataBlock *newb; ID *id= (ID *) data; int idn; if (!data) return BPY_incr_ret(Py_None); idn = GET_ID_TYPE(id); if (idn==ID_OB) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Object"; newb->type_list= getObjectList(); newb->properties= Object_Properties; } else if (idn==ID_ME) { #ifdef USE_NMESH return newNMesh(data); #else newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Mesh"; newb->type_list= getMeshList(); newb->properties= Mesh_Properties; #endif // } else if (idn==ID_CU) { /* Special case, should be fixed * by proper high-level NURBS access. * * Later. */ // return newNCurveObject(data); } else if (idn==ID_LA) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Lamp"; newb->type_list= getLampList(); newb->properties= Lamp_Properties; } else if (idn==ID_CA) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Camera"; newb->type_list= getCameraList(); newb->properties= Camera_Properties; } else if (idn==ID_MA) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Material"; newb->type_list= getMaterialList(); newb->properties= Material_Properties; } else if (idn==ID_WO) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "World"; newb->type_list= getWorldList(); newb->properties= World_Properties; } else if (idn==ID_IP) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Ipo"; newb->type_list= getIpoList(); newb->properties= Ipo_Properties; #ifdef EXPERIMENTAL } else if (idn==ID_TE) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Tex"; newb->type_list= getTextureList(); newb->properties= Texture_Properties; #endif } else if (idn==ID_IM) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Image"; newb->type_list= getImageList(); newb->properties= Image_Properties; } else if (idn==ID_TXT) { newb= PyObject_NEW(DataBlock, &DataBlock_Type); newb->type= "Text"; newb->type_list= getTextList(); newb->properties= Text_Properties; } else return BPY_err_ret_ob(PyExc_SystemError, "unable to create Block for data"); newb->data= data; #ifdef REF_USERCOUNT BOB_INCUSER(id); // XXX abuse for refcount #endif return (PyObject *) newb; } PyObject *get_DataBlock_func(void **ptr) { ID *id= (ID*) *ptr; return DataBlock_fromData(id); } /* ---------------------------------------------------------------------- */ /* INIT ROUTINE */ void init_types(PyObject *dict) { PyObject *tmod, *tdict; tmod= Py_InitModule("Blender.Types", Null_methods); PyDict_SetItemString(dict, "Types", tmod); tdict= PyModule_GetDict(tmod); PyDict_SetItemString(tdict, "IpoCurve", (PyObject *)&PyIpoCurve_Type); PyDict_SetItemString(tdict, "BezTriple", (PyObject *)&PyBezTriple_Type); PyDict_SetItemString(tdict, "ButtonType", (PyObject *)&Button_Type); PyDict_SetItemString(tdict, "BufferType", (PyObject *)&Buffer_Type); PyDict_SetItemString(tdict, "NMeshType", (PyObject *)&NMesh_Type); PyDict_SetItemString(tdict, "NMFaceType", (PyObject *)&NMFace_Type); PyDict_SetItemString(tdict, "NMVertType", (PyObject *)&NMVert_Type); PyDict_SetItemString(tdict, "NMColType", (PyObject *)&NMCol_Type); PyDict_SetItemString(tdict, "BlockType", (PyObject *)&DataBlock_Type); /* Setup external types */ PyDict_SetItemString(tdict, "VectorType", (PyObject *)&Vector_Type); PyDict_SetItemString(tdict, "MatrixType", (PyObject *)&Matrix_Type); } #undef BPY_ADDCONST #define BPY_ADDCONST(dict, name) insertConst(dict, #name, PyInt_FromLong(LA_##name)) PyObject *initLamp(void) { PyObject *mod, *dict, *d; mod= Py_InitModule(MODNAME(BLENDERMODULE) ".Lamp", Lampmodule_methods); dict= PyModule_GetDict(mod); d = ConstObject_New(); PyDict_SetItemString(dict, "Types", d); /* type */ BPY_ADDCONST(d, LOCAL); BPY_ADDCONST(d, SUN); BPY_ADDCONST(d, SPOT); BPY_ADDCONST(d, HEMI); d = ConstObject_New(); PyDict_SetItemString(dict, "Modes", d); /* mode */ BPY_ADDCONST(d, SHAD); BPY_ADDCONST(d, HALO); BPY_ADDCONST(d, LAYER); BPY_ADDCONST(d, QUAD); BPY_ADDCONST(d, NEG); BPY_ADDCONST(d, ONLYSHADOW); BPY_ADDCONST(d, SPHERE); BPY_ADDCONST(d, SQUARE); BPY_ADDCONST(d, TEXTURE); BPY_ADDCONST(d, OSATEX); BPY_ADDCONST(d, DEEP_SHADOW); return mod; } PyObject *initMaterial(void) { PyObject *mod, *dict, *d; mod= Py_InitModule(MODNAME(BLENDERMODULE) ".Material", Materialmodule_methods); dict= PyModule_GetDict(mod); d = ConstObject_New(); PyDict_SetItemString(dict, "Modes", d); /* MATERIAL MODES * ...some of these have really cryptic defines :-) * We try to match them to the GUI descriptions... */ #undef BPY_ADDCONST #define BPY_ADDCONST(dict, name) \ insertConst(dict, #name, PyInt_FromLong(MA_##name)) insertConst(d, "TRACEABLE", PyInt_FromLong(MA_TRACEBLE)); BPY_ADDCONST(d, SHADOW); insertConst(d, "SHADELESS", PyInt_FromLong(MA_SHLESS)); BPY_ADDCONST(d, WIRE); insertConst(d, "VCOL_LIGHT", PyInt_FromLong(MA_VERTEXCOL)); BPY_ADDCONST(d, HALO); insertConst(d, "ZTRANSP", PyInt_FromLong(MA_ZTRA)); insertConst(d, "VCOL_PAINT", PyInt_FromLong(MA_VERTEXCOLP)); insertConst(d, "ZINVERT", PyInt_FromLong(MA_ZINV)); BPY_ADDCONST(d, ONLYSHADOW); BPY_ADDCONST(d, STAR); insertConst(d, "TEXFACE", PyInt_FromLong(MA_FACETEXTURE)); BPY_ADDCONST(d, NOMIST); /* HALO MODES */ d = ConstObject_New(); PyDict_SetItemString(dict, "HaloModes", d); #undef BPY_ADDCONST #define BPY_ADDCONST(dict, name) \ insertConst(dict, #name, PyInt_FromLong(MA_HALO_##name)) BPY_ADDCONST(d, RINGS); BPY_ADDCONST(d, LINES); insertConst(d, "TEX", PyInt_FromLong(MA_HALOTEX)); insertConst(d, "PUNO", PyInt_FromLong(MA_HALOPUNO)); BPY_ADDCONST(d, SHADE); BPY_ADDCONST(d, FLARE); return mod; } void init_Datablockmodules(PyObject *dict) { #define MODLOAD(name) PyDict_SetItemString(dict, #name, Py_InitModule(MODNAME(BLENDERMODULE) "." #name, name##module_methods)) DataBlock_Type.ob_type = &PyType_Type; PyIpoCurve_Type.ob_type= &PyType_Type; PyBezTriple_Type.ob_type= &PyType_Type; PyDict_SetItemString(dict, "Object", initObject()); PyDict_SetItemString(dict, "Lamp", initLamp()); PyDict_SetItemString(dict, "Material", initMaterial()); PyDict_SetItemString(dict, "Ipo", initIpo()); PyDict_SetItemString(dict, "Scene", initScene()); MODLOAD(Text); // MODLOAD(Mesh); MODLOAD(Camera); MODLOAD(World); MODLOAD(Image); /* MODLOAD(Texture); */ }