X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;f=src%2F3dengfx%2Fsrc%2Fgfx%2Fanimation.cpp;fp=src%2F3dengfx%2Fsrc%2Fgfx%2Fanimation.cpp;h=20d3a4687b33d70e2ea829c289097994352291d5;hb=6e23259dbabaeb1711a2a5ca25b9cb421f693759;hp=0000000000000000000000000000000000000000;hpb=fe068fa879814784c45e0cb2e65dac489e8f5594;p=summerhack diff --git a/src/3dengfx/src/gfx/animation.cpp b/src/3dengfx/src/gfx/animation.cpp new file mode 100644 index 0000000..20d3a46 --- /dev/null +++ b/src/3dengfx/src/gfx/animation.cpp @@ -0,0 +1,480 @@ +/* +This file is part of the graphics core library. + +Copyright (c) 2004, 2005 John Tsiombikas + +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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* fundamental animation system (initally part of 3dgeom.cpp) + * + * Author: John Tsiombikas 2004 + * Modified: + * John Tsiombikas 2005 + */ + +#include "3dengfx_config.h" + +#include +#include "animation.hpp" + +using std::vector; + + +///////////////// PRS ///////////////////// + +PRS::PRS() { + scale = Vector3(1, 1, 1); +} + +PRS::PRS(const Vector3 &pos, const Quaternion &rot, const Vector3 &scale, const Vector3 &pivot) { + position = pos; + rotation = rot; + this->scale = scale; + this->pivot = pivot; +} + +Matrix4x4 PRS::get_xform_matrix() const { + Matrix4x4 trans_mat, rot_mat, scale_mat, pivot_mat, neg_pivot_mat; + + pivot_mat.set_translation(pivot); + neg_pivot_mat.set_translation(-pivot); + + trans_mat.set_translation(position); + rot_mat = (Matrix4x4)rotation.get_rotation_matrix(); + scale_mat.set_scaling(scale); + + return pivot_mat * trans_mat * rot_mat * scale_mat * neg_pivot_mat; +} + +PRS combine_prs(const PRS &prs1, const PRS &prs2) { + PRS prs; + + prs.position = prs1.position + prs2.position; + prs.rotation = prs2.rotation * prs1.rotation; + prs.scale.x = prs1.scale.x * prs2.scale.x; + prs.scale.y = prs1.scale.y * prs2.scale.y; + prs.scale.z = prs1.scale.z * prs2.scale.z; + prs.pivot = prs1.pivot; + + return prs; +} + +PRS inherit_prs(const PRS &child, const PRS &parent) { + PRS prs; + prs.pivot = child.pivot; + + prs.rotation = parent.rotation * child.rotation; + + prs.position += child.position; + prs.position -= parent.position; + prs.position.transform(parent.rotation.conjugate()); + prs.position += parent.position; + + Vector3 ppos_trans = parent.position.transformed(parent.rotation.conjugate()); + prs.position += ppos_trans; + + prs.position.x *= parent.scale.x; + prs.position.y *= parent.scale.y; + prs.position.z *= parent.scale.z; + + prs.scale.x = child.scale.x * parent.scale.x; + prs.scale.y = child.scale.y * parent.scale.y; + prs.scale.z = child.scale.z * parent.scale.z; + + return prs; +} + +std::ostream &operator <<(std::ostream &out, const PRS &prs) { + out << "p: " << prs.position << " r: " << prs.rotation << " s: " << prs.scale; + return out; +} + +//////////////// Keyframe ///////////////// + +Keyframe::Keyframe(const PRS &prs, unsigned long time) { + this->prs = prs; + this->time = time; +} + + +////////////// XFormNode /////////////// +XFormNode::XFormNode() { + key_count = 0; + use_ctrl = 0; + key_time_mode = TIME_CLAMP; + parent = 0; + cache.valid = false; +} + +XFormNode::~XFormNode() { +} + +Keyframe *XFormNode::get_nearest_key(int start, int end, unsigned long time) { + if(start == end) return &keys[start]; + if(end - start == 1) { + return abs((int)(time - keys[start].time)) < abs((int)(keys[end].time - time)) ? &keys[start] : &keys[end]; + } + + int mid = (start + end) / 2; + if(time < keys[mid].time) return get_nearest_key(start, mid, time); + if(time > keys[mid].time) return get_nearest_key(mid + 1, end, time); + return &keys[mid]; +} + +void XFormNode::get_key_interval(unsigned long time, const Keyframe **start, const Keyframe **end) const { + const Keyframe *nearest = get_nearest_key(time); + + *start = nearest; + *end = 0; + + if(time < nearest->time && nearest->time != keys[0].time) { + *start = nearest - 1; + *end = nearest; + } else if(time > nearest->time && nearest->time != keys[key_count - 1].time) { + *start = nearest; + *end = nearest + 1; + } +} + +void XFormNode::add_controller(MotionController ctrl, ControllerType ctrl_type) { + switch(ctrl_type) { + case CTRL_TRANSLATION: + trans_ctrl.push_back(ctrl); + break; + + case CTRL_ROTATION: + rot_ctrl.push_back(ctrl); + break; + + case CTRL_SCALING: + scale_ctrl.push_back(ctrl); + break; + } + use_ctrl = true; + cache.valid = false; +} + +vector *XFormNode::get_controllers(ControllerType ctrl_type) { + switch(ctrl_type) { + case CTRL_TRANSLATION: + return &trans_ctrl; + break; + + case CTRL_ROTATION: + return &rot_ctrl; + break; + + default: // just to make sure at least one of them is returned + case CTRL_SCALING: + return &scale_ctrl; + break; + } + cache.valid = false; +} + +void XFormNode::add_keyframe(const Keyframe &key) { + if(!keys.empty()) { + Keyframe *keyframe = get_nearest_key(key.time); + + if(keyframe->time == key.time) { + keyframe->prs = key.prs; + } else { + key_count++; + keys.push_back(key); + sort(keys.begin(), keys.end()); + } + } else { + keys.push_back(key); + key_count++; + } + cache.valid = false; +} + +Keyframe *XFormNode::get_keyframe(unsigned long time) { + cache.valid = false; + Keyframe *keyframe = get_nearest_key(time); + return (keyframe->time == time) ? keyframe : 0; +} + +void XFormNode::delete_keyframe(unsigned long time) { + vector::iterator iter = find(keys.begin(), keys.end(), Keyframe(PRS(), time)); + if(iter != keys.end()) { + keys.erase(iter); + } + cache.valid = false; +} + +std::vector *XFormNode::get_keyframes() { + cache.valid = false; + return &keys; +} + +void XFormNode::set_timeline_mode(TimelineMode time_mode) { + key_time_mode = time_mode; + cache.valid = false; +} + +void XFormNode::set_position(const Vector3 &pos, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.position = pos; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.position = pos; + } + } + cache.valid = false; +} + +void XFormNode::set_rotation(const Quaternion &rot, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.rotation = rot; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.rotation = rot; + } + } + cache.valid = false; +} + +void XFormNode::set_rotation(const Vector3 &euler, unsigned long time) { + + Quaternion xrot, yrot, zrot; + xrot.set_rotation(Vector3(1, 0, 0), euler.x); + yrot.set_rotation(Vector3(0, 1, 0), euler.y); + zrot.set_rotation(Vector3(0, 0, 1), euler.z); + + if(time == XFORM_LOCAL_PRS) { + local_prs.rotation = xrot * yrot * zrot; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.rotation = xrot * yrot * zrot; + } + } + cache.valid = false; +} + +void XFormNode::set_scaling(const Vector3 &scale, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.scale = scale; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.scale = scale; + } + } + cache.valid = false; +} + +void XFormNode::set_pivot(const Vector3 &pivot) { + local_prs.pivot = pivot; + cache.valid = false; +} + + +Vector3 XFormNode::get_position(unsigned long time) const { + return get_prs(time).position; +} + +Quaternion XFormNode::get_rotation(unsigned long time) const { + return get_prs(time).rotation; +} + +Vector3 XFormNode::get_scaling(unsigned long time) const { + return get_prs(time).scale; +} + +Vector3 XFormNode::get_pivot() const { + return local_prs.pivot; +} + + +void XFormNode::translate(const Vector3 &trans, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.position += trans; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.position += trans; + } + } + cache.valid = false; +} + +void XFormNode::rotate(const Quaternion &rot, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.rotation = rot * local_prs.rotation; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.rotation = rot * keyframe->prs.rotation; + } + } + cache.valid = false; +} + +void XFormNode::rotate(const Vector3 &euler, unsigned long time) { + + Quaternion xrot, yrot, zrot; + xrot.set_rotation(Vector3(1, 0, 0), euler.x); + yrot.set_rotation(Vector3(0, 1, 0), euler.y); + zrot.set_rotation(Vector3(0, 0, 1), euler.z); + + if(time == XFORM_LOCAL_PRS) { + local_prs.rotation = xrot * yrot * zrot * local_prs.rotation; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.rotation = xrot * yrot * zrot * keyframe->prs.rotation; + } + } + cache.valid = false; +} + +void XFormNode::rotate(const Matrix3x3 &rmat, unsigned long time) { + // hack a matrix to quat conversion (this should go into the math lib) + Quaternion q; + q.s = sqrt(rmat[0][0] + rmat[1][1] + rmat[2][2] + 1.0) / 2.0; + scalar_t ssq = q.s * q.s; + q.v.x = sqrt((rmat[0][0] + 1.0 - 2.0 * ssq) / 2.0); + q.v.y = sqrt((rmat[1][1] + 1.0 - 2.0 * ssq) / 2.0); + q.v.z = sqrt((rmat[2][2] + 1.0 - 2.0 * ssq) / 2.0); + + rotate(q, time); + cache.valid = false; +} + +void XFormNode::scale(const Vector3 &scale, unsigned long time) { + if(time == XFORM_LOCAL_PRS) { + local_prs.scale.x *= scale.x; + local_prs.scale.y *= scale.y; + local_prs.scale.z *= scale.z; + } else { + Keyframe *keyframe = get_nearest_key(time); + if(keyframe && keyframe->time == time) { + keyframe->prs.scale.x *= scale.x; + keyframe->prs.scale.y *= scale.y; + keyframe->prs.scale.z *= scale.z; + } + } + cache.valid = false; +} + + +void XFormNode::reset_position(unsigned long time) { + set_position(Vector3(0, 0, 0), time); + cache.valid = false; +} + +void XFormNode::reset_rotation(unsigned long time) { + set_rotation(Quaternion(), time); + cache.valid = false; +} + +void XFormNode::reset_scaling(unsigned long time) { + set_scaling(Vector3(1, 1, 1), time); + cache.valid = false; +} + +void XFormNode::reset_xform(unsigned long time) { + reset_position(time); + reset_rotation(time); + reset_scaling(time); +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +PRS XFormNode::get_prs(unsigned long time) const { + /*if(cache.valid && time == cache.time) { + return cache.prs; + } + */ + cache.valid = true; + cache.time = time; + + PRS parent_prs; + if(parent) { + parent_prs = parent->get_prs(time); + } + + if(time == XFORM_LOCAL_PRS) { + cache.prs = combine_prs(local_prs, parent_prs); + return cache.prs; + } + + PRS prs = local_prs; + + // apply keyframes + if(key_count) { + unsigned long ktime = get_timeline_time(time, keys[0].time, keys[key_count-1].time, key_time_mode); + + const Keyframe *start, *end; + get_key_interval(ktime, &start, &end); + + PRS key_prs; + + if(end) { + // find the parametric location of the given keyframe in the range we have + scalar_t t = (scalar_t)(ktime - start->time) / (scalar_t)(end->time - start->time); + + key_prs.position = start->prs.position + (end->prs.position - start->prs.position) * t; + key_prs.scale = start->prs.scale + (end->prs.scale - start->prs.scale) * t; + key_prs.rotation = slerp(start->prs.rotation, end->prs.rotation, t); + } else { + key_prs = start->prs; + } + + prs = combine_prs(prs, key_prs); + } + + // now let's also apply the controllers, if any + if(use_ctrl) { + PRS ctrl_prs; + + int count = trans_ctrl.size(); + for(int i=0; i