Bugfix for FBX export for animations

in 2.48 constant interpolations meant that wasnt a problem but since it now uses linear interp. you can notice errors with animated characters because the 2 eulers are not compatible.

Added optional euler_compat argument to matrix.toEuler(eul) and quat.toEuler(eul) so when getting the euler rotations from a list of matrices the animation curve will be continues.
Also added euler.makeCompatible(euler).

- warning silenced for imagepaint.c
This commit is contained in:
Campbell Barton 2009-05-18 04:11:54 +00:00
parent 9a40f4d2a6
commit 5a16f0b60c
9 changed files with 100 additions and 20 deletions

@ -2591,8 +2591,18 @@ Takes: {''')
for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
if TX_CHAN=='T': context_bone_anim_vecs = [mtx[0].translationPart() for mtx in context_bone_anim_mats]
elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].toEuler() for mtx in context_bone_anim_mats]
else: context_bone_anim_vecs = [mtx[0].scalePart() for mtx in context_bone_anim_mats]
elif TX_CHAN=='S': context_bone_anim_vecs = [mtx[0].scalePart() for mtx in context_bone_anim_mats]
elif TX_CHAN=='R':
# Was....
# elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].toEuler() for mtx in context_bone_anim_mats]
#
# ...but we need to use the previous euler for compatible conversion.
context_bone_anim_vecs = []
prev_eul = None
for mtx in context_bone_anim_mats:
if prev_eul: prev_eul = mtx[1].toEuler(prev_eul)
else: prev_eul = mtx[1].toEuler()
context_bone_anim_vecs.append(prev_eul)
file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation

@ -617,6 +617,12 @@ class Euler:
@rtype: Quaternion object
@return: Quaternion representation of the euler.
"""
def makeCompatible(eul_compat):
"""
Make this euler compatible with another, so interpolating between them works as expected.
@rtype: Euler object
@return: an instance of itself
"""
class Quaternion:
"""
@ -718,9 +724,11 @@ class Quaternion:
@return: an instance of itself
"""
def toEuler():
def toEuler(eul_compat):
"""
Return Euler representation of the quaternion.
@type eul_compat: L{Euler}
@param eul_compat: Optional euler argument the new euler will be made compatible with (no axis flipping between them). Useful for converting a series of matrices to animation curves.
@rtype: Euler object
@return: Euler representation of the quaternion.
"""
@ -870,9 +878,11 @@ class Matrix:
@return: an instance of itself.
"""
def toEuler():
def toEuler(eul_compat):
"""
Return an Euler representation of the rotation matrix (3x3 or 4x4 matrix only).
@type eul_compat: L{Euler}
@param eul_compat: Optional euler argument the new euler will be made compatible with (no axis flipping between them). Useful for converting a series of matrices to animation curves.
@rtype: Euler object
@return: Euler representation of the rotation matrix.
"""

@ -40,6 +40,7 @@ char Euler_ToMatrix_doc[] = "() - returns a rotation matrix representing the eul
char Euler_ToQuat_doc[] = "() - returns a quaternion representing the euler rotation";
char Euler_Rotate_doc[] = "() - rotate a euler by certain amount around an axis of rotation";
char Euler_copy_doc[] = "() - returns a copy of the euler.";
char Euler_MakeCompatible_doc[] = "(euler) - Make this user compatible with another (no axis flipping).";
//-----------------------METHOD DEFINITIONS ----------------------
struct PyMethodDef Euler_methods[] = {
{"zero", (PyCFunction) Euler_Zero, METH_NOARGS, Euler_Zero_doc},
@ -47,6 +48,7 @@ struct PyMethodDef Euler_methods[] = {
{"toMatrix", (PyCFunction) Euler_ToMatrix, METH_NOARGS, Euler_ToMatrix_doc},
{"toQuat", (PyCFunction) Euler_ToQuat, METH_NOARGS, Euler_ToQuat_doc},
{"rotate", (PyCFunction) Euler_Rotate, METH_VARARGS, Euler_Rotate_doc},
{"makeCompatible", (PyCFunction) Euler_MakeCompatible, METH_O, Euler_MakeCompatible_doc},
{"__copy__", (PyCFunction) Euler_copy, METH_VARARGS, Euler_copy_doc},
{"copy", (PyCFunction) Euler_copy, METH_VARARGS, Euler_copy_doc},
{NULL, NULL, 0, NULL}
@ -173,6 +175,32 @@ PyObject *Euler_Rotate(EulerObject * self, PyObject *args)
Py_INCREF(self);
return (PyObject *)self;
}
PyObject *Euler_MakeCompatible(EulerObject * self, EulerObject *value)
{
float eul_from_rad[3];
int x;
if(!EulerObject_Check(value)) {
PyErr_SetString(PyExc_TypeError, "euler.makeCompatible(euler):expected a single euler argument.");
return NULL;
}
//covert to radians
for(x = 0; x < 3; x++) {
self->eul[x] = self->eul[x] * ((float)Py_PI / 180);
eul_from_rad[x] = value->eul[x] * ((float)Py_PI / 180);
}
compatible_eul(self->eul, eul_from_rad);
//convert back from radians
for(x = 0; x < 3; x++) {
self->eul[x] *= (180 / (float)Py_PI);
}
Py_INCREF(self);
return (PyObject *)self;
}
//----------------------------Euler.rotate()-----------------------
// return a copy of the euler
PyObject *Euler_copy(EulerObject * self, PyObject *args)
@ -528,4 +556,3 @@ PyObject *newEulerObject(float *eul, int type)
}
return (PyObject *)self;
}

@ -58,6 +58,7 @@ PyObject *Euler_Unique( EulerObject * self );
PyObject *Euler_ToMatrix( EulerObject * self );
PyObject *Euler_ToQuat( EulerObject * self );
PyObject *Euler_Rotate( EulerObject * self, PyObject *args );
PyObject *Euler_MakeCompatible( EulerObject * self, EulerObject *value );
PyObject *Euler_copy( EulerObject * self, PyObject *args );
PyObject *newEulerObject( float *eul, int type );

@ -41,7 +41,7 @@ char Matrix_TranslationPart_doc[] = "() - return a vector encompassing the trans
char Matrix_RotationPart_doc[] = "() - return a vector encompassing the rotation of the matrix";
char Matrix_scalePart_doc[] = "() - convert matrix to a 3D vector";
char Matrix_Resize4x4_doc[] = "() - resize the matrix to a 4x4 square matrix";
char Matrix_toEuler_doc[] = "() - convert matrix to a euler angle rotation";
char Matrix_toEuler_doc[] = "(eul_compat) - convert matrix to a euler angle rotation, optional euler argument that the new euler will be made compatible with.";
char Matrix_toQuat_doc[] = "() - convert matrix to a quaternion rotation";
char Matrix_copy_doc[] = "() - return a copy of the matrix";
/*-----------------------METHOD DEFINITIONS ----------------------*/
@ -55,7 +55,7 @@ struct PyMethodDef Matrix_methods[] = {
{"rotationPart", (PyCFunction) Matrix_RotationPart, METH_NOARGS, Matrix_RotationPart_doc},
{"scalePart", (PyCFunction) Matrix_scalePart, METH_NOARGS, Matrix_scalePart_doc},
{"resize4x4", (PyCFunction) Matrix_Resize4x4, METH_NOARGS, Matrix_Resize4x4_doc},
{"toEuler", (PyCFunction) Matrix_toEuler, METH_NOARGS, Matrix_toEuler_doc},
{"toEuler", (PyCFunction) Matrix_toEuler, METH_VARARGS, Matrix_toEuler_doc},
{"toQuat", (PyCFunction) Matrix_toQuat, METH_NOARGS, Matrix_toQuat_doc},
{"copy", (PyCFunction) Matrix_copy, METH_NOARGS, Matrix_copy_doc},
{"__copy__", (PyCFunction) Matrix_copy, METH_NOARGS, Matrix_copy_doc},
@ -81,19 +81,32 @@ PyObject *Matrix_toQuat(MatrixObject * self)
return newQuaternionObject(quat, Py_NEW);
}
/*---------------------------Matrix.toEuler() --------------------*/
PyObject *Matrix_toEuler(MatrixObject * self)
PyObject *Matrix_toEuler(MatrixObject * self, PyObject *args)
{
float eul[3];
float eul[3], eul_compatf[3];
EulerObject *eul_compat = NULL;
int x;
if(!PyArg_ParseTuple(args, "|O!:toEuler", &euler_Type, &eul_compat))
return NULL;
if(eul_compat) {
for(x = 0; x < 3; x++) {
eul_compatf[x] = eul_compat->eul[x] * ((float)Py_PI / 180);
}
}
/*must be 3-4 cols, 3-4 rows, square matrix*/
if(self->colSize ==3 && self->rowSize ==3) {
Mat3ToEul((float (*)[3])*self->matrix, eul);
if(eul_compat) Mat3ToCompatibleEul((float (*)[3])*self->matrix, eul, eul_compatf);
else Mat3ToEul((float (*)[3])*self->matrix, eul);
}else if (self->colSize ==4 && self->rowSize ==4) {
float tempmat3[3][3];
Mat3CpyMat4(tempmat3, (float (*)[4])*self->matrix);
Mat3ToEul(tempmat3, eul);
if(eul_compat) Mat3ToCompatibleEul(tempmat3, eul, eul_compatf);
else Mat3ToEul(tempmat3, eul);
}else {
PyErr_SetString(PyExc_AttributeError, "Matrix.toEuler(): inappropriate matrix size - expects 3x3 or 4x4 matrix\n");
return NULL;

@ -69,7 +69,7 @@ PyObject *Matrix_TranslationPart( MatrixObject * self );
PyObject *Matrix_RotationPart( MatrixObject * self );
PyObject *Matrix_scalePart( MatrixObject * self );
PyObject *Matrix_Resize4x4( MatrixObject * self );
PyObject *Matrix_toEuler( MatrixObject * self );
PyObject *Matrix_toEuler( MatrixObject * self, PyObject *args );
PyObject *Matrix_toQuat( MatrixObject * self );
PyObject *Matrix_copy( MatrixObject * self );
PyObject *newMatrixObject(float *mat, int rowSize, int colSize, int type);

@ -39,7 +39,7 @@ char Quaternion_Negate_doc[] = "() - set all values in the quaternion to their n
char Quaternion_Conjugate_doc[] = "() - set the quaternion to it's conjugate";
char Quaternion_Inverse_doc[] = "() - set the quaternion to it's inverse";
char Quaternion_Normalize_doc[] = "() - normalize the vector portion of the quaternion";
char Quaternion_ToEuler_doc[] = "() - return a euler rotation representing the quaternion";
char Quaternion_ToEuler_doc[] = "(eul_compat) - return a euler rotation representing the quaternion, optional euler argument that the new euler will be made compatible with.";
char Quaternion_ToMatrix_doc[] = "() - return a rotation matrix representing the quaternion";
char Quaternion_copy_doc[] = "() - return a copy of the quat";
//-----------------------METHOD DEFINITIONS ----------------------
@ -49,7 +49,7 @@ struct PyMethodDef Quaternion_methods[] = {
{"conjugate", (PyCFunction) Quaternion_Conjugate, METH_NOARGS, Quaternion_Conjugate_doc},
{"inverse", (PyCFunction) Quaternion_Inverse, METH_NOARGS, Quaternion_Inverse_doc},
{"normalize", (PyCFunction) Quaternion_Normalize, METH_NOARGS, Quaternion_Normalize_doc},
{"toEuler", (PyCFunction) Quaternion_ToEuler, METH_NOARGS, Quaternion_ToEuler_doc},
{"toEuler", (PyCFunction) Quaternion_ToEuler, METH_VARARGS, Quaternion_ToEuler_doc},
{"toMatrix", (PyCFunction) Quaternion_ToMatrix, METH_NOARGS, Quaternion_ToMatrix_doc},
{"__copy__", (PyCFunction) Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
{"copy", (PyCFunction) Quaternion_copy, METH_NOARGS, Quaternion_copy_doc},
@ -58,12 +58,30 @@ struct PyMethodDef Quaternion_methods[] = {
//-----------------------------METHODS------------------------------
//----------------------------Quaternion.toEuler()------------------
//return the quat as a euler
PyObject *Quaternion_ToEuler(QuaternionObject * self)
PyObject *Quaternion_ToEuler(QuaternionObject * self, PyObject *args)
{
float eul[3];
EulerObject *eul_compat = NULL;
int x;
QuatToEul(self->quat, eul);
if(!PyArg_ParseTuple(args, "|O!:toEuler", &euler_Type, &eul_compat))
return NULL;
if(eul_compat) {
float mat[3][3], eul_compatf[3];
for(x = 0; x < 3; x++) {
eul_compatf[x] = eul_compat->eul[x] * ((float)Py_PI / 180);
}
QuatToMat3(self->quat, mat);
Mat3ToCompatibleEul(mat, eul, eul_compatf);
}
else {
QuatToEul(self->quat, eul);
}
for(x = 0; x < 3; x++) {
eul[x] *= (180 / (float)Py_PI);
}

@ -62,7 +62,7 @@ PyObject *Quaternion_Negate( QuaternionObject * self );
PyObject *Quaternion_Conjugate( QuaternionObject * self );
PyObject *Quaternion_Inverse( QuaternionObject * self );
PyObject *Quaternion_Normalize( QuaternionObject * self );
PyObject *Quaternion_ToEuler( QuaternionObject * self );
PyObject *Quaternion_ToEuler( QuaternionObject * self, PyObject *args );
PyObject *Quaternion_ToMatrix( QuaternionObject * self );
PyObject *Quaternion_copy( QuaternionObject * self );
PyObject *newQuaternionObject( float *quat, int type );

@ -2603,7 +2603,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
if (!is_ortho) {
pixelScreenCo[3] = 1.0f;
Mat4MulVec4fl(ps->projectMat, pixelScreenCo);
Mat4MulVec4fl((float (*)[4])*ps->projectMat, pixelScreenCo);
pixelScreenCo[0] = (float)(curarea->winx/2.0f)+(curarea->winx/2.0f)*pixelScreenCo[0]/pixelScreenCo[3];
pixelScreenCo[1] = (float)(curarea->winy/2.0f)+(curarea->winy/2.0f)*pixelScreenCo[1]/pixelScreenCo[3];
pixelScreenCo[2] = pixelScreenCo[2]/pixelScreenCo[3]; /* Use the depth for bucket point occlusion */
@ -4664,3 +4664,4 @@ void imagepaint_pick(short mousebutton)
sample_vpaint();
}