BGE Action Actuator setChannel() function was broken in a number of ways.

* extract_pose_from_pose only checked one of the list items for NULL when looping over them yet its possible they are different sizes.
* game_free_pose needed to be used rather then MEM_freeN, channels would never be freed leaking memory.
* setChannel() would make a new pose that wasnt aligned with the existing pose, the lists are assumed aligned so when extracting the channels its unlikely this was ever useful.
* Added getChannel() - returns pose loc/size/quat
* Added option args for setChannel(channel, matrix) or setChannel(channel, loc, size, quat)
This commit is contained in:
Campbell Barton 2009-06-16 18:25:48 +00:00
parent aa0825a5e5
commit 2faf20c4b3
4 changed files with 116 additions and 81 deletions

@ -798,6 +798,7 @@ void calc_action_range(const bAction *act, float *start, float *end, int incl_hi
/* Copy the data from the action-pose (src) into the pose */
/* both args are assumed to be valid */
/* exported to game engine */
/* Note! this assumes both poses are aligned, this isnt always true when dealing with user poses */
void extract_pose_from_pose(bPose *pose, const bPose *src)
{
const bPoseChannel *schan;
@ -808,7 +809,7 @@ void extract_pose_from_pose(bPose *pose, const bPose *src)
return;
}
for (schan=src->chanbase.first; schan; schan=schan->next, pchan= pchan->next) {
for (schan=src->chanbase.first; (schan && pchan); schan=schan->next, pchan= pchan->next) {
copy_pose_channel_data(pchan, schan);
}
}

@ -50,6 +50,7 @@
#include "BKE_utildefines.h"
#include "FloatValue.h"
#include "PyObjectPlus.h"
#include "KX_PyMath.h"
#include "blendef.h"
#ifdef HAVE_CONFIG_H
@ -366,8 +367,7 @@ bool BL_ActionActuator::Update(double curtime, bool frame)
/* Perform the user override (if any) */
if (m_userpose){
extract_pose_from_pose(m_pose, m_userpose);
// clear_pose(m_userpose);
MEM_freeN(m_userpose);
game_free_pose(m_userpose); //cant use MEM_freeN(m_userpose) because the channels need freeing too.
m_userpose = NULL;
}
#if 1
@ -767,22 +767,55 @@ PyObject* BL_ActionActuator::PySetFrameProperty(PyObject* args,
Py_RETURN_NONE;
}
/*
PyObject* BL_ActionActuator::PyGetChannel(PyObject* args,
PyObject* kwds) {
char *string;
PyObject* BL_ActionActuator::PyGetChannel(PyObject* value) {
char *string= PyString_AsString(value);
if (PyArg_ParseTuple(args,"s:getChannel",&string))
{
m_propname = string;
}
else {
if (!string) {
PyErr_SetString(PyExc_TypeError, "expected a single string");
return NULL;
}
Py_RETURN_NONE;
}
bPoseChannel *pchan;
// get_pose_channel accounts for NULL pose, run on both incase one exists but
// the channel doesnt
if( !(pchan=get_pose_channel(m_userpose, string)) &&
!(pchan=get_pose_channel(m_pose, string)) )
{
PyErr_SetString(PyExc_ValueError, "channel doesnt exist");
return NULL;
}
PyObject *ret = PyTuple_New(3);
PyObject *list = PyList_New(3);
PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->loc[0]));
PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->loc[1]));
PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->loc[2]));
PyTuple_SET_ITEM(ret, 0, list);
list = PyList_New(3);
PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->size[0]));
PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->size[1]));
PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->size[2]));
PyTuple_SET_ITEM(ret, 1, list);
list = PyList_New(4);
PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->quat[0]));
PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->quat[1]));
PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->quat[2]));
PyList_SET_ITEM(list, 3, PyFloat_FromDouble(pchan->quat[3]));
PyTuple_SET_ITEM(ret, 2, list);
return ret;
/*
return Py_BuildValue("([fff][fff][ffff])",
pchan->loc[0], pchan->loc[1], pchan->loc[2],
pchan->size[0], pchan->size[1], pchan->size[2],
pchan->quat[0], pchan->quat[1], pchan->quat[2], pchan->quat[3] );
*/
}
/* getType */
const char BL_ActionActuator::GetType_doc[] =
@ -857,76 +890,69 @@ KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
"\t - matrix : A 4x4 matrix specifying the overriding transformation\n"
"\t as an offset from the bone's rest position.\n")
{
float matrix[4][4];
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
char *string;
PyObject* pylist;
bool error = false;
int row,col;
int mode = 0; /* 0 for bone space, 1 for armature/world space */
PyObject *pymat= NULL;
PyObject *pyloc= NULL, *pysize= NULL, *pyquat= NULL;
bPoseChannel *pchan;
if (!PyArg_ParseTuple(args,"sO|i:setChannel", &string, &pylist, &mode))
if(PyTuple_Size(args)==2) {
if (!PyArg_ParseTuple(args,"sO:setChannel", &string, &pymat)) // matrix
return NULL;
}
else if(PyTuple_Size(args)==4) {
if (!PyArg_ParseTuple(args,"sOOO:setChannel", &string, &pyloc, &pysize, &pyquat)) // loc/size/quat
return NULL;
}
else {
PyErr_SetString(PyExc_ValueError, "Expected a string and a 4x4 matrix (2 args) or a string and loc/size/quat sequences (4 args)");
return NULL;
if (pylist->ob_type == &CListValue::Type)
{
CListValue* listval = (CListValue*) pylist;
if (listval->GetCount() == 4)
{
for (row=0;row<4;row++) // each row has a 4-vector [x,y,z, w]
{
CListValue* vecval = (CListValue*)listval->GetValue(row);
for (col=0;col<4;col++)
{
matrix[row][col] = vecval->GetValue(col)->GetNumber();
}
}
}
else
{
error = true;
}
}
else
{
// assert the list is long enough...
int numitems = PyList_Size(pylist);
if (numitems == 4)
{
for (row=0;row<4;row++) // each row has a 4-vector [x,y,z, w]
{
PyObject* veclist = PyList_GetItem(pylist,row); // here we have a vector4 list
for (col=0;col<4;col++)
{
matrix[row][col] = PyFloat_AsDouble(PyList_GetItem(veclist,col));
}
}
}
else
{
error = true;
}
}
if (!error)
{
/* DO IT HERE */
bPoseChannel *pchan= verify_pose_channel(m_userpose, string);
Mat4ToQuat(matrix, pchan->quat);
Mat4ToSize(matrix, pchan->size);
VECCOPY (pchan->loc, matrix[3]);
if(pymat) {
float matrix[4][4];
MT_Matrix4x4 mat;
pchan->flag |= POSE_ROT|POSE_LOC|POSE_SIZE;
if (!m_userpose){
m_userpose = (bPose*)MEM_callocN(sizeof(bPose), "userPose");
if(!PyMatTo(pymat, mat))
return NULL;
mat.setValue((const float *)matrix);
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
if (!m_userpose) {
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
game_copy_pose(&m_userpose, m_pose);
}
pchan= verify_pose_channel(m_userpose, string); // adds the channel if its not there.
VECCOPY (pchan->loc, matrix[3]);
Mat4ToSize(matrix, pchan->size);
Mat4ToQuat(matrix, pchan->quat);
}
else {
MT_Vector3 loc;
MT_Vector3 size;
MT_Vector4 quat;
if (!PyVecTo(pyloc, loc) || !PyVecTo(pysize, size) || !PyVecTo(pyquat, quat))
return NULL;
// same as above
if (!m_userpose) {
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
game_copy_pose(&m_userpose, m_pose);
}
pchan= verify_pose_channel(m_userpose, string);
// for some reason loc.setValue(pchan->loc) fails
pchan->loc[0]= loc[0]; pchan->loc[1]= loc[1]; pchan->loc[2]= loc[2];
pchan->size[0]= size[0]; pchan->size[1]= size[1]; pchan->size[2]= size[2];
pchan->quat[0]= quat[0]; pchan->quat[1]= quat[1]; pchan->quat[2]= quat[2]; pchan->quat[3]= quat[3];
}
pchan->flag |= POSE_ROT|POSE_LOC|POSE_SIZE;
Py_RETURN_NONE;
}
@ -986,7 +1012,7 @@ PyMethodDef BL_ActionActuator::Methods[] = {
{"getFrame", (PyCFunction) BL_ActionActuator::sPyGetFrame, METH_VARARGS, (PY_METHODCHAR)GetFrame_doc},
{"getProperty", (PyCFunction) BL_ActionActuator::sPyGetProperty, METH_VARARGS, (PY_METHODCHAR)GetProperty_doc},
{"getFrameProperty", (PyCFunction) BL_ActionActuator::sPyGetFrameProperty, METH_VARARGS, (PY_METHODCHAR)GetFrameProperty_doc},
// {"getChannel", (PyCFunction) BL_ActionActuator::sPyGetChannel, METH_VARARGS},
{"getChannel", (PyCFunction) BL_ActionActuator::sPyGetChannel, METH_O},
{"getType", (PyCFunction) BL_ActionActuator::sPyGetType, METH_VARARGS, (PY_METHODCHAR)GetType_doc},
{"setType", (PyCFunction) BL_ActionActuator::sPySetType, METH_VARARGS, (PY_METHODCHAR)SetType_doc},
{"getContinue", (PyCFunction) BL_ActionActuator::sPyGetContinue, METH_NOARGS, 0},

@ -104,7 +104,7 @@ public:
KX_PYMETHOD_DOC(BL_ActionActuator,GetFrame);
KX_PYMETHOD_DOC(BL_ActionActuator,GetProperty);
KX_PYMETHOD_DOC(BL_ActionActuator,GetFrameProperty);
// KX_PYMETHOD(BL_ActionActuator,GetChannel);
KX_PYMETHOD_O(BL_ActionActuator,GetChannel);
KX_PYMETHOD_DOC(BL_ActionActuator,GetType);
KX_PYMETHOD_DOC(BL_ActionActuator,SetType);
KX_PYMETHOD_NOARGS(BL_ActionActuator,GetContinue);

@ -336,15 +336,23 @@ class BL_ActionActuator(SCA_IActuator):
@ivar framePropName: The name of the property that is set to the current frame number.
@type framePropName: string
"""
def setChannel(channel, matrix, mode = False):
def setChannel(channel, matrix):
"""
@param channel: A string specifying the name of the bone channel.
Alternative to the 2 arguments, 4 arguments (channel, matrix, loc, size, quat) are also supported.
@param channel: A string specifying the name of the bone channel, created if missing.
@type channel: string
@param matrix: A 4x4 matrix specifying the overriding transformation
as an offset from the bone's rest position.
@type matrix: list [[float]]
@param mode: True for armature/world space, False for bone space
@type mode: boolean
"""
def getChannel(channel):
"""
@param channel: A string specifying the name of the bone channel. error raised if missing.
@type channel: string
@rtype: tuple
@return: (loc, size, quat)
"""
#{ Deprecated