BGE: TrackTo actuator: increasing up & track axis options

This is related to Task T34861 to increase up & track axis options for TrackTo actuator. I've just added it to differential to facilitate an easier review.

With the patch applied you can select X, Y and Z axis for the Up axis, and X, Y, Z, -X, -Y and -Z for the track axis.

Related to the implementation I have used the algorithm from Trackto constrain placed in constrain.c but adapted to be used with MOTO library.

The wiki docs are here (http://wiki.blender.org/index.php/User:Lordloki/Doc:2.6/Manual/Game_Engine/Logic/Actuators/Edit_Object#Trackto_Actuator).

Test file is here: {F97623}

I have also uploaded 2 screenshots showing the UI modifications to the TrackTo actuator:

{F91992} {F91990}

Reviewers: moguri, dfelinto

Reviewed By: moguri

CC: Genome36

Differential Revision: https://developer.blender.org/D565
This commit is contained in:
Jorge Bernal 2014-07-14 17:36:56 -07:00 committed by Mitchell Stokes
parent 50d30148b6
commit 1bf87fa26c
16 changed files with 249 additions and 149 deletions

View File

@ -868,6 +868,23 @@ See :class:`bge.types.KX_SteeringActuator.behavior`
:value: 3
.. _logic-trackto-actuator:
-----------------
TrackTo Actuator
-----------------
See :class:`bge.types.KX_TrackToActuator`
.. data:: KX_TRACK_UPAXIS_POS_X
.. data:: KX_TRACK_UPAXIS_POS_Y
.. data:: KX_TRACK_UPAXIS_POS_Z
.. data:: KX_TRACK_TRAXIS_POS_X
.. data:: KX_TRACK_TRAXIS_POS_Y
.. data:: KX_TRACK_TRAXIS_POS_Z
.. data:: KX_TRACK_TRAXIS_NEG_X
.. data:: KX_TRACK_TRAXIS_NEG_Y
.. data:: KX_TRACK_TRAXIS_NEG_Z
=======
Various

View File

@ -37,3 +37,25 @@ base class --- :class:`SCA_IActuator`
:type: boolean
.. attribute:: upAxis
The axis that points upward.
:type: integer from 0 to 2
* KX_TRACK_UPAXIS_POS_X
* KX_TRACK_UPAXIS_POS_Y
* KX_TRACK_UPAXIS_POS_Z
.. attribute:: trackAxis
The axis that points to the target object.
:type: integer from 0 to 5
* KX_TRACK_TRAXIS_POS_X
* KX_TRACK_TRAXIS_POS_Y
* KX_TRACK_TRAXIS_POS_Z
* KX_TRACK_TRAXIS_NEG_X
* KX_TRACK_TRAXIS_NEG_Y
* KX_TRACK_TRAXIS_NEG_Z

View File

@ -84,11 +84,18 @@ public:
MT_Vector3 getColumn(int i) const {
return MT_Vector3(m_el[0][i], m_el[1][i], m_el[2][i]);
}
void setColumn(int i, const MT_Vector3& v) {
m_el[0][i] = v[0];
m_el[1][i] = v[1];
m_el[2][i] = v[2];
}
void setRow(int i, const MT_Vector3& v) {
m_el[i][0] = v[0];
m_el[i][1] = v[1];
m_el[i][2] = v[2];
}
void setValue(const float *m) {
m_el[0][0] = *m++; m_el[1][0] = *m++; m_el[2][0] = *m++; m++;

View File

@ -75,7 +75,7 @@ public:
void normalize();
MT_Vector3 normalized() const;
MT_Vector3 safe_normalized() const;
MT_Vector3 safe_normalized_vec(MT_Vector3 vecnormalized) const;
void scale(MT_Scalar x, MT_Scalar y, MT_Scalar z);
MT_Vector3 scaled(MT_Scalar x, MT_Scalar y, MT_Scalar z) const;

View File

@ -77,6 +77,13 @@ GEN_INLINE MT_Vector3 MT_Vector3::safe_normalized() const {
*this / len;
}
GEN_INLINE MT_Vector3 MT_Vector3::safe_normalized_vec(MT_Vector3 vecnormalized) const {
MT_Scalar len = length();
return MT_fuzzyZero(len) ?
vecnormalized :
*this / len;
}
GEN_INLINE void MT_Vector3::scale(MT_Scalar xx, MT_Scalar yy, MT_Scalar zz) {
m_co[0] *= xx; m_co[1] *= yy; m_co[2] *= zz;
}

View File

@ -301,10 +301,11 @@ class OBJECT_PT_relations_extras(ObjectButtonsPanel, Panel):
split = layout.split()
col = split.column()
col.label(text="Tracking Axes:")
col.prop(ob, "track_axis", text="Axis")
col.prop(ob, "up_axis", text="Up Axis")
if context.scene.render.engine != 'BLENDER_GAME':
col = split.column()
col.label(text="Tracking Axes:")
col.prop(ob, "track_axis", text="Axis")
col.prop(ob, "up_axis", text="Up Axis")
col = split.column()
col.prop(ob, "use_slow_parent")

View File

@ -42,7 +42,7 @@ extern "C" {
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 271
#define BLENDER_SUBVERSION 1
#define BLENDER_SUBVERSION 2
/* 262 was the last editmesh release but it has compatibility code for bmesh data */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 5

View File

@ -392,6 +392,7 @@ void init_actuator(bActuator *act)
bSteeringActuator *sta;
bArmatureActuator *arma;
bMouseActuator *ma;
bEditObjectActuator *eoa;
if (act->data) MEM_freeN(act->data);
act->data= NULL;
@ -430,6 +431,9 @@ void init_actuator(bActuator *act)
break;
case ACT_EDIT_OBJECT:
act->data= MEM_callocN(sizeof(bEditObjectActuator), "editobact");
eoa = act->data;
eoa->upflag= ACT_TRACK_UP_Z;
eoa->trackflag= ACT_TRACK_TRAXIS_Y;
break;
case ACT_CONSTRAINT:
act->data= MEM_callocN(sizeof(bConstraintActuator), "cons act");

View File

@ -42,6 +42,7 @@
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_actuator_types.h"
#include "DNA_genfile.h"
@ -310,6 +311,29 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
if (!MAIN_VERSION_ATLEAST(main, 271, 2)) {
/* init up & track axis property of trackto actuators */
Object *ob;
for (ob = main->object.first; ob; ob = ob->id.next) {
bActuator *act;
for (act = ob->actuators.first; act; act = act->next) {
if (act->type == ACT_EDIT_OBJECT) {
bEditObjectActuator *eoact = act->data;
eoact->trackflag = ob->trackflag;
/* if trackflag is pointing +-Z axis then upflag should point Y axis.
* Rest of trackflag cases, upflag should be point z axis */
if ((ob->trackflag == OB_POSZ) || (ob->trackflag == OB_NEGZ)) {
eoact->upflag = 1;
}
else {
eoact->upflag = 2;
}
}
}
}
}
if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) {
Scene *scene;
for (scene = main->scene.first; scene; scene = scene->id.next) {

View File

@ -1720,6 +1720,12 @@ static void draw_actuator_edit_object(uiLayout *layout, PointerRNA *ptr)
sub = uiLayoutSplit(split, 0.7f, false);
uiItemR(sub, ptr, "time", 0, NULL, ICON_NONE);
uiItemR(sub, ptr, "use_3d_tracking", UI_ITEM_R_TOGGLE, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, ptr, "up_axis", 0, NULL, ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, ptr, "track_axis", 0, NULL, ICON_NONE);
break;
case ACT_EDOB_DYNAMICS:
if (ob->type != OB_MESH) {

View File

@ -97,6 +97,8 @@ typedef struct bEditObjectActuator {
float mass;
short localflag; /* flag for the lin & ang. vel: apply locally */
short dyn_operation;
short upflag, trackflag; /* flag for up axis and track axis */
int pad;
} bEditObjectActuator;
typedef struct bSceneActuator {
@ -439,6 +441,19 @@ typedef struct bActuator {
/* editObjectActuator->flag */
#define ACT_TRACK_3D 1
/* editObjectActuator->upflag */
#define ACT_TRACK_UP_X 0
#define ACT_TRACK_UP_Y 1
#define ACT_TRACK_UP_Z 2
/* editObjectActuator->trackflag */
#define ACT_TRACK_TRAXIS_X 0
#define ACT_TRACK_TRAXIS_Y 1
#define ACT_TRACK_TRAXIS_Z 2
#define ACT_TRACK_TRAXIS_NEGX 3
#define ACT_TRACK_TRAXIS_NEGY 4
#define ACT_TRACK_TRAXIS_NEGZ 5
/* editObjectActuator->flag for replace mesh actuator */
#define ACT_EDOB_REPLACE_MESH_NOGFX 2 /* use for replace mesh actuator */
#define ACT_EDOB_REPLACE_MESH_PHYS 4

View File

@ -1369,6 +1369,23 @@ static void rna_def_edit_object_actuator(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem prop_track_axis_items[] = {
{ACT_TRACK_TRAXIS_X, "TRACKAXISX", 0, "X axis", ""},
{ACT_TRACK_TRAXIS_Y, "TRACKAXISY", 0, "Y axis", ""},
{ACT_TRACK_TRAXIS_Z, "TRACKAXISZ", 0, "Z axis", ""},
{ACT_TRACK_TRAXIS_NEGX, "TRACKAXISNEGX", 0, "-X axis", ""},
{ACT_TRACK_TRAXIS_NEGY, "TRACKAXISNEGY", 0, "-Y axis", ""},
{ACT_TRACK_TRAXIS_NEGZ, "TRACKAXISNEGZ", 0, "-Z axis", ""},
{0, NULL, 0, NULL, NULL}
};
static EnumPropertyItem prop_up_axis_items[] = {
{ACT_TRACK_UP_X, "UPAXISX", 0, "X axis", ""},
{ACT_TRACK_UP_Y, "UPAXISY", 0, "Y axis", ""},
{ACT_TRACK_UP_Z, "UPAXISZ", 0, "Z axis", ""},
{0, NULL, 0, NULL, NULL}
};
srna = RNA_def_struct(brna, "EditObjectActuator", "Actuator");
RNA_def_struct_ui_text(srna, "Edit Object Actuator", "Actuator used to edit objects");
RNA_def_struct_sdna_from(srna, "bEditObjectActuator", "data");
@ -1385,6 +1402,18 @@ static void rna_def_edit_object_actuator(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Dynamic Operation", "");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "upflag");
RNA_def_property_enum_items(prop, prop_up_axis_items);
RNA_def_property_ui_text(prop, "Up Axis", "The axis that points upward");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop = RNA_def_property(srna, "track_axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "trackflag");
RNA_def_property_enum_items(prop, prop_track_axis_items);
RNA_def_property_ui_text(prop, "Track Axis", "The axis that points to the target object");
RNA_def_property_update(prop, NC_LOGIC, NULL);
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_pointer_sdna(prop, NULL, "ob");

View File

@ -538,8 +538,8 @@ void BL_ConvertActuators(const char* maggiename,
originalval,
editobact->time,
editobact->flag,
blenderobject->trackflag,
blenderobject->upflag);
editobact->trackflag,
editobact->upflag);
baseact = tmptrackact;
break;
}

View File

@ -88,6 +88,7 @@ extern "C" {
#include "KX_SteeringActuator.h"
#include "KX_NavMeshObject.h"
#include "KX_MouseActuator.h"
#include "KX_TrackToActuator.h"
#include "SCA_IInputDevice.h"
#include "SCA_PropertySensor.h"
@ -1754,6 +1755,17 @@ PyObject *initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack
KX_MACRO_addTypesToDict(d, KX_RAY_AXIS_NEG_Y, KX_RaySensor::KX_RAY_AXIS_NEG_Y);
KX_MACRO_addTypesToDict(d, KX_RAY_AXIS_NEG_Z, KX_RaySensor::KX_RAY_AXIS_NEG_Z);
/* TrackTo Actuator */
KX_MACRO_addTypesToDict(d, KX_TRACK_UPAXIS_POS_X, KX_TrackToActuator::KX_TRACK_UPAXIS_POS_X);
KX_MACRO_addTypesToDict(d, KX_TRACK_UPAXIS_POS_Y, KX_TrackToActuator::KX_TRACK_UPAXIS_POS_Y);
KX_MACRO_addTypesToDict(d, KX_TRACK_UPAXIS_POS_Z, KX_TrackToActuator::KX_TRACK_UPAXIS_POS_Z);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_POS_X, KX_TrackToActuator::KX_TRACK_TRAXIS_POS_X);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_POS_Y, KX_TrackToActuator::KX_TRACK_TRAXIS_POS_Y);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_POS_Z, KX_TrackToActuator::KX_TRACK_TRAXIS_POS_Z);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_NEG_X, KX_TrackToActuator::KX_TRACK_TRAXIS_NEG_X);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_NEG_Y, KX_TrackToActuator::KX_TRACK_TRAXIS_NEG_Y);
KX_MACRO_addTypesToDict(d, KX_TRACK_TRAXIS_NEG_Z, KX_TrackToActuator::KX_TRACK_TRAXIS_NEG_Z);
/* Dynamic actuator */
KX_MACRO_addTypesToDict(d, KX_DYN_RESTORE_DYNAMICS, KX_SCA_DynamicActuator::KX_DYN_RESTORE_DYNAMICS);
KX_MACRO_addTypesToDict(d, KX_DYN_DISABLE_DYNAMICS, KX_SCA_DynamicActuator::KX_DYN_DISABLE_DYNAMICS);

View File

@ -31,8 +31,7 @@
* Replace the mesh for this actuator's parent
*/
/* todo: not all trackflags / upflags are implemented/tested !
* m_trackflag is used to determine the forward tracking direction
/* m_trackflag is used to determine the forward tracking direction
* m_upflag for the up direction
* normal situation is +y for forward, +z for up */
@ -177,7 +176,77 @@ static MT_Matrix3x3 matrix3x3_interpol(MT_Matrix3x3 oldmat, MT_Matrix3x3 mat, in
return EulToMat3(eul);
}
static float basis_cross(int n, int m)
{
switch (n - m) {
case 1:
case -2:
return 1.0f;
case -1:
case 2:
return -1.0f;
default:
return 0.0f;
}
}
/* vectomat function obtained from constrain.c and modified to work with MOTO library */
static MT_Matrix3x3 vectomat(MT_Vector3 vec, short axis, short upflag, short threedimup)
{
MT_Matrix3x3 mat;
MT_Vector3 y(MT_Scalar(0.0), MT_Scalar(1.0), MT_Scalar(0.0));
MT_Vector3 z(MT_Scalar(0.0), MT_Scalar(0.0), MT_Scalar(1.0)); /* world Z axis is the global up axis */
MT_Vector3 proj;
MT_Vector3 right;
MT_Scalar mul;
int right_index;
/* Normalized Vec vector*/
vec = vec.safe_normalized_vec(z);
/* if 2D doesn't move the up vector */
if (!threedimup){
vec.setValue(MT_Scalar(vec[0]), MT_Scalar(vec[1]), MT_Scalar(0.0));
vec = (vec - z.dot(vec)*z).safe_normalized_vec(z);
}
if (axis > 2)
axis -= 3;
else
vec = -vec;
/* project the up vector onto the plane specified by vec */
/* first z onto vec... */
mul = z.dot(vec) / vec.dot(vec);
proj = vec * mul;
/* then onto the plane */
proj = z - proj;
/* proj specifies the transformation of the up axis */
proj = proj.safe_normalized_vec(y);
/* Normalized cross product of vec and proj specifies transformation of the right axis */
right = proj.cross(vec);
right.normalize();
if (axis != upflag) {
right_index = 3 - axis - upflag;
/* account for up direction, track direction */
right = right * basis_cross(axis, upflag);
mat.setRow(right_index, right);
mat.setRow(upflag, proj);
mat.setRow(axis, vec);
mat = mat.inverse();
}
/* identity matrix - don't do anything if the two axes are the same */
else {
mat.setIdentity();
}
return mat;
}
KX_TrackToActuator::~KX_TrackToActuator()
{
@ -247,153 +316,24 @@ bool KX_TrackToActuator::Update(double curtime, bool frame)
else if (m_object)
{
KX_GameObject* curobj = (KX_GameObject*) GetParent();
MT_Vector3 dir = ((KX_GameObject*)m_object)->NodeGetWorldPosition() - curobj->NodeGetWorldPosition();
if (dir.length2())
dir.normalize();
MT_Vector3 up(0,0,1);
#ifdef DSADSA
switch (m_upflag)
{
case 0:
{
up.setValue(1.0,0,0);
break;
}
case 1:
{
up.setValue(0,1.0,0);
break;
}
case 2:
default:
{
up.setValue(0,0,1.0);
}
}
#endif
if (m_allow3D)
{
up = (up - up.dot(dir) * dir).safe_normalized();
}
else
{
dir = (dir - up.dot(dir)*up).safe_normalized();
}
MT_Vector3 left;
MT_Vector3 dir = curobj->NodeGetWorldPosition() - ((KX_GameObject*)m_object)->NodeGetWorldPosition();
MT_Matrix3x3 mat;
switch (m_trackflag)
{
case 0: // TRACK X
{
// (1.0 , 0.0 , 0.0 ) x direction is forward, z (0.0 , 0.0 , 1.0 ) up
left = dir.safe_normalized();
dir = up.cross(left).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
break;
};
case 1: // TRACK Y
{
// (0.0 , 1.0 , 0.0 ) y direction is forward, z (0.0 , 0.0 , 1.0 ) up
left = (dir.cross(up)).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
break;
}
case 2: // track Z
{
left = up.safe_normalized();
up = dir.safe_normalized();
dir = left;
left = (dir.cross(up)).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
break;
}
case 3: // TRACK -X
{
// (1.0 , 0.0 , 0.0 ) x direction is forward, z (0.0 , 0.0 , 1.0 ) up
left = -dir.safe_normalized();
dir = up.cross(left).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
break;
};
case 4: // TRACK -Y
{
// (0.0 , -1.0 , 0.0 ) -y direction is forward, z (0.0 , 0.0 , 1.0 ) up
left = (-dir.cross(up)).safe_normalized();
mat.setValue (
left[0], -dir[0],up[0],
left[1], -dir[1],up[1],
left[2], -dir[2],up[2]
);
break;
}
case 5: // track -Z
{
left = up.safe_normalized();
up = -dir.safe_normalized();
dir = left;
left = (dir.cross(up)).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
break;
}
default:
{
// (1.0 , 0.0 , 0.0 ) -x direction is forward, z (0.0 , 0.0 , 1.0 ) up
left = -dir.safe_normalized();
dir = up.cross(left).safe_normalized();
mat.setValue (
left[0], dir[0],up[0],
left[1], dir[1],up[1],
left[2], dir[2],up[2]
);
}
}
MT_Matrix3x3 oldmat;
oldmat= curobj->NodeGetWorldOrientation();
mat = vectomat(dir, m_trackflag, m_upflag, m_allow3D);
oldmat = curobj->NodeGetWorldOrientation();
/* erwin should rewrite this! */
mat= matrix3x3_interpol(oldmat, mat, m_time);
mat = matrix3x3_interpol(oldmat, mat, m_time);
if (m_parentobj) { // check if the model is parented and calculate the child transform
/* check if the model is parented and calculate the child transform */
if (m_parentobj) {
MT_Point3 localpos;
localpos = curobj->GetSGNode()->GetLocalPosition();
// Get the inverse of the parent matrix
MT_Matrix3x3 parentmatinv;
parentmatinv = m_parentobj->NodeGetWorldOrientation ().inverse ();
parentmatinv = m_parentobj->NodeGetWorldOrientation().inverse();
// transform the local coordinate system into the parents system
mat = parentmatinv * mat;
// append the initial parent local rotation matrix
@ -404,8 +344,7 @@ bool KX_TrackToActuator::Update(double curtime, bool frame)
curobj->NodeSetLocalPosition(localpos);
//curobj->UpdateTransform();
}
else
{
else {
curobj->NodeSetLocalOrientation(mat);
}
@ -451,6 +390,8 @@ PyMethodDef KX_TrackToActuator::Methods[] = {
PyAttributeDef KX_TrackToActuator::Attributes[] = {
KX_PYATTRIBUTE_INT_RW("time",0,1000,true,KX_TrackToActuator,m_time),
KX_PYATTRIBUTE_BOOL_RW("use3D",KX_TrackToActuator,m_allow3D),
KX_PYATTRIBUTE_INT_RW("upAxis", 0, 2, true, KX_TrackToActuator,m_upflag),
KX_PYATTRIBUTE_INT_RW("trackAxis", 0, 5, true, KX_TrackToActuator,m_trackflag),
KX_PYATTRIBUTE_RW_FUNCTION("object", KX_TrackToActuator, pyattr_get_object, pyattr_set_object),
{ NULL } //Sentinel

View File

@ -68,6 +68,21 @@ class KX_TrackToActuator : public SCA_IActuator
virtual void Relink(CTR_Map<CTR_HashedPtr, void*> *obj_map);
virtual bool Update(double curtime, bool frame);
//Python Interface
enum UpAxis {
KX_TRACK_UPAXIS_POS_X = 0,
KX_TRACK_UPAXIS_POS_Y,
KX_TRACK_UPAXIS_POS_Z
};
enum TrackAxis {
KX_TRACK_TRAXIS_POS_X = 0,
KX_TRACK_TRAXIS_POS_Y,
KX_TRACK_TRAXIS_POS_Z,
KX_TRACK_TRAXIS_NEG_X,
KX_TRACK_TRAXIS_NEG_Y,
KX_TRACK_TRAXIS_NEG_Z
};
#ifdef WITH_PYTHON
/* Python part */