/* Copyright (C) 2007, 2010 - Bit-Blot This file is part of Aquaria. Aquaria 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. */ #include "Vector.h" #include "MathFunctions.h" #include "Base.h" #include /*************************************************************************/ void Vector::rotate2D360(float angle) { rotate2DRad(angle * (PI / 180.0f)); } void Vector::rotate2DRad(float rad) { float ox=x,oy=y; x = cosf(rad)*ox - sinf(rad)*oy; y = sinf(rad)*ox + cosf(rad)*oy; } Vector getRotatedVector(const Vector &vec, float rot) { #ifdef BBGE_BUILD_OPENGL glPushMatrix(); glLoadIdentity(); glRotatef(rot, 0, 0, 1); if (vec.x != 0 || vec.y != 0) { //glRotatef(this->rotation.z, 0,0,1,this->rotation.z); glTranslatef(vec.x, vec.y, 0); } float m[16]; glGetFloatv(GL_MODELVIEW_MATRIX, m); float x = m[12]; float y = m[13]; float z = m[14]; glPopMatrix(); return Vector(x,y,z); #elif defined(BBGE_BUILD_DIRECTX) return vec; #endif } // note update this from float lerp Vector lerp(const Vector &v1, const Vector &v2, float dt, int lerpType) { switch(lerpType) { case LERP_EASE: { // ease in and out return v1*(2*(dt*dt*dt)-3*sqr(dt)+1) + v2*(3*sqr(dt) - 2*(dt*dt*dt)); } case LERP_EASEIN: { float lerpAvg = 1.0f-dt; return (v2-v1)*(sinf(dt*PI_HALF)*(1.0f-lerpAvg)+dt*lerpAvg)+v1; } case LERP_EASEOUT: { return (v2-v1)*-sinf(-dt*PI_HALF)+v1; } } return (v2-v1)*dt+v1; } /*************************************************************************/ float Bias( float x, float biasAmt ) { // WARNING: not thread safe static float lastAmt = -1; static float lastExponent = 0; if( lastAmt != biasAmt ) { lastExponent = logf( biasAmt ) * -1.4427f; // (-1.4427 = 1 / log(0.5)) } return powf( x, lastExponent ); } float Gain( float x, float biasAmt ) { // WARNING: not thread safe if( x < 0.5f ) return 0.5f * Bias(2*x, 1-biasAmt); else return 1 - 0.5f * Bias(2 - 2*x, 1-biasAmt); } float SmoothCurve( float x ) { return (1 - cosf( x * PI )) * 0.5f; } inline float MovePeak( float x, float flPeakPos ) { // Todo: make this higher-order? if( x < flPeakPos ) return x * 0.5f / flPeakPos; else return 0.5f + 0.5f * (x - flPeakPos) / (1 - flPeakPos); } float SmoothCurve_Tweak( float x, float flPeakPos, float flPeakSharpness ) { float flMovedPeak = MovePeak( x, flPeakPos ); float flSharpened = Gain( flMovedPeak, flPeakSharpness ); return SmoothCurve( flSharpened ); } float SimpleSpline( float value ) { float valueSquared = value * value; // Nice little ease-in, ease-out spline-like curve return (3 * valueSquared - 2 * valueSquared * value); } void VectorPath::addPathNode(Vector v, float p) { VectorPathNode node; node.value = v; node.percent = p; pathNodes.push_back(node); } void VectorPath::flip() { std::vector copyNodes; copyNodes = pathNodes; pathNodes.clear(); for (int i = copyNodes.size()-1; i >=0; i--) { copyNodes[i].percent = 1 - copyNodes[i].percent; pathNodes.push_back(copyNodes[i]); } } void VectorPath::realPercentageCalc() { float totalLen = getLength(); float len = 0; for (int i = 1; i < pathNodes.size(); i++) { Vector diff = pathNodes[i].value - pathNodes[i-1].value; len += diff.getLength2D(); pathNodes[i].percent = len/totalLen; } } float VectorPath::getSubSectionLength(int startIncl, int endIncl) { float len = 0; for (int i = startIncl+1; i <= endIncl; i++) { Vector diff = pathNodes[i].value - pathNodes[i-1].value; len += diff.getLength2D(); } return len; } float VectorPath::getLength() { float len = 0; for (int i = 1; i < pathNodes.size(); i++) { Vector diff = pathNodes[i].value - pathNodes[i-1].value; len += diff.getLength2D(); } return len; } void VectorPath::clear() { pathNodes.clear(); } void VectorPath::splice(const VectorPath &path, int sz) { std::vector copy = pathNodes; pathNodes.clear(); int i = 0; for (i = 0; i < path.pathNodes.size(); i++) pathNodes.push_back(path.pathNodes[i]); for (i = sz+1; i < copy.size(); i++) pathNodes.push_back(copy[i]); for (i = 0; i < pathNodes.size(); i++) { pathNodes[i].percent = i/float(pathNodes.size()); } } void VectorPath::removeNodes(int startInclusive, int endInclusive) { std::vector copy = pathNodes; pathNodes.clear(); for (int i = 0; i < copy.size(); i++) { if (i < startInclusive || i > endInclusive) { pathNodes.push_back(copy[i]); } } } void VectorPath::prepend(const VectorPath &path) { std::vector copy = pathNodes; pathNodes.clear(); int i = 0; for (i = 0; i < path.pathNodes.size(); i++) pathNodes.push_back(path.pathNodes[i]); for (i = 0; i < copy.size(); i++) pathNodes.push_back(copy[i]); } void VectorPath::calculatePercentages() { for (int i = 0; i < pathNodes.size(); i++) { pathNodes[i].percent = i/float(pathNodes.size()); } } void VectorPath::append(const VectorPath &path) { std::vector copy = pathNodes; pathNodes.clear(); int i = 0; for (i = 0; i < copy.size(); i++) pathNodes.push_back(copy[i]); for (i = 0; i < path.pathNodes.size(); i++) pathNodes.push_back(path.pathNodes[i]); } void VectorPath::cut(int n) { std::vector copy = pathNodes; pathNodes.clear(); for (int i = 0; i < copy.size(); i+=n) { pathNodes.push_back(copy[i]); } } void VectorPath::removeNode(int t) { std::vector copy = pathNodes; pathNodes.clear(); for (int i = 0; i < copy.size(); i++) { if (i != t) pathNodes.push_back(copy[i]); } } Vector VectorPath::getValue(float usePercent) { if (pathNodes.empty()) { debugLog("Vector path nodes empty"); return Vector(0,0,0); } VectorPathNode *target = 0; VectorPathNode *from = &pathNodes[0]; for (int i = 0; i < pathNodes.size(); ++i) { if (pathNodes[i].percent >= usePercent) { target = &pathNodes[i]; break; } from = &pathNodes[i]; } if (!from && !target) { errorLog("returning first value"); return pathNodes[0].value; } else if (!from && target) { errorLog("Unexpected Path node result (UPDATE: Could use current value as from?)"); } else if (from && !target) { // Should only happen at end // msg ("returning just a value"); return from->value; } else if (from && target && from==target) { return from->value; } else if (from && target) { //bool smoothing = false; Vector v; float perc=0; perc = ((usePercent - from->percent)/(target->percent-from->percent)); //perc = Gain(perc, 0.8); Vector targetValue = target->value; Vector fromValue = from->value; /* int nexti = i + 1; int previ = i - 1; if (perc > 0.5f && nexti < pathNodes.size()) { float scale = ((perc-0.5f)/0.5f) * 0.1f; targetValue = targetValue * (1.0f-scale) + pathNodes[nexti].value * scale; } else if (perc < 0.5f && previ > 0) { float scale = (1.0f-(perc/0.5f)) * 0.1f; targetValue = targetValue * (1.0f-scale) + pathNodes[previ].value * scale; } */ v = (targetValue - fromValue) * (perc); v += fromValue; return v; /* int nexti = i + 1; int previ = i - 1; if (smoothing && perc >= 0.5f && nexti < pathNodes.size() && nexti >= 0) { VectorPathNode *next = &pathNodes[nexti]; float nextPerc = perc - 0.5f; v = (target->value - from->value) * (perc-nextPerc); Vector v2 = (next->value - from->value) * nextPerc; v = v+v2; v += from->value; } else if (smoothing && perc <= 0.5f && previ < pathNodes.size() && previ >= 0) { VectorPathNode *prev = &pathNodes[previ]; float prevPerc = perc + 0.5f; v = (target->value - from->value) * (perc-prevPerc); Vector v2 = (from->value - prev->value) * prevPerc; //v = (v + v2)/2.0f; v = v+v2; v += from->value; } else { v = (target->value - from->value) * (perc); v += from->value; } */ /* int nexti = i + 1; Vector perp; if (smoothing && nexti < pathNodes.size() && nexti >= 0) { VectorPathNode *next = &pathNodes[nexti]; Vector perp = (next->value - from->value); perp = perp.getPerpendicularLeft(); Vector p = getNearestPointOnLine(from->value, next->value, target->value); float dist = (target->value - p).getLength2D(); if (dist > 0) { float bulge = sinf(perc * PI); perp |= dist; perp *= bulge; } } */ } return Vector(0,0,0); } /*************************************************************************/ float InterpolatedVector::interpolateTo(Vector vec, float timePeriod, int loopType, bool pingPong, bool ease, InterpolateToFlag flag) { if (timePeriod == 0) { this->x = vec.x; this->y = vec.y; this->z = vec.z; return 0; } InterpolatedVectorData *data = ensureData(); data->ease = ease; data->timePassed = 0; //data->fakeTimePassed = 0; if (timePeriod < 0) { timePeriod = -timePeriod; timePeriod = (vec-Vector(x,y,z)).getLength3D() / timePeriod; /* std::ostringstream os; os << "calced: " << timePeriod; debugLog(os.str()); */ } data->timePeriod = timePeriod; data->from = Vector (this->x, this->y, this->z); data->target = vec; data->loopType = loopType; data->pingPong = pingPong; data->interpolating = true; return data->timePeriod; } void InterpolatedVector::stop() { if (data) data->interpolating = false; } void InterpolatedVector::startPath(float time, float ease) { InterpolatedVectorData *data = ensureData(); if (data->path.getNumPathNodes()==0) return; data->pathTimer = 0; data->pathTime = time; data->followingPath = true; data->loopType = 0; data->pingPong = false; // get the right values to start off with updatePath(0); } void InterpolatedVector::stopPath() { if (data) data->followingPath = false; } void InterpolatedVector::resumePath() { InterpolatedVectorData *data = ensureData(); data->followingPath = true; } void InterpolatedVector::updatePath(float dt) { InterpolatedVectorData *data = ensureData(); if (data->pathTimer > data->pathTime) { Vector value = data->path.getPathNode(data->path.getNumPathNodes()-1)->value; this->x = value.x; this->y = value.y; this->z = value.z; if (data->loopType != 0) { if (data->loopType > 0) data->loopType -= 1; int oldLoopType = data->loopType; if (data->pingPong) { // flip path data->path.flip(); startPath(data->pathTime); data->loopType = oldLoopType; } else { startPath(data->pathTime); data->loopType = oldLoopType; } } else { stopPath(); } } else { data->pathTimer += dt * data->pathTimeMultiplier; float perc = data->pathTimer/data->pathTime; Vector value = data->path.getValue(perc); this->x = value.x; this->y = value.y; this->z = value.z; } } float InterpolatedVector::getPercentDone() { InterpolatedVectorData *data = ensureData(); return data->timePassed/data->timePeriod; } void InterpolatedVector::doInterpolate(float dt) { InterpolatedVectorData *data = ensureData(); //errorLog ("gothere"); /* // old method if (data->ease) { float diff = data->timePassed / data->timePeriod; if (diff > 0.5f) diff = 1.0f - diff; diff /= 0.5f; diff *= 2; //diff += 0.5f; data->fakeTimePassed += dt*diff; } */ data->timePassed += dt; if (data->timePassed >= data->timePeriod) { this->x = data->target.x; this->y = data->target.y; this->z = data->target.z; data->interpolating = false; if (data->loopType != 0) { if (data->loopType > 0) data->loopType -= 1; if (data->pingPong) { interpolateTo (data->from, data->timePeriod, data->loopType, data->pingPong, data->ease, IS_LOOPING); } else { this->x = data->from.x; this->y = data->from.y; this->z = data->from.z; interpolateTo (data->target, data->timePeriod, data->loopType, data->pingPong, data->ease, IS_LOOPING); } } } else { Vector v; /* // old method if (data->ease) { v = lerp(data->from, data->target, (data->timePassed / data->timePeriod), data->ease); //v = (data->target - data->from) * //v = (data->target - data->from) * (data->fakeTimePassed / data->timePeriod); } else { float perc = data->timePassed / data->timePeriod; v = (data->target - data->from) * perc; } v += data->from; */ v = lerp(data->from, data->target, (data->timePassed / data->timePeriod), data->ease ? LERP_EASE : LERP_LINEAR); this->x = v.x; this->y = v.y; this->z = v.z; //*updatee += data->from; } }