Index: source/gameengine/Ketsji/KX_PythonInit.cpp =================================================================== --- source/gameengine/Ketsji/KX_PythonInit.cpp (revision 20037) +++ source/gameengine/Ketsji/KX_PythonInit.cpp (working copy) @@ -1708,6 +1708,9 @@ /* since python restarts we cant let the python backup of the sys.path hang around in a global pointer */ restorePySysPath(); /* get back the original sys.path and clear the backup */ + /* Modules that need freeing */ + Mathutils_Free(NULL); + Py_Finalize(); bpy_import_main_set(NULL); } Index: source/blender/python/api2_2x/Mathutils.c =================================================================== --- source/blender/python/api2_2x/Mathutils.c (revision 20037) +++ source/blender/python/api2_2x/Mathutils.c (working copy) @@ -107,6 +107,13 @@ /*----------------------------MODULE INIT-------------------------*/ /* from can be Blender.Mathutils or GameLogic.Mathutils for the BGE */ +static VectorMixedGetseters mixedGetseters = {NULL, NULL}; + +void Mathutils_Free(void * closure) +{ + Vector_DelGetseters(&mixedGetseters); +} + #if (PY_VERSION_HEX >= 0x03000000) static struct PyModuleDef M_Mathutils_module_def = { {}, /* m_base */ @@ -117,7 +124,7 @@ 0, /* m_reload */ 0, /* m_traverse */ 0, /* m_clear */ - 0, /* m_free */ + Mathutils_Free, /* m_free */ }; #endif @@ -126,10 +133,14 @@ PyObject *submodule; //seed the generator for the rand function - BLI_srand((unsigned int) (PIL_check_seconds_timer() * - 0x7FFFFFFF)); + BLI_srand((unsigned int) (PIL_check_seconds_timer() * 0x7FFFFFFF)); /* needed for getseters */ + if((vector_Type.tp_flags & Py_TPFLAGS_READY)==0) + if (Vector_AppendSwizzleGetseters(vector_Type.tp_getset, &mixedGetseters) != 0) + return NULL; + + vector_Type.tp_getset = mixedGetseters.getseters; if( PyType_Ready( &vector_Type ) < 0 ) return NULL; if( PyType_Ready( &matrix_Type ) < 0 ) @@ -147,6 +158,7 @@ return (submodule); } + //-----------------------------METHODS---------------------------- //----------------column_vector_multiplication (internal)--------- //COLUMN VECTOR Multiplication (Matrix X Vector) Index: source/blender/python/api2_2x/Mathutils.h =================================================================== --- source/blender/python/api2_2x/Mathutils.h (revision 20037) +++ source/blender/python/api2_2x/Mathutils.h (working copy) @@ -38,6 +38,8 @@ #include "euler.h" PyObject *Mathutils_Init( const char * from ); +void Mathutils_Free(void *); + PyObject *row_vector_multiplication(VectorObject* vec, MatrixObject * mat); PyObject *column_vector_multiplication(MatrixObject * mat, VectorObject* vec); PyObject *quat_rotation(PyObject *arg1, PyObject *arg2); Index: source/blender/python/api2_2x/vector.c =================================================================== --- source/blender/python/api2_2x/vector.c (revision 20037) +++ source/blender/python/api2_2x/vector.c (working copy) @@ -20,7 +20,7 @@ * All rights reserved. * * - * Contributor(s): Willian P. Germano & Joseph Gilbert, Ken Hughes + * Contributor(s): Willian P. Germano & Joseph Gilbert, Ken Hughes, Alex Fraser * * ***** END GPL LICENSE BLOCK ***** */ @@ -31,6 +31,13 @@ #include "BKE_utildefines.h" #include "BLI_arithb.h" +#define MAX_DIMENSIONS 4 +/* Swizzle axes get packed into a single value that is used as a closure. Each + axis uses SWIZZLE_BITS_PER_AXIS bits. The first bit (SWIZZLE_VALID_AXIS) is + used as a sentinel: if it is unset, the axis is not valid. */ +#define SWIZZLE_BITS_PER_AXIS 3 +#define SWIZZLE_VALID_AXIS 0x4 +#define SWIZZLE_AXIS 0x3 /*-------------------------DOC STRINGS ---------------------------*/ char Vector_Zero_doc[] = "() - set all values in the vector to 0"; @@ -42,6 +49,7 @@ char Vector_ToTrackQuat_doc[] = "(track, up) - extract a quaternion from the vector and the track and up axis"; char Vector_reflect_doc[] = "(mirror) - return a vector reflected on the mirror normal"; char Vector_copy_doc[] = "() - return a copy of the vector"; +char Vector_swizzle_doc[] = "Swizzle: Get or set axes in specified order"; /*-----------------------METHOD DEFINITIONS ----------------------*/ struct PyMethodDef Vector_methods[] = { {"zero", (PyCFunction) Vector_Zero, METH_NOARGS, Vector_Zero_doc}, @@ -1121,8 +1129,283 @@ {NULL,NULL,NULL,NULL,NULL} /* Sentinel */ }; +/* Get a new Vector according to the provided swizzle. This function has little + error checking, as we are in control of the inputs: the closure is set by us + in Vector_createSwizzleGetSeter. */ +static PyObject *Vector_getSwizzle(VectorObject * self, void *closure) +{ + size_t axisA; + size_t axisB; + float vec[MAX_DIMENSIONS]; + unsigned int swizzleClosure; + + /* Unpack the axes from the closure into an array. */ + axisA = 0; + swizzleClosure = (unsigned int) closure; + while (swizzleClosure & SWIZZLE_VALID_AXIS) + { + axisB = swizzleClosure & SWIZZLE_AXIS; + vec[axisA] = self->vec[axisB]; + swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS; + axisA++; + } + + return newVectorObject(vec, axisA, Py_NEW); +} +/* Set the items of this vector using a swizzle. + - If value is a vector or list this operates like an array copy, except that + the destination is effectively re-ordered as defined by the swizzle. At + most min(len(source), len(dest)) values will be copied. + - If the value is scalar, it is copied to all axes listed in the swizzle. + - If an axis appears more than once in the swizzle, the final occurrance is + the one that determines its value. + Returns 0 on success and -1 on failure. On failure, the vector will be + unchanged. */ +static int Vector_setSwizzle(VectorObject * self, PyObject * value, void *closure) +{ + VectorObject *vecVal; + PyObject *item; + size_t listLen; + float scalarVal; + + size_t axisB; + size_t axisA; + unsigned int swizzleClosure; + + float vecTemp[MAX_DIMENSIONS]; + + /* Check that the closure can be used with this vector: even 2D vectors have + swizzles defined for axes z and w, but they would be invalid. */ + swizzleClosure = (unsigned int) closure; + while (swizzleClosure & SWIZZLE_VALID_AXIS) + { + axisA = swizzleClosure & SWIZZLE_AXIS; + if (axisA >= self->size) + { + PyErr_SetString(PyExc_AttributeError, "Error: vector does not have specified axis.\n"); + return -1; + } + swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS; + } + + if (VectorObject_Check(value)) + { + /* Copy vector contents onto swizzled axes. */ + vecVal = (VectorObject*) value; + axisB = 0; + swizzleClosure = (unsigned int) closure; + while (swizzleClosure & SWIZZLE_VALID_AXIS && axisB < vecVal->size) + { + axisA = swizzleClosure & SWIZZLE_AXIS; + vecTemp[axisA] = vecVal->vec[axisB]; + + swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS; + axisB++; + } + memcpy(self->vec, vecTemp, axisB * sizeof(float)); + return 0; + } + else if (PyList_Check(value)) + { + /* Copy list contents onto swizzled axes. */ + listLen = PyList_Size(value); + swizzleClosure = (unsigned int) closure; + axisB = 0; + while (swizzleClosure & SWIZZLE_VALID_AXIS && axisB < listLen) + { + item = PyList_GetItem(value, axisB); + if (!PyNumber_Check(item)) + { + PyErr_SetString(PyExc_AttributeError, "Error: vector does not have specified axis.\n"); + return -1; + } + scalarVal = (float)PyFloat_AsDouble(item); + + axisA = swizzleClosure & SWIZZLE_AXIS; + vecTemp[axisA] = scalarVal; + + swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS; + axisB++; + } + memcpy(self->vec, vecTemp, axisB * sizeof(float)); + return 0; + } + else if (PyNumber_Check(value)) + { + /* Assign the same value to each axis. */ + scalarVal = (float)PyFloat_AsDouble(value); + swizzleClosure = (unsigned int) closure; + while (swizzleClosure & SWIZZLE_VALID_AXIS) + { + axisA = swizzleClosure & SWIZZLE_AXIS; + self->vec[axisA] = scalarVal; + + swizzleClosure = swizzleClosure >> SWIZZLE_BITS_PER_AXIS; + } + return 0; + } + else + { + PyErr_SetString( PyExc_TypeError, "Expected a Vector, list or scalar value." ); + return -1; + } +} + +/* Create a getseter that operates on the axes defined in swizzle. + Parameters: + gsd: An empty PyGetSetDef object. This will be modified. + swizzle: An array of axis indices. + dimensions: The number of axes to swizzle. Must be >= 2 and <= + MAX_DIMENSIONS. + name: A pointer to string that the name will be stored in. This is + purely to reduce the number of allocations. Before this function + returns, name will be advanced to the point immediately after + the name of the new getseter. Therefore, do not attempt to read + its contents. */ +static void Vector_createSwizzleGetSeter +( + PyGetSetDef *gsd, + unsigned short *swizzle, + size_t dimensions, + char **name +) +{ + const char axes[] = {'x', 'y', 'z', 'w'}; + unsigned int closure; + int i; + + /* Convert the index array into named axes. Store the name in the string + that was passed in, and make the getseter structure point to the same + address. */ + gsd->name = *name; + for (i = 0; i < dimensions; i++) + gsd->name[i] = axes[swizzle[i]]; + gsd->name[i] = '\0'; + /* Advance the name pointer to the next available address. */ + (*name) = (*name) + dimensions + 1; + + gsd->get = (getter)Vector_getSwizzle; + gsd->set = (setter)Vector_setSwizzle; + + gsd->doc = Vector_swizzle_doc; + + /* Pack the axes into a single value to use as the closure. Pack these in + in reverse so they come out in the right order when unpacked. */ + closure = 0; + for (i = MAX_DIMENSIONS - 1; i >= 0; i--) + { + closure = closure << SWIZZLE_BITS_PER_AXIS; + if (i < dimensions) + closure = closure | swizzle[i] | SWIZZLE_VALID_AXIS; + } + gsd->closure = (void*) closure; +} + +/* Create and append all implicit swizzle getseters to an existing array of + getseters. + Parameters: + explicitGsd: A null-terminated array of getseters that have been manually + defined. + resGds: The resultant array of getseters. This will be a combination of + static and manually-defined getseters. Use Vector_DelGetseters + to free this array. + + Returns: 0 on success, -1 on failure. On failure, resGsd will be left + untouched. */ +int Vector_AppendSwizzleGetseters(const PyGetSetDef *explicitGsd, VectorMixedGetseters *mixGsd) +{ + size_t len; + size_t nameLen; + unsigned int i; + unsigned short swizzle[MAX_DIMENSIONS]; + VectorMixedGetseters mixGsdTemp; + char *name; + + /* Count the explicit getseters. */ + for (len = 0; explicitGsd[len].name != NULL; len++); + + /* Then there are 4^4 + 4^3 + 4^2 = 336 swizzles. */ + len += 336; + /* Plus one sentinel. */ + len++; + + /* That means 4^4 names of length 4 + 1, 4^3 names of length 3 + 1, and 4^2 + names of length 2 + 1 (including a null character at the end of each) + = (4^4) * 5 + (4^3) * 4 + (4^2) * 3 + = 1584 */ + nameLen = 1584; + + mixGsdTemp.getseters = PyMem_New(PyGetSetDef, len); + if (mixGsdTemp.getseters == NULL) + { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory for swizzle getseters."); + return -1; + } + memset(mixGsdTemp.getseters, 0, len * sizeof(PyGetSetDef)); + + mixGsdTemp.names = PyMem_New(char, nameLen); + if (mixGsdTemp.names == NULL) + { + PyErr_SetString(PyExc_MemoryError, "Could not allocate memory for swizzle getseter names."); + PyMem_Del(mixGsdTemp.getseters); + return -1; + } + memset(mixGsdTemp.names, 0, nameLen*sizeof(char)); + + /* Do a shallow copy of the getseters. A deep clone can't be done because + we don't know how much memory each closure needs. */ + for (i = 0; explicitGsd[i].name != NULL; i++) + mixGsdTemp.getseters[i] = explicitGsd[i]; + + /* Create the swizzle functions. The pointer for name will be advanced by + Vector_createSwizzleGetSeter. */ + name = mixGsdTemp.names; + for (swizzle[0] = 0; swizzle[0] < MAX_DIMENSIONS; swizzle[0]++) + { + for (swizzle[1] = 0; swizzle[1] < MAX_DIMENSIONS; swizzle[1]++) + { + Vector_createSwizzleGetSeter(&mixGsdTemp.getseters[i++], swizzle, 2, &name); + for (swizzle[2] = 0; swizzle[2] < MAX_DIMENSIONS; swizzle[2]++) + { + Vector_createSwizzleGetSeter(&mixGsdTemp.getseters[i++], swizzle, 3, &name); + for (swizzle[3] = 0; swizzle[3] < MAX_DIMENSIONS; swizzle[3]++) + { + Vector_createSwizzleGetSeter(&mixGsdTemp.getseters[i++], swizzle, 4, &name); + } + } + } + } + + /* No need to add a sentinel - memory was initialised to zero above. */ + *mixGsd = mixGsdTemp; + return 0; +} + +/* Delete an array of getseters that was created by + Vector_AppendSwizzleGetseters. It is safe to call this even if the structure + members are NULL. */ +void Vector_DelGetseters(VectorMixedGetseters *mixGsd) +{ + /* Free strings that were allocated on the heap. */ + if (mixGsd->names != NULL) + { + PyMem_Del(mixGsd->names); + mixGsd->names = NULL; + } + + /* Free the structure arrays themselves. */ + if (mixGsd->getseters != NULL) + { + PyMem_Del(mixGsd->getseters); + mixGsd->getseters = NULL; + } + + /* for the blenderplayer that will initialize multiple times :| */ + vector_Type.tp_getset = Vector_getseters; +} + /* Note Py_TPFLAGS_CHECKTYPES allows us to avoid casting all types to Vector when coercing but this means for eg that @@ -1189,7 +1472,7 @@ /*** Attribute descriptor and subclassing stuff ***/ Vector_methods, /* struct PyMethodDef *tp_methods; */ NULL, /* struct PyMemberDef *tp_members; */ - Vector_getseters, /* struct PyGetSetDef *tp_getset; */ + Vector_getseters, /* struct PyGetSetDef *tp_getset; */ NULL, /* struct _typeobject *tp_base; */ NULL, /* PyObject *tp_dict; */ NULL, /* descrgetfunc tp_descr_get; */ Index: source/blender/python/api2_2x/vector.h =================================================================== --- source/blender/python/api2_2x/vector.h (revision 20037) +++ source/blender/python/api2_2x/vector.h (working copy) @@ -43,6 +43,19 @@ short wrapped; /* is wrapped data? */ } VectorObject; +/* An array of getseters, some of which have members on the stack and some on + the heap. + + Members: + getseters: The getseter structures. Terminated with a NULL sentinel. + names: All the names of the getseters that were allocated on the heap. + Each name is terminated with a null character, but there is + currently no way to find the length of this array. */ +typedef struct { + PyGetSetDef *getseters; /* members shallow-copied from the stack. */ + char *names; +} VectorMixedGetseters; + /*prototypes*/ PyObject *Vector_Zero( VectorObject * self ); PyObject *Vector_Normalize( VectorObject * self ); @@ -54,5 +67,7 @@ PyObject *Vector_reflect( VectorObject * self, PyObject * value ); PyObject *Vector_copy( VectorObject * self ); PyObject *newVectorObject(float *vec, int size, int type); +int Vector_AppendSwizzleGetseters(const PyGetSetDef *explicitGsd, VectorMixedGetseters *resGsd); +void Vector_DelGetseters(VectorMixedGetseters *getseters); #endif /* EXPP_vector_h */ Index: source/blender/python/BPY_interface.c =================================================================== --- source/blender/python/BPY_interface.c (revision 20037) +++ source/blender/python/BPY_interface.c (working copy) @@ -286,6 +286,8 @@ free_libblock( &G.main->script, script ); } + Mathutils_Free(NULL); + Py_Finalize( ); BPyMenu_RemoveAllEntries( ); /* freeing bpymenu mem */