Initial commit
This commit is contained in:
257
engines/tetraedge/te/te_bezier_curve.cpp
Normal file
257
engines/tetraedge/te/te_bezier_curve.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
|
||||
#include "tetraedge/te/te_bezier_curve.h"
|
||||
#include "tetraedge/te/te_mesh.h"
|
||||
#include "tetraedge/te/te_renderer.h"
|
||||
#include "tetraedge/tetraedge.h"
|
||||
|
||||
namespace Tetraedge {
|
||||
|
||||
TeBezierCurve::TeBezierCurve() : _length(0.0), _rawLength(0.0), _lengthNeedsUpdate(true),
|
||||
_rawLengthNeedsUpdate(true), _numIterations(1000) {
|
||||
}
|
||||
|
||||
//int TeBezierCurve::bounds(int start);
|
||||
|
||||
void TeBezierCurve::clear() {
|
||||
_lengthNeedsUpdate = true;
|
||||
_rawLengthNeedsUpdate = true;
|
||||
_length = 0.0;
|
||||
_controlPoints.clear();
|
||||
}
|
||||
|
||||
void TeBezierCurve::draw() {
|
||||
if (!worldVisible() || _controlPoints.empty())
|
||||
return;
|
||||
|
||||
Common::SharedPtr<TeMesh> mesh1(TeMesh::makeInstance());
|
||||
Common::SharedPtr<TeMesh> mesh2(TeMesh::makeInstance());
|
||||
uint npoints = _controlPoints.size();
|
||||
|
||||
mesh1->setConf(npoints, npoints, TeMesh::MeshMode_Points, 0, 0);
|
||||
for (uint i = 0; i < npoints; i++) {
|
||||
mesh1->setVertex(i, _controlPoints[i]);
|
||||
mesh1->setIndex(i, i);
|
||||
}
|
||||
|
||||
mesh2->setConf(npoints, npoints, TeMesh::MeshMode_LineStrip, 0, 0);
|
||||
for (uint i = 0; i < npoints; i++) {
|
||||
mesh2->setVertex(i, _controlPoints[i]);
|
||||
mesh2->setNormal(i, TeVector3f32(0.0f, 1.0f, 0.0));
|
||||
mesh2->setIndex(i, i);
|
||||
}
|
||||
|
||||
TeRenderer *renderer = g_engine->getRenderer();
|
||||
const TeColor prevColor = renderer->currentColor();
|
||||
renderer->pushMatrix();
|
||||
renderer->multiplyMatrix(worldTransformationMatrix());
|
||||
renderer->setCurrentColor(TeColor(0, 0xff, 0xff, 0xff));
|
||||
mesh2->draw();
|
||||
renderer->setCurrentColor(TeColor(0xff, 0, 0xff, 0xff));
|
||||
mesh1->draw();
|
||||
renderer->popMatrix();
|
||||
renderer->setCurrentColor(prevColor);
|
||||
}
|
||||
|
||||
float TeBezierCurve::length() {
|
||||
if (_lengthNeedsUpdate) {
|
||||
_length = 0.0;
|
||||
_lengthNeedsUpdate = false;
|
||||
_lengths.clear();
|
||||
|
||||
TeVector3f32 lastpt = _controlPoints[0];
|
||||
lastpt.y() = 0;
|
||||
for (uint i = 0; i < _numIterations; i++) {
|
||||
float amount = (float)i / _numIterations;
|
||||
TeVector3f32 pt = retrievePoint(amount);
|
||||
pt.y() = 0;
|
||||
float len = (lastpt - pt).length();
|
||||
_length += len;
|
||||
_lengths.push_back(_length);
|
||||
lastpt = pt;
|
||||
}
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
void TeBezierCurve::pseudoTangent(float offset, TeVector3f32 &v1, TeVector3f32 &v2) {
|
||||
const float step = 1.0f / _numIterations;
|
||||
if (step + offset <= 1.0f) {
|
||||
v1 = retrievePoint(offset);
|
||||
v2 = retrievePoint(offset + step);
|
||||
} else {
|
||||
v1 = retrievePoint(offset - step);
|
||||
v2 = retrievePoint(offset);
|
||||
}
|
||||
}
|
||||
|
||||
float TeBezierCurve::rawLength() {
|
||||
if (_rawLengthNeedsUpdate) {
|
||||
_rawLengthNeedsUpdate = false;
|
||||
_rawLength = 0.0;
|
||||
_rawLengths.clear();
|
||||
_rawLengths.push_back(0.0);
|
||||
for (uint i = 1; i < _controlPoints.size(); i++) {
|
||||
const TeVector3f32 diff = _controlPoints[i] - _controlPoints[i - 1];
|
||||
_rawLength += diff.length();
|
||||
_rawLengths.push_back(_rawLength);
|
||||
}
|
||||
}
|
||||
return _rawLength;
|
||||
}
|
||||
|
||||
TeVector3f32 TeBezierCurve::retrievePoint(float offset) {
|
||||
const int npoints = _controlPoints.size();
|
||||
|
||||
// Simple cases for small numbers of points.
|
||||
if (npoints == 0)
|
||||
return TeVector3f32();
|
||||
else if (npoints == 1)
|
||||
return _controlPoints[0];
|
||||
else if (npoints == 2)
|
||||
return _controlPoints[0] + (_controlPoints[1] - _controlPoints[0]) * offset;
|
||||
|
||||
// else, there are at least 3 points so need to actually interpolate.
|
||||
const float rawlen = rawLength();
|
||||
|
||||
float proportion = 0.0f;
|
||||
int startpt = 0;
|
||||
while (startpt < npoints) {
|
||||
proportion = _rawLengths[startpt] / rawlen;
|
||||
if (proportion >= offset)
|
||||
break;
|
||||
startpt++;
|
||||
}
|
||||
|
||||
float t;
|
||||
if (proportion == offset) {
|
||||
// Exactly on a point
|
||||
t = 0.0f;
|
||||
} else {
|
||||
// Proportion between two points
|
||||
float p1 = _rawLengths[startpt - 1];
|
||||
float p2 = _rawLengths[startpt];
|
||||
t = (rawlen * offset - p1) / (p2 - p1);
|
||||
startpt--;
|
||||
}
|
||||
|
||||
// Collect 4 points around the startpt (1 before, 2 after)
|
||||
TeVector3f32 points[4];
|
||||
const int maxPt = _controlPoints.size() - 1;
|
||||
for (int p = 0; p < 4; p++) {
|
||||
int ptno = CLIP(startpt + p - 1, 0, maxPt);
|
||||
points[p] = _controlPoints[ptno];
|
||||
}
|
||||
|
||||
// If we hit the end, linearly extend the last gradient.
|
||||
if (startpt <= 0)
|
||||
points[0] += (points[1] - points[2]);
|
||||
|
||||
if (startpt + 1 >= maxPt)
|
||||
points[3] += (points[2] - points[1]);
|
||||
|
||||
return hermiteInterpolate(t, points, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void TeBezierCurve::setControlPoints(const Common::Array<TeVector3f32> &points) {
|
||||
_lengthNeedsUpdate = true;
|
||||
_rawLengthNeedsUpdate = true;
|
||||
_controlPoints = points;
|
||||
}
|
||||
|
||||
void TeBezierCurve::setNbIterations(uint iterations) {
|
||||
_lengthNeedsUpdate = true;
|
||||
_rawLengthNeedsUpdate = true;
|
||||
_numIterations = iterations;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void TeBezierCurve::serialize(Common::WriteStream &stream, const TeBezierCurve &curve) {
|
||||
error("TODO: Implement TeBezierCurve::serialize");
|
||||
}
|
||||
|
||||
/*static*/
|
||||
void TeBezierCurve::deserialize(Common::ReadStream &stream, TeBezierCurve &curve) {
|
||||
Te3DObject2::deserialize(stream, curve);
|
||||
|
||||
curve._lengthNeedsUpdate = false;
|
||||
curve._length = stream.readFloatLE();
|
||||
uint32 npoints = stream.readUint32LE();
|
||||
if (npoints > 1000000)
|
||||
error("TeBezierCurve::deserialize improbable number of control ponts %d", npoints);
|
||||
|
||||
for (uint i = 0; i < npoints; i++) {
|
||||
TeVector3f32 vec;
|
||||
TeVector3f32::deserialize(stream, vec);
|
||||
curve._controlPoints.push_back(vec);
|
||||
}
|
||||
}
|
||||
|
||||
void TeBezierCurve::loadBin(TetraedgeFSNode &node) {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> file(node.createReadStream());
|
||||
Common::String fname = node.getPath().baseName();
|
||||
if (fname.size() < 4)
|
||||
error("TeBezierCurve::loadBin fname %s is too short", fname.c_str());
|
||||
setName(fname.substr(0, fname.size() - 4));
|
||||
|
||||
// Load position / rotation / size
|
||||
Te3DObject2::deserialize(*file, *this, false);
|
||||
// Then it resets them?
|
||||
setPosition(TeVector3f32());
|
||||
setRotation(TeQuaternion());
|
||||
setSize(TeVector3f32(1, 1, 1));
|
||||
|
||||
_lengthNeedsUpdate = true;
|
||||
uint32 npoints = file->readUint32LE();
|
||||
if (npoints > 1000000)
|
||||
error("TeBezierCurve::loadBin improbable number of control ponts %d", npoints);
|
||||
|
||||
for (uint i = 0; i < npoints; i++) {
|
||||
TeVector3f32 vec;
|
||||
TeVector3f32::deserialize(*file, vec);
|
||||
_controlPoints.push_back(vec);
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/
|
||||
TeVector3f32 TeBezierCurve::hermiteInterpolate(float t, const TeVector3f32 *points, float param_4, float param_5) {
|
||||
assert(points);
|
||||
const TeVector3f32 delta1 = ((points[1] - points[0]) * (param_5 + 1.0) * (1.0 - param_4)) / 2.0;
|
||||
const TeVector3f32 delta2a = ((points[2] - points[1]) * (1.0 - param_5) * (1.0 - param_4)) / 2.0;
|
||||
const TeVector3f32 delta2b = ((points[2] - points[1]) * (param_5 + 1.0) * (1.0 - param_4)) / 2.0;
|
||||
const TeVector3f32 delta3 = ((points[3] - points[2]) * (1.0 - param_5) * (1.0 - param_4)) / 2.0;
|
||||
|
||||
const TeVector3f32 x1 = delta1 + delta2a;
|
||||
const TeVector3f32 x2 = delta2b + delta3;
|
||||
|
||||
const float t2 = t * t;
|
||||
const float t3 = t * t * t;
|
||||
const TeVector3f32 h1a = points[1] * ((t3 + t3) - t2 * 3.0 + 1.0);
|
||||
const TeVector3f32 h1b = x1 * ((t3 - (t2 + t2)) + t);
|
||||
const TeVector3f32 h1 = (h1a + h1b) + (x2 * (t3 - t2));
|
||||
return h1 + (points[2] * (t3 * -2.0 + t2 * 3.0));
|
||||
}
|
||||
|
||||
|
||||
} // end namespace Tetraedge
|
||||
Reference in New Issue
Block a user