PageRenderTime 46ms CodeModel.GetById 12ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/screensavers/rsxs-0.9/src/hyperspace/spline.cc

http://github.com/xbmc/xbmc
C++ | 155 lines | 95 code | 22 blank | 38 comment | 4 complexity | e4d143141fa89ae05061152a114f0e5b MD5 | raw file
  1/*
  2 * Really Slick XScreenSavers
  3 * Copyright (C) 2002-2006  Michael Chapman
  4 *
  5 * This program is free software; you can redistribute it and/or modify
  6 * it under the terms of the GNU General Public License version 2 as
  7 * published by the Free Software Foundation.
  8 *
  9 * This program is distributed in the hope that it will be useful,
 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 * GNU General Public License for more details.
 13 *
 14 * You should have received a copy of the GNU General Public License
 15 * along with this program; if not, write to the Free Software
 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 17 *
 18 *****************************************************************************
 19 *
 20 * This is a Linux port of the Really Slick Screensavers,
 21 * Copyright (C) 2005 Terence M. Welsh, available from www.reallyslick.com
 22 */
 23#include <common.hh>
 24
 25#include <spline.hh>
 26#include <vector.hh>
 27
 28namespace Spline {
 29	unsigned int points;
 30	float step;
 31
 32	std::vector<float> _phase;
 33	std::vector<float> _rate;
 34	std::vector<Vector> _moveXYZ;
 35	std::vector<Vector> _baseXYZ;
 36	std::vector<Vector> _XYZ;
 37	std::vector<Vector> _baseDir;
 38	std::vector<Vector> _dir;
 39
 40	Vector interpolate(const Vector&, const Vector&, const Vector&, const Vector&, float);
 41};
 42
 43void Spline::init(unsigned int length) {
 44	// 6 is the minimum number of points necessary for a tunnel to have one segment
 45	points = (length < 6 ? 6 : length);
 46	step = 0.0f;
 47
 48	stdx::construct_n(_phase, points);
 49	stdx::construct_n(_rate, points);
 50	stdx::construct_n(_moveXYZ, points);
 51	stdx::construct_n(_baseXYZ, points);
 52	stdx::construct_n(_XYZ, points);
 53	stdx::construct_n(_baseDir, points);
 54	stdx::construct_n(_dir, points);
 55
 56	_baseXYZ[points - 2].z() = 4.0f;
 57
 58	for (unsigned int i = 0; i < points; ++i)
 59		makeNewPoint();
 60}
 61
 62void Spline::makeNewPoint() {
 63	// shift points to rear of path
 64	std::rotate(_baseXYZ.begin(), _baseXYZ.begin() + 1, _baseXYZ.end());
 65	std::rotate(_moveXYZ.begin(), _moveXYZ.begin() + 1, _moveXYZ.end());
 66	std::rotate(_XYZ.begin(), _XYZ.begin() + 1, _XYZ.end());
 67	std::rotate(_phase.begin(), _phase.begin() + 1, _phase.end());
 68	std::rotate(_rate.begin(), _rate.begin() + 1, _rate.end());
 69
 70	// make vector to new point
 71	int lastPoint = points - 1;
 72	float tempX = _baseXYZ[lastPoint - 1].x() - _baseXYZ[lastPoint - 2].x();
 73	float tempZ = _baseXYZ[lastPoint - 1].z() - _baseXYZ[lastPoint - 2].z();
 74
 75	// find good angle
 76	float turnAngle;
 77	float pathAngle = std::atan2(tempX, tempZ);
 78	float distSquared =
 79		_baseXYZ[lastPoint].x() * _baseXYZ[lastPoint].x() +
 80		_baseXYZ[lastPoint].z() * _baseXYZ[lastPoint].z();
 81	if (distSquared > 10000.0f) {
 82		float angleToCenter = std::atan2(-_baseXYZ[lastPoint].x(), -_baseXYZ[lastPoint].z());
 83		turnAngle = angleToCenter - pathAngle;
 84		if (turnAngle > M_PI)
 85			turnAngle -= M_PI * 2.0f;
 86		if (turnAngle < -M_PI)
 87			turnAngle += M_PI * 2.0f;
 88		turnAngle = Common::clamp(turnAngle, -0.7f, 0.7f);
 89	} else
 90		turnAngle = Common::randomFloat(1.4f) - 0.7f;
 91
 92	// rotate new point to some new position
 93	float ca = std::cos(turnAngle);
 94	float sa = std::cos(turnAngle);
 95	_baseXYZ[lastPoint].set(tempX * ca + tempZ * sa, 0.0f, tempX * -sa + tempZ * ca);
 96
 97	// normalize and set length of vector
 98	// make it at least length 2, which is the grid size of the goo
 99	float lengthener = (Common::randomFloat(6.0f) + 2.0f) / _baseXYZ[lastPoint].length();
100	_baseXYZ[lastPoint] *= lengthener;
101
102	// make new movement vector proportional to base vector
103	_moveXYZ[lastPoint].set(
104		Common::randomFloat(0.25f) * -_baseXYZ[lastPoint].z(),
105		0.3f,
106		Common::randomFloat(0.25f) * -_baseXYZ[lastPoint].x()
107	);
108
109	// add vector to previous point to get new point
110	_baseXYZ[lastPoint] += Vector(
111		_baseXYZ[lastPoint - 1].x(),
112		0.0f,
113		_baseXYZ[lastPoint - 1].z()
114	);
115
116	// make new phase and movement rate
117	_phase[lastPoint] = Common::randomFloat(M_PI * 2);
118	_rate[lastPoint]  = Common::randomFloat(1.0f);
119
120	// reset base direction vectors
121	_baseDir.front() = _baseXYZ[1] - _baseXYZ[points - 1];
122	std::transform(
123		_baseXYZ.begin() + 2, _baseXYZ.end(),
124		_baseXYZ.begin(), _baseDir.begin() + 1,
125		std::minus<Vector>()
126	);
127	_baseDir.back() = _baseXYZ[0] - _baseXYZ[points - 2];
128}
129
130Vector Spline::at(unsigned int section, float where) {
131	section = Common::clamp(section, 1u, points - 3);
132
133	return interpolate(_XYZ[section - 1], _XYZ[section], _XYZ[section + 1], _XYZ[section + 2], where);
134}
135
136Vector Spline::direction(unsigned int section, float where) {
137	section = Common::clamp(section, 1u, points - 3);
138
139	Vector direction(interpolate(_dir[section - 1], _dir[section], _dir[section + 1], _dir[section + 2], where));
140	direction.normalize();
141	return direction;
142}
143
144// Here's a little calculus that takes 4 points
145// and interpolates smoothly between the second and third
146// depending on the value of where which can be 0.0 to 1.0.
147// The slope at b is estimated using a and c.  The slope at c
148// is estimated using b and d.
149Vector Spline::interpolate(const Vector& a, const Vector& b, const Vector& c, const Vector& d, float where) {
150	return
151		(((b * 3.0f) + d - a - (c * 3.0f)) * (where * where * where)) * 0.5f +
152		(((a * 2.0f) - (b * 5.0f) + (c * 4.0f) - d) * (where * where)) * 0.5f +
153		((c - a) * where) * 0.5f +
154		b;
155}