This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/gameengine/Converter/BL_ActionActuator.cpp
Mitchell Stokes 5996ad2fd4 Fixes for T41168
after the completion of the action in "Flipper", layer is removed and the actuator mistakenly receive zero when trying to get the current frame

Patch Author: avrprj

Reviewers: moguri

Projects: #game_logic

Differential Revision: https://developer.blender.org/D906
2014-12-02 19:12:03 -08:00

651 lines
19 KiB
C++

/*
* ***** BEGIN GPL 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 LICENSE BLOCK *****
*/
/** \file gameengine/Converter/BL_ActionActuator.cpp
* \ingroup bgeconv
*/
#include "SCA_LogicManager.h"
#include "BL_ActionActuator.h"
#include "BL_ArmatureObject.h"
#include "BL_SkinDeformer.h"
#include "BL_Action.h"
#include "BL_ActionManager.h"
#include "KX_GameObject.h"
#include "STR_HashedString.h"
#include "MEM_guardedalloc.h"
#include "DNA_nla_types.h"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "MT_Matrix4x4.h"
#include "BKE_action.h"
#include "FloatValue.h"
#include "PyObjectPlus.h"
#include "KX_PyMath.h"
extern "C" {
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "RNA_access.h"
#include "RNA_define.h"
}
BL_ActionActuator::BL_ActionActuator(SCA_IObject *gameobj,
const STR_String& propname,
const STR_String& framepropname,
float starttime,
float endtime,
struct bAction *action,
short playtype,
short blend_mode,
short blendin,
short priority,
short layer,
float layer_weight,
short ipo_flags,
short end_reset,
float stride)
: SCA_IActuator(gameobj, KX_ACT_ACTION),
m_lastpos(0, 0, 0),
m_blendframe(0),
m_flag(0),
m_startframe (starttime),
m_endframe(endtime) ,
m_starttime(0),
m_localtime(starttime),
m_lastUpdate(-1),
m_blendin(blendin),
m_blendstart(0),
m_stridelength(stride),
m_layer_weight(layer_weight),
m_playtype(playtype),
m_blendmode(blend_mode),
m_priority(priority),
m_layer(layer),
m_ipo_flags(ipo_flags),
m_action(action),
m_propname(propname),
m_framepropname(framepropname)
{
if (!end_reset)
m_flag |= ACT_FLAG_CONTINUE;
};
BL_ActionActuator::~BL_ActionActuator()
{
}
void BL_ActionActuator::ProcessReplica()
{
SCA_IActuator::ProcessReplica();
m_localtime=m_startframe;
m_lastUpdate=-1;
}
void BL_ActionActuator::SetBlendTime(float newtime)
{
m_blendframe = newtime;
}
void BL_ActionActuator::SetLocalTime(float curtime)
{
float dt = (curtime-m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
if (m_endframe < m_startframe)
dt = -dt;
m_localtime = m_startframe + dt;
// Handle wrap around
if (m_localtime < min(m_startframe, m_endframe) || m_localtime > max(m_startframe, m_endframe))
{
switch (m_playtype) {
case ACT_ACTION_PLAY:
// Clamp
m_localtime = m_endframe;
break;
case ACT_ACTION_LOOP_END:
// Put the time back to the beginning
m_localtime = m_startframe;
m_starttime = curtime;
break;
case ACT_ACTION_PINGPONG:
// Swap the start and end frames
float temp = m_startframe;
m_startframe = m_endframe;
m_endframe = temp;
m_starttime = curtime;
m_flag ^= ACT_FLAG_REVERSE;
break;
}
}
}
void BL_ActionActuator::ResetStartTime(float curtime)
{
float dt = m_localtime - m_startframe;
m_starttime = curtime - dt / (KX_KetsjiEngine::GetAnimFrameRate());
//SetLocalTime(curtime);
}
CValue* BL_ActionActuator::GetReplica()
{
BL_ActionActuator* replica = new BL_ActionActuator(*this);//m_float,GetName());
replica->ProcessReplica();
return replica;
}
bool BL_ActionActuator::Update(double curtime, bool frame)
{
bool bNegativeEvent = false;
bool bPositiveEvent = false;
bool bUseContinue = false;
KX_GameObject *obj = (KX_GameObject*)GetParent();
short playtype = BL_Action::ACT_MODE_PLAY;
short blendmode = (m_blendmode == ACT_ACTION_ADD) ? BL_Action::ACT_BLEND_ADD : BL_Action::ACT_BLEND_BLEND;
float start = m_startframe;
float end = m_endframe;
// If we don't have an action, we can't do anything
if (!m_action)
return false;
// Convert our playtype to one that BL_Action likes
switch (m_playtype) {
case ACT_ACTION_LOOP_END:
case ACT_ACTION_LOOP_STOP:
playtype = BL_Action::ACT_MODE_LOOP;
break;
case ACT_ACTION_PINGPONG:
// We handle ping pong ourselves to increase compabitility
// with files made prior to animation changes from GSoC 2011.
playtype = BL_Action::ACT_MODE_PLAY;
if (m_flag & ACT_FLAG_REVERSE)
{
start = m_endframe;
end = m_startframe;
}
break;
case ACT_ACTION_FROM_PROP:
CValue* prop = GetParent()->GetProperty(m_propname);
// If we don't have a property, we can't do anything, so just bail
if (!prop) return false;
playtype = BL_Action::ACT_MODE_PLAY;
start = end = prop->GetNumber();
break;
}
if (m_flag & ACT_FLAG_CONTINUE)
bUseContinue = true;
// Handle events
if (frame)
{
bNegativeEvent = m_negevent;
bPositiveEvent = m_posevent;
RemoveAllEvents();
}
// "Active" actions need to keep updating their current frame
if (bUseContinue && (m_flag & ACT_FLAG_ACTIVE))
m_localtime = obj->GetActionFrame(m_layer);
if (m_flag & ACT_FLAG_ATTEMPT_PLAY)
SetLocalTime(curtime);
else
ResetStartTime(curtime);
// Handle a frame property if it's defined
if ((m_flag & ACT_FLAG_ACTIVE) && m_framepropname[0] != 0)
{
CValue* oldprop = obj->GetProperty(m_framepropname);
CValue* newval = new CFloatValue(obj->GetActionFrame(m_layer));
if (oldprop)
oldprop->SetValue(newval);
else
obj->SetProperty(m_framepropname, newval);
newval->Release();
}
// Handle a finished animation
if ((m_flag & ACT_FLAG_PLAY_END) && (m_flag & ACT_FLAG_ACTIVE) && obj->IsActionDone(m_layer))
{
m_flag &= ~ACT_FLAG_ACTIVE;
m_flag &= ~ACT_FLAG_ATTEMPT_PLAY;
if (m_playtype == ACT_ACTION_PINGPONG)
m_flag ^= ACT_FLAG_REVERSE;
else
return false;
}
// If a different action is playing, we've been overruled and are no longer active
if (obj->GetCurrentAction(m_layer) != m_action && !obj->IsActionDone(m_layer))
m_flag &= ~ACT_FLAG_ACTIVE;
if (bPositiveEvent || (m_flag & ACT_FLAG_ATTEMPT_PLAY && !(m_flag & ACT_FLAG_ACTIVE)))
{
if (bPositiveEvent && m_playtype == ACT_ACTION_PLAY)
{
if (obj->IsActionDone(m_layer))
m_localtime = start;
ResetStartTime(curtime);
}
if (obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, m_blendin, playtype, m_layer_weight, m_ipo_flags, 1.f, blendmode))
{
m_flag |= ACT_FLAG_ACTIVE;
if (bUseContinue)
obj->SetActionFrame(m_layer, m_localtime);
if (m_playtype == ACT_ACTION_PLAY || m_playtype == ACT_ACTION_PINGPONG)
m_flag |= ACT_FLAG_PLAY_END;
else
m_flag &= ~ACT_FLAG_PLAY_END;
}
m_flag |= ACT_FLAG_ATTEMPT_PLAY;
}
else if ((m_flag & ACT_FLAG_ACTIVE) && bNegativeEvent)
{
m_flag &= ~ACT_FLAG_ATTEMPT_PLAY;
m_localtime = obj->GetActionFrame(m_layer);
bAction *curr_action = obj->GetCurrentAction(m_layer);
if (curr_action && curr_action != m_action)
{
// Someone changed the action on us, so we wont mess with it
// Hopefully there wont be too many problems with two actuators using
// the same action...
m_flag &= ~ACT_FLAG_ACTIVE;
return false;
}
switch (m_playtype) {
case ACT_ACTION_LOOP_STOP:
obj->StopAction(m_layer); // Stop the action after getting the frame
// We're done
m_flag &= ~ACT_FLAG_ACTIVE;
return false;
case ACT_ACTION_LOOP_END:
// Convert into a play and let it finish
obj->SetPlayMode(m_layer, BL_Action::ACT_MODE_PLAY);
m_flag |= ACT_FLAG_PLAY_END;
break;
case ACT_ACTION_FLIPPER:
// Convert into a play action and play back to the beginning
float temp = end;
end = start;
start = curr_action ? obj->GetActionFrame(m_layer) : temp;
obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, 0, BL_Action::ACT_MODE_PLAY, m_layer_weight, m_ipo_flags, 1.f, blendmode);
m_flag |= ACT_FLAG_PLAY_END;
break;
}
}
return m_flag & ACT_FLAG_ACTIVE;
}
#ifdef WITH_PYTHON
/* ------------------------------------------------------------------------- */
/* Python functions */
/* ------------------------------------------------------------------------- */
PyObject *BL_ActionActuator::PyGetChannel(PyObject *value)
{
PyErr_SetString(PyExc_NotImplementedError, "BL_ActionActuator.getChannel() no longer works, please use BL_ArmatureObject.channels instead");
return NULL;
#if 0 // XXX To be removed in a later version (first removed in 2.64)
const char *string= _PyUnicode_AsString(value);
if (GetParent()->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE)
{
PyErr_SetString(PyExc_NotImplementedError, "actuator.getChannel(): Only armatures support channels");
return NULL;
}
if (!string) {
PyErr_SetString(PyExc_TypeError, "expected a single string");
return NULL;
}
bPoseChannel *pchan;
if (m_userpose==NULL && m_pose==NULL) {
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
}
// BKE_pose_channel_find_name accounts for NULL pose, run on both in case one exists but
// the channel doesnt
if ( !(pchan=BKE_pose_channel_find_name(m_userpose, string)) &&
!(pchan=BKE_pose_channel_find_name(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;
#if 0
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] );
#endif
#endif
}
/* setChannel */
KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
"setChannel(channel, matrix)\n"
"\t - channel : A string specifying the name of the bone channel.\n"
"\t - matrix : A 4x4 matrix specifying the overriding transformation\n"
"\t as an offset from the bone's rest position.\n")
{
PyErr_SetString(PyExc_NotImplementedError, "BL_ActionActuator.setChannel() no longer works, please use BL_ArmatureObject.channels instead");
return NULL;
#if 0 // XXX To be removed in a later version (first removed in 2.64)
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
char *string;
PyObject *pymat= NULL;
PyObject *pyloc= NULL, *pysize= NULL, *pyquat= NULL;
bPoseChannel *pchan;
if (GetParent()->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE)
{
PyErr_SetString(PyExc_NotImplementedError, "actuator.setChannel(): Only armatures support channels");
return NULL;
}
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 (pymat) {
float matrix[4][4];
MT_Matrix4x4 mat;
if (!PyMatTo(pymat, mat))
return NULL;
mat.getValue((float*)matrix);
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
if (!m_userpose) {
if (!m_pose)
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
game_copy_pose(&m_userpose, m_pose, 0);
}
// pchan= BKE_pose_channel_verify(m_userpose, string); // adds the channel if its not there.
pchan= BKE_pose_channel_find_name(m_userpose, string); // adds the channel if its not there.
if (pchan) {
copy_v3_v3(pchan->loc, matrix[3]);
mat4_to_size(pchan->size, matrix);
mat4_to_quat(pchan->quat, matrix);
}
}
else {
MT_Vector3 loc;
MT_Vector3 size;
MT_Quaternion quat;
if (!PyVecTo(pyloc, loc) || !PyVecTo(pysize, size) || !PyQuatTo(pyquat, quat))
return NULL;
// same as above
if (!m_userpose) {
if (!m_pose)
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
game_copy_pose(&m_userpose, m_pose, 0);
}
// pchan= BKE_pose_channel_verify(m_userpose, string);
pchan= BKE_pose_channel_find_name(m_userpose, string); // adds the channel if its not there.
// for some reason loc.setValue(pchan->loc) fails
if (pchan) {
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[3]; pchan->quat[1] = quat[0]; pchan->quat[2] = quat[1]; pchan->quat[3] = quat[2]; /* notice xyzw -> wxyz is intentional */
}
}
if (pchan==NULL) {
PyErr_SetString(PyExc_ValueError, "Channel could not be found, use the 'channelNames' attribute to get a list of valid channels");
return NULL;
}
Py_RETURN_NONE;
#endif
}
/* ------------------------------------------------------------------------- */
/* Python Integration Hooks */
/* ------------------------------------------------------------------------- */
PyTypeObject BL_ActionActuator::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"BL_ActionActuator",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,0,0,0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&SCA_IActuator::Type,
0,0,0,0,0,0,
py_base_new
};
PyMethodDef BL_ActionActuator::Methods[] = {
{"getChannel", (PyCFunction) BL_ActionActuator::sPyGetChannel, METH_O},
KX_PYMETHODTABLE(BL_ActionActuator, setChannel),
{NULL,NULL} //Sentinel
};
PyAttributeDef BL_ActionActuator::Attributes[] = {
KX_PYATTRIBUTE_FLOAT_RW("frameStart", 0, MAXFRAMEF, BL_ActionActuator, m_startframe),
KX_PYATTRIBUTE_FLOAT_RW("frameEnd", 0, MAXFRAMEF, BL_ActionActuator, m_endframe),
KX_PYATTRIBUTE_FLOAT_RW("blendIn", 0, MAXFRAMEF, BL_ActionActuator, m_blendin),
KX_PYATTRIBUTE_RW_FUNCTION("action", BL_ActionActuator, pyattr_get_action, pyattr_set_action),
KX_PYATTRIBUTE_RO_FUNCTION("channelNames", BL_ActionActuator, pyattr_get_channel_names),
KX_PYATTRIBUTE_SHORT_RW("priority", 0, 100, false, BL_ActionActuator, m_priority),
KX_PYATTRIBUTE_SHORT_RW("layer", 0, MAX_ACTION_LAYERS-1, true, BL_ActionActuator, m_layer),
KX_PYATTRIBUTE_FLOAT_RW("layerWeight", 0, 1.0, BL_ActionActuator, m_layer_weight),
KX_PYATTRIBUTE_RW_FUNCTION("frame", BL_ActionActuator, pyattr_get_frame, pyattr_set_frame),
KX_PYATTRIBUTE_STRING_RW("propName", 0, MAX_PROP_NAME, false, BL_ActionActuator, m_propname),
KX_PYATTRIBUTE_STRING_RW("framePropName", 0, MAX_PROP_NAME, false, BL_ActionActuator, m_framepropname),
KX_PYATTRIBUTE_RW_FUNCTION("useContinue", BL_ActionActuator, pyattr_get_use_continue, pyattr_set_use_continue),
KX_PYATTRIBUTE_FLOAT_RW_CHECK("blendTime", 0, MAXFRAMEF, BL_ActionActuator, m_blendframe, CheckBlendTime),
KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",0,100,false,BL_ActionActuator,m_playtype,CheckType),
{ NULL } //Sentinel
};
PyObject *BL_ActionActuator::pyattr_get_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
return PyUnicode_FromString(self->GetAction() ? self->GetAction()->id.name+2 : "");
}
int BL_ActionActuator::pyattr_set_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
if (!PyUnicode_Check(value))
{
PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, expected the string name of the action");
return PY_SET_ATTR_FAIL;
}
bAction *action= NULL;
STR_String val = _PyUnicode_AsString(value);
if (val != "")
{
action= (bAction*)SCA_ILogicBrick::m_sCurrentLogicManager->GetActionByName(val);
if (!action)
{
PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, action not found!");
return PY_SET_ATTR_FAIL;
}
}
self->SetAction(action);
return PY_SET_ATTR_SUCCESS;
}
PyObject *BL_ActionActuator::pyattr_get_channel_names(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
PyErr_SetString(PyExc_NotImplementedError, "BL_ActionActuator.channelNames no longer works, please use BL_ArmatureObject.channels instead");
return NULL;
#if 0 // XXX To be removed in a later version (first removed in 2.64)
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
PyObject *ret= PyList_New(0);
PyObject *item;
if (self->GetParent()->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE)
{
PyErr_SetString(PyExc_NotImplementedError, "actuator.channelNames: Only armatures support channels");
return NULL;
}
bPose *pose= ((BL_ArmatureObject*)self->GetParent())->GetOrigPose();
if (pose) {
bPoseChannel *pchan;
for (pchan= (bPoseChannel *)pose->chanbase.first; pchan; pchan= (bPoseChannel *)pchan->next) {
item= PyUnicode_FromString(pchan->name);
PyList_Append(ret, item);
Py_DECREF(item);
}
}
return ret;
#endif
}
PyObject *BL_ActionActuator::pyattr_get_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
return PyBool_FromLong(self->m_flag & ACT_FLAG_CONTINUE);
}
int BL_ActionActuator::pyattr_set_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
if (PyObject_IsTrue(value))
self->m_flag |= ACT_FLAG_CONTINUE;
else
self->m_flag &= ~ACT_FLAG_CONTINUE;
return PY_SET_ATTR_SUCCESS;
}
PyObject *BL_ActionActuator::pyattr_get_frame(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
return PyFloat_FromDouble(((KX_GameObject*)self->m_gameobj)->GetActionFrame(self->m_layer));
}
int BL_ActionActuator::pyattr_set_frame(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
BL_ActionActuator* self = static_cast<BL_ActionActuator*>(self_v);
((KX_GameObject*)self->m_gameobj)->SetActionFrame(self->m_layer, PyFloat_AsDouble(value));
return PY_SET_ATTR_SUCCESS;
}
#endif // WITH_PYTHON