gtkradiant/tools/urt/libs/splines/splines.cpp

1424 lines
36 KiB
C++

/*
This code is based on source provided under the terms of the Id Software
LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the
GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of
LICENSE_ID, please contact Id Software immediately at info@idsoftware.com.
All changes and additions to the original source which have been developed by
other contributors (see CONTRIBUTORS) are provided under the terms of the
license the contributors choose (see LICENSE), to the extent permitted by the
LICENSE_ID. If you did not receive a copy of the contributor license,
please contact the GtkRadiant maintainers at info@gtkradiant.com immediately.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "q_shared.h"
#include "splines.h"
extern "C" {
int FS_Write( const void *buffer, int len, fileHandle_t h );
int FS_ReadFile( const char *qpath, void **buffer );
void FS_FreeFile( void *buffer );
fileHandle_t FS_FOpenFileWrite( const char *filename );
void FS_FCloseFile( fileHandle_t f );
void Cbuf_AddText( const char *text );
void Cbuf_Execute (void);
}
float Q_fabs( float f ) {
int tmp = * ( int * ) &f;
tmp &= 0x7FFFFFFF;
return * ( float * ) &tmp;
}
// (SA) making a list of cameras so I can use
// the splines as targets for other things.
// Certainly better ways to do this, but this lets
// me get underway quickly with ents that need spline
// targets.
#define MAX_CAMERAS 64
idCameraDef camera[MAX_CAMERAS];
extern "C" {
qboolean loadCamera(int camNum, const char *name) {
if(camNum < 0 || camNum >= MAX_CAMERAS )
return qfalse;
camera[camNum].clear();
return (qboolean)camera[camNum].load(name);
}
qboolean getCameraInfo(int camNum, int time, float *origin, float *angles, float *fov) {
idVec3 dir, org;
if(camNum < 0 || camNum >= MAX_CAMERAS )
return qfalse;
org[0] = origin[0];
org[1] = origin[1];
org[2] = origin[2];
if (camera[camNum].getCameraInfo(time, org, dir, fov)) {
origin[0] = org[0];
origin[1] = org[1];
origin[2] = org[2];
angles[1] = atan2 (dir[1], dir[0])*180/3.14159;
angles[0] = asin (dir[2])*180/3.14159;
return qtrue;
}
return qfalse;
}
void startCamera(int camNum, int time) {
if(camNum < 0 || camNum >= MAX_CAMERAS )
return;
camera[camNum].startCamera(time);
}
}
//#include "../shared/windings.h"
//#include "../qcommon/qcommon.h"
//#include "../sys/sys_public.h"
//#include "../game/game_entity.h"
idCameraDef splineList;
idCameraDef *g_splineList = &splineList;
idVec3 idSplineList::zero(0,0,0);
void glLabeledPoint(idVec3 &color, idVec3 &point, float size, const char *label) {
glColor3fv(color);
glPointSize(size);
glBegin(GL_POINTS);
glVertex3fv(point);
glEnd();
idVec3 v = point;
v.x += 1;
v.y += 1;
v.z += 1;
glRasterPos3fv (v);
glCallLists (strlen(label), GL_UNSIGNED_BYTE, label);
}
void glBox(idVec3 &color, idVec3 &point, float size) {
idVec3 mins(point);
idVec3 maxs(point);
mins[0] -= size;
mins[1] += size;
mins[2] -= size;
maxs[0] += size;
maxs[1] -= size;
maxs[2] += size;
glColor3fv(color);
glBegin(GL_LINE_LOOP);
glVertex3f(mins[0],mins[1],mins[2]);
glVertex3f(maxs[0],mins[1],mins[2]);
glVertex3f(maxs[0],maxs[1],mins[2]);
glVertex3f(mins[0],maxs[1],mins[2]);
glEnd();
glBegin(GL_LINE_LOOP);
glVertex3f(mins[0],mins[1],maxs[2]);
glVertex3f(maxs[0],mins[1],maxs[2]);
glVertex3f(maxs[0],maxs[1],maxs[2]);
glVertex3f(mins[0],maxs[1],maxs[2]);
glEnd();
glBegin(GL_LINES);
glVertex3f(mins[0],mins[1],mins[2]);
glVertex3f(mins[0],mins[1],maxs[2]);
glVertex3f(mins[0],maxs[1],maxs[2]);
glVertex3f(mins[0],maxs[1],mins[2]);
glVertex3f(maxs[0],mins[1],mins[2]);
glVertex3f(maxs[0],mins[1],maxs[2]);
glVertex3f(maxs[0],maxs[1],maxs[2]);
glVertex3f(maxs[0],maxs[1],mins[2]);
glEnd();
}
void splineTest() {
//g_splineList->load("p:/doom/base/maps/test_base1.camera");
}
void splineDraw() {
//g_splineList->addToRenderer();
}
//extern void D_DebugLine( const idVec3 &color, const idVec3 &start, const idVec3 &end );
void debugLine(idVec3 &color, float x, float y, float z, float x2, float y2, float z2) {
idVec3 from(x, y, z);
idVec3 to(x2, y2, z2);
//D_DebugLine(color, from, to);
}
void idSplineList::addToRenderer() {
if (controlPoints.Num() == 0) {
return;
}
idVec3 mins, maxs;
idVec3 yellow(1.0, 1.0, 0);
idVec3 white(1.0, 1.0, 1.0);
int i;
for(i = 0; i < controlPoints.Num(); i++) {
VectorCopy(*controlPoints[i], mins);
VectorCopy(mins, maxs);
mins[0] -= 8;
mins[1] += 8;
mins[2] -= 8;
maxs[0] += 8;
maxs[1] -= 8;
maxs[2] += 8;
debugLine( yellow, mins[0], mins[1], mins[2], maxs[0], mins[1], mins[2]);
debugLine( yellow, maxs[0], mins[1], mins[2], maxs[0], maxs[1], mins[2]);
debugLine( yellow, maxs[0], maxs[1], mins[2], mins[0], maxs[1], mins[2]);
debugLine( yellow, mins[0], maxs[1], mins[2], mins[0], mins[1], mins[2]);
debugLine( yellow, mins[0], mins[1], maxs[2], maxs[0], mins[1], maxs[2]);
debugLine( yellow, maxs[0], mins[1], maxs[2], maxs[0], maxs[1], maxs[2]);
debugLine( yellow, maxs[0], maxs[1], maxs[2], mins[0], maxs[1], maxs[2]);
debugLine( yellow, mins[0], maxs[1], maxs[2], mins[0], mins[1], maxs[2]);
}
int step = 0;
idVec3 step1;
for(i = 3; i < controlPoints.Num(); i++) {
for (float tension = 0.0f; tension < 1.001f; tension += 0.1f) {
float x = 0;
float y = 0;
float z = 0;
for (int j = 0; j < 4; j++) {
x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension);
y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension);
z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension);
}
if (step == 0) {
step1[0] = x;
step1[1] = y;
step1[2] = z;
step = 1;
} else {
debugLine( white, step1[0], step1[1], step1[2], x, y, z);
step = 0;
}
}
}
}
void idSplineList::buildSpline() {
//int start = Sys_Milliseconds();
clearSpline();
for(int i = 3; i < controlPoints.Num(); i++) {
for (float tension = 0.0f; tension < 1.001f; tension += granularity) {
float x = 0;
float y = 0;
float z = 0;
for (int j = 0; j < 4; j++) {
x += controlPoints[i - (3 - j)]->x * calcSpline(j, tension);
y += controlPoints[i - (3 - j)]->y * calcSpline(j, tension);
z += controlPoints[i - (3 - j)]->z * calcSpline(j, tension);
}
splinePoints.Append(new idVec3(x, y, z));
}
}
dirty = false;
//Com_Printf("Spline build took %f seconds\n", (float)(Sys_Milliseconds() - start) / 1000);
}
void idSplineList::draw(bool editMode) {
int i;
idVec4 yellow(1, 1, 0, 1);
if (controlPoints.Num() == 0) {
return;
}
if (dirty) {
buildSpline();
}
glColor3fv(controlColor);
glPointSize(5);
glBegin(GL_POINTS);
for (i = 0; i < controlPoints.Num(); i++) {
glVertex3fv(*controlPoints[i]);
}
glEnd();
if (editMode) {
for(i = 0; i < controlPoints.Num(); i++) {
glBox(activeColor, *controlPoints[i], 4);
}
}
//Draw the curve
glColor3fv(pathColor);
glBegin(GL_LINE_STRIP);
int count = splinePoints.Num();
for (i = 0; i < count; i++) {
glVertex3fv(*splinePoints[i]);
}
glEnd();
if (editMode) {
glColor3fv(segmentColor);
glPointSize(3);
glBegin(GL_POINTS);
for (i = 0; i < count; i++) {
glVertex3fv(*splinePoints[i]);
}
glEnd();
}
if (count > 0) {
//assert(activeSegment >=0 && activeSegment < count);
if (activeSegment >=0 && activeSegment < count) {
glBox(activeColor, *splinePoints[activeSegment], 6);
glBox(yellow, *splinePoints[activeSegment], 8);
}
}
}
float idSplineList::totalDistance() {
// FIXME: save dist and return
//
if (controlPoints.Num() == 0) {
return 0.0;
}
if (dirty) {
buildSpline();
}
float dist = 0.0;
idVec3 temp;
int count = splinePoints.Num();
for(int i = 1; i < count; i++) {
temp = *splinePoints[i-1];
temp -= *splinePoints[i];
dist += temp.Length();
}
return dist;
}
void idSplineList::initPosition(long bt, long totalTime) {
if (dirty) {
buildSpline();
}
if (splinePoints.Num() == 0) {
return;
}
baseTime = bt;
time = totalTime;
// calc distance to travel ( this will soon be broken into time segments )
splineTime.Clear();
splineTime.Append(bt);
double dist = totalDistance();
double distSoFar = 0.0;
idVec3 temp;
int count = splinePoints.Num();
//for(int i = 2; i < count - 1; i++) {
for(int i = 1; i < count; i++) {
temp = *splinePoints[i-1];
temp -= *splinePoints[i];
distSoFar += temp.Length();
double percent = distSoFar / dist;
percent *= totalTime;
splineTime.Append(percent + bt);
}
assert(splineTime.Num() == splinePoints.Num());
activeSegment = 0;
}
float idSplineList::calcSpline(int step, float tension) {
switch(step) {
case 0: return (pow(1 - tension, 3)) / 6;
case 1: return (3 * pow(tension, 3) - 6 * pow(tension, 2) + 4) / 6;
case 2: return (-3 * pow(tension, 3) + 3 * pow(tension, 2) + 3 * tension + 1) / 6;
case 3: return pow(tension, 3) / 6;
}
return 0.0;
}
void idSplineList::updateSelection(const idVec3 &move) {
if (selected) {
dirty = true;
VectorAdd(*selected, move, *selected);
}
}
void idSplineList::setSelectedPoint(idVec3 *p) {
if (p) {
p->Snap();
for(int i = 0; i < controlPoints.Num(); i++) {
if (*p == *controlPoints[i]) {
selected = controlPoints[i];
}
}
} else {
selected = NULL;
}
}
const idVec3 *idSplineList::getPosition(long t) {
static idVec3 interpolatedPos;
static long lastTime = -1;
int count = splineTime.Num();
if (count == 0) {
return &zero;
}
// Com_Printf("Time: %d\n", t);
assert(splineTime.Num() == splinePoints.Num());
while (activeSegment < count) {
if (splineTime[activeSegment] >= t) {
if (activeSegment > 0 && activeSegment < count - 1) {
double timeHi = splineTime[activeSegment + 1];
double timeLo = splineTime[activeSegment - 1];
double percent = (timeHi - t) / (timeHi - timeLo);
// pick two bounding points
idVec3 v1 = *splinePoints[activeSegment-1];
idVec3 v2 = *splinePoints[activeSegment+1];
v2 *= (1.0 - percent);
v1 *= percent;
v2 += v1;
interpolatedPos = v2;
return &interpolatedPos;
}
return splinePoints[activeSegment];
} else {
activeSegment++;
}
}
return splinePoints[count-1];
}
void idSplineList::parse(const char *(*text) ) {
const char *token;
//Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !Q_stricmp (token, "}") ) {
break;
}
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !Q_stricmp (token, "(") || !Q_stricmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "granularity") == 0) {
granularity = atof(token);
} else if (Q_stricmp(key.c_str(), "name") == 0) {
name = token;
}
token = Com_Parse(text);
} while (1);
if ( !Q_stricmp (token, "}") ) {
break;
}
Com_UngetToken();
// read the control point
idVec3 point;
Com_Parse1DMatrix( text, 3, point );
addPoint(point.x, point.y, point.z);
} while (1);
//Com_UngetToken();
//Com_MatchToken( text, "}" );
dirty = true;
}
void idSplineList::write(fileHandle_t file, const char *p) {
idStr s = va("\t\t%s {\n", p);
FS_Write(s.c_str(), s.length(), file);
//s = va("\t\tname %s\n", name.c_str());
//FS_Write(s.c_str(), s.length(), file);
s = va("\t\t\tgranularity %f\n", granularity);
FS_Write(s.c_str(), s.length(), file);
int count = controlPoints.Num();
for (int i = 0; i < count; i++) {
s = va("\t\t\t( %f %f %f )\n", controlPoints[i]->x, controlPoints[i]->y, controlPoints[i]->z);
FS_Write(s.c_str(), s.length(), file);
}
s = "\t\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
void idCameraDef::getActiveSegmentInfo(int segment, idVec3 &origin, idVec3 &direction, float *fov) {
#if 0
if (!cameraSpline.validTime()) {
buildCamera();
}
double d = (double)segment / numSegments();
getCameraInfo(d * totalTime * 1000, origin, direction, fov);
#endif
/*
if (!cameraSpline.validTime()) {
buildCamera();
}
origin = *cameraSpline.getSegmentPoint(segment);
idVec3 temp;
int numTargets = getTargetSpline()->controlPoints.Num();
int count = cameraSpline.splineTime.Num();
if (numTargets == 0) {
// follow the path
if (cameraSpline.getActiveSegment() < count - 1) {
temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
}
} else if (numTargets == 1) {
temp = *getTargetSpline()->controlPoints[0];
} else {
temp = *getTargetSpline()->getSegmentPoint(segment);
}
temp -= origin;
temp.Normalize();
direction = temp;
*/
}
bool idCameraDef::getCameraInfo(long time, idVec3 &origin, idVec3 &direction, float *fv) {
char buff[1024];
if ((time - startTime) / 1000 > totalTime) {
return false;
}
for (int i = 0; i < events.Num(); i++) {
if (time >= startTime + events[i]->getTime() && !events[i]->getTriggered()) {
events[i]->setTriggered(true);
if (events[i]->getType() == idCameraEvent::EVENT_TARGET) {
setActiveTargetByName(events[i]->getParam());
getActiveTarget()->start(startTime + events[i]->getTime());
//Com_Printf("Triggered event switch to target: %s\n",events[i]->getParam());
} else if (events[i]->getType() == idCameraEvent::EVENT_TRIGGER) {
//idEntity *ent = NULL;
//ent = level.FindTarget( ent, events[i]->getParam());
//if (ent) {
// ent->signal( SIG_TRIGGER );
// ent->ProcessEvent( &EV_Activate, world );
//}
} else if (events[i]->getType() == idCameraEvent::EVENT_FOV) {
memset(buff, 0, sizeof(buff));
strcpy(buff, events[i]->getParam());
const char *param1 = strtok(buff, " \t,\0");
const char *param2 = strtok(NULL, " \t,\0");
float len = (param2) ? atof(param2) : 0;
float newfov = (param1) ? atof(param1) : 90;
fov.reset(fov.getFOV(time), newfov, time, len);
//*fv = fov = atof(events[i]->getParam());
} else if (events[i]->getType() == idCameraEvent::EVENT_FADEIN) {
float time = atof(events[i]->getParam());
Cbuf_AddText(va("fade 0 0 0 0 %f", time));
Cbuf_Execute();
} else if (events[i]->getType() == idCameraEvent::EVENT_FADEOUT) {
float time = atof(events[i]->getParam());
Cbuf_AddText(va("fade 0 0 0 255 %f", time));
Cbuf_Execute();
} else if (events[i]->getType() == idCameraEvent::EVENT_CAMERA) {
memset(buff, 0, sizeof(buff));
strcpy(buff, events[i]->getParam());
const char *param1 = strtok(buff, " \t,\0");
const char *param2 = strtok(NULL, " \t,\0");
if(param2) {
loadCamera(atoi(param1), va("cameras/%s.camera", param2));
startCamera(time);
} else {
loadCamera(0, va("cameras/%s.camera", events[i]->getParam()));
startCamera(time);
}
return true;
} else if (events[i]->getType() == idCameraEvent::EVENT_STOP) {
return false;
}
}
}
origin = *cameraPosition->getPosition(time);
*fv = fov.getFOV(time);
idVec3 temp = origin;
int numTargets = targetPositions.Num();
if (numTargets == 0) {
/*
// follow the path
if (cameraSpline.getActiveSegment() < count - 1) {
temp = *cameraSpline.splinePoints[cameraSpline.getActiveSegment()+1];
if (temp == origin) {
int index = cameraSpline.getActiveSegment() + 2;
while (temp == origin && index < count - 1) {
temp = *cameraSpline.splinePoints[index++];
}
}
}
*/
} else {
if( getActiveTarget()->numPoints() > 0 ) {
temp = *getActiveTarget()->getPosition(time);
}
}
temp -= origin;
temp.Normalize();
direction = temp;
return true;
}
bool idCameraDef::waitEvent(int index) {
//for (int i = 0; i < events.Num(); i++) {
// if (events[i]->getSegment() == index && events[i]->getType() == idCameraEvent::EVENT_WAIT) {
// return true;
// }
//}
return false;
}
#define NUM_CCELERATION_SEGS 10
#define CELL_AMT 5
void idCameraDef::buildCamera() {
int i;
int lastSwitch = 0;
idList<float> waits;
idList<int> targets;
totalTime = baseTime;
cameraPosition->setTime((long)totalTime * 1000);
// we have a base time layout for the path and the target path
// now we need to layer on any wait or speed changes
for (i = 0; i < events.Num(); i++) {
idCameraEvent *ev = events[i];
events[i]->setTriggered(false);
switch (events[i]->getType()) {
case idCameraEvent::EVENT_TARGET : {
targets.Append(i);
break;
}
case idCameraEvent::EVENT_FEATHER : {
long startTime = 0;
float speed = 0;
long loopTime = 10;
float stepGoal = cameraPosition->getBaseVelocity() / (1000 / loopTime);
while (startTime <= 1000) {
cameraPosition->addVelocity(startTime, loopTime, speed);
speed += stepGoal;
if (speed > cameraPosition->getBaseVelocity()) {
speed = cameraPosition->getBaseVelocity();
}
startTime += loopTime;
}
startTime = (long)(totalTime * 1000 - 1000);
long endTime = startTime + 1000;
speed = cameraPosition->getBaseVelocity();
while (startTime < endTime) {
speed -= stepGoal;
if (speed < 0) {
speed = 0;
}
cameraPosition->addVelocity(startTime, loopTime, speed);
startTime += loopTime;
}
break;
}
case idCameraEvent::EVENT_WAIT : {
waits.Append(atof(events[i]->getParam()));
//FIXME: this is quite hacky for Wolf E3, accel and decel needs
// do be parameter based etc..
long startTime = events[i]->getTime() - 1000;
if (startTime < 0) {
startTime = 0;
}
float speed = cameraPosition->getBaseVelocity();
long loopTime = 10;
float steps = speed / ((events[i]->getTime() - startTime) / loopTime);
while (startTime <= events[i]->getTime() - loopTime) {
cameraPosition->addVelocity(startTime, loopTime, speed);
speed -= steps;
startTime += loopTime;
}
cameraPosition->addVelocity(events[i]->getTime(), (long)atof(events[i]->getParam()) * 1000, 0);
startTime = (long)(events[i]->getTime() + atof(events[i]->getParam()) * 1000);
long endTime = startTime + 1000;
speed = 0;
while (startTime <= endTime) {
cameraPosition->addVelocity(startTime, loopTime, speed);
speed += steps;
startTime += loopTime;
}
break;
}
case idCameraEvent::EVENT_TARGETWAIT : {
//targetWaits.Append(i);
break;
}
case idCameraEvent::EVENT_SPEED : {
/*
// take the average delay between up to the next five segments
float adjust = atof(events[i]->getParam());
int index = events[i]->getSegment();
total = 0;
count = 0;
// get total amount of time over the remainder of the segment
for (j = index; j < cameraSpline.numSegments() - 1; j++) {
total += cameraSpline.getSegmentTime(j + 1) - cameraSpline.getSegmentTime(j);
count++;
}
// multiply that by the adjustment
double newTotal = total * adjust;
// what is the difference..
newTotal -= total;
totalTime += newTotal / 1000;
// per segment difference
newTotal /= count;
int additive = newTotal;
// now propogate that difference out to each segment
for (j = index; j < cameraSpline.numSegments(); j++) {
cameraSpline.addSegmentTime(j, additive);
additive += newTotal;
}
break;
*/
}
}
}
for (i = 0; i < waits.Num(); i++) {
totalTime += waits[i];
}
// on a new target switch, we need to take time to this point ( since last target switch )
// and allocate it across the active target, then reset time to this point
long timeSoFar = 0;
long total = (long)(totalTime * 1000);
for (i = 0; i < targets.Num(); i++) {
long t;
if (i < targets.Num() - 1) {
t = events[targets[i+1]]->getTime();
} else {
t = total - timeSoFar;
}
// t is how much time to use for this target
setActiveTargetByName(events[targets[i]]->getParam());
getActiveTarget()->setTime(t);
timeSoFar += t;
}
}
void idCameraDef::startCamera(long t) {
cameraPosition->clearVelocities();
cameraPosition->start(t);
buildCamera();
fov.reset(90, 90, t, 0);
//for (int i = 0; i < targetPositions.Num(); i++) {
// targetPositions[i]->
//}
startTime = t;
cameraRunning = true;
}
void idCameraDef::parse(const char *(*text) ) {
const char *token;
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !Q_stricmp (token, "}") ) {
break;
}
if (Q_stricmp(token, "time") == 0) {
baseTime = Com_ParseFloat(text);
}
else if (Q_stricmp(token, "camera_fixed") == 0) {
cameraPosition = new idFixedPosition();
cameraPosition->parse(text);
}
else if (Q_stricmp(token, "camera_interpolated") == 0) {
cameraPosition = new idInterpolatedPosition();
cameraPosition->parse(text);
}
else if (Q_stricmp(token, "camera_spline") == 0) {
cameraPosition = new idSplinePosition();
cameraPosition->parse(text);
}
else if (Q_stricmp(token, "target_fixed") == 0) {
idFixedPosition *pos = new idFixedPosition();
pos->parse(text);
targetPositions.Append(pos);
}
else if (Q_stricmp(token, "target_interpolated") == 0) {
idInterpolatedPosition *pos = new idInterpolatedPosition();
pos->parse(text);
targetPositions.Append(pos);
}
else if (Q_stricmp(token, "target_spline") == 0) {
idSplinePosition *pos = new idSplinePosition();
pos->parse(text);
targetPositions.Append(pos);
}
else if (Q_stricmp(token, "fov") == 0) {
fov.parse(text);
}
else if (Q_stricmp(token, "event") == 0) {
idCameraEvent *event = new idCameraEvent();
event->parse(text);
addEvent(event);
}
} while (1);
if ( !cameraPosition ) {
Com_Printf( "no camera position specified\n" );
// prevent a crash later on
cameraPosition = new idFixedPosition();
}
Com_UngetToken();
Com_MatchToken( text, "}" );
}
bool idCameraDef::load(const char *filename) {
char *buf;
const char *buf_p;
int length = FS_ReadFile( filename, (void **)&buf );
if ( !buf ) {
return false;
}
clear();
Com_BeginParseSession( filename );
buf_p = buf;
parse(&buf_p);
Com_EndParseSession();
FS_FreeFile( buf );
return true;
}
void idCameraDef::save(const char *filename) {
fileHandle_t file = FS_FOpenFileWrite(filename);
if (file) {
int i;
idStr s = "cameraPathDef { \n";
FS_Write(s.c_str(), s.length(), file);
s = va("\ttime %f\n", baseTime);
FS_Write(s.c_str(), s.length(), file);
cameraPosition->write(file, va("camera_%s",cameraPosition->typeStr()));
for (i = 0; i < numTargets(); i++) {
targetPositions[i]->write(file, va("target_%s", targetPositions[i]->typeStr()));
}
for (i = 0; i < events.Num(); i++) {
events[i]->write(file, "event");
}
fov.write(file, "fov");
s = "}\n";
FS_Write(s.c_str(), s.length(), file);
}
FS_FCloseFile(file);
}
int idCameraDef::sortEvents(const void *p1, const void *p2) {
idCameraEvent *ev1 = (idCameraEvent*)(p1);
idCameraEvent *ev2 = (idCameraEvent*)(p2);
if (ev1->getTime() > ev2->getTime()) {
return -1;
}
if (ev1->getTime() < ev2->getTime()) {
return 1;
}
return 0;
}
void idCameraDef::addEvent(idCameraEvent *event) {
events.Append(event);
//events.Sort(&sortEvents);
}
void idCameraDef::addEvent(idCameraEvent::eventType t, const char *param, long time) {
addEvent(new idCameraEvent(t, param, time));
buildCamera();
}
void idCameraDef::removeEvent(int index) {
events.RemoveIndex(index);
buildCamera();
}
const char *idCameraEvent::eventStr[] = {
"NA",
"WAIT",
"TARGETWAIT",
"SPEED",
"TARGET",
"SNAPTARGET",
"FOV",
"CMD",
"TRIGGER",
"STOP",
"CAMERA",
"FADEOUT",
"FADEIN",
"FEATHER"
};
void idCameraEvent::parse(const char *(*text) ) {
const char *token;
Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !strcmp (token, "}") ) {
break;
}
// here we may have to jump over brush epairs ( only used in editor )
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "type") == 0) {
type = static_cast<idCameraEvent::eventType>(atoi(token));
} else if (Q_stricmp(key.c_str(), "param") == 0) {
paramStr = token;
} else if (Q_stricmp(key.c_str(), "time") == 0) {
time = atoi(token);
}
token = Com_Parse(text);
} while (1);
if ( !strcmp (token, "}") ) {
break;
}
} while (1);
Com_UngetToken();
Com_MatchToken( text, "}" );
}
void idCameraEvent::write(fileHandle_t file, const char *name) {
idStr s = va("\t%s {\n", name);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\ttype %d\n", static_cast<int>(type));
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tparam \"%s\"\n", paramStr.c_str());
FS_Write(s.c_str(), s.length(), file);
s = va("\t\ttime %d\n", time);
FS_Write(s.c_str(), s.length(), file);
s = "\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
const char *idCameraPosition::positionStr[] = {
"Fixed",
"Interpolated",
"Spline",
};
const idVec3 *idInterpolatedPosition::getPosition(long t) {
static idVec3 interpolatedPos;
float velocity = getVelocity(t);
float timePassed = t - lastTime;
lastTime = t;
// convert to seconds
timePassed /= 1000;
float distToTravel = timePassed * velocity;
idVec3 temp = startPos;
temp -= endPos;
float distance = temp.Length();
distSoFar += distToTravel;
float percent = (float)(distSoFar) / distance;
if (percent > 1.0) {
percent = 1.0;
} else if (percent < 0.0) {
percent = 0.0;
}
// the following line does a straigt calc on percentage of time
// float percent = (float)(startTime + time - t) / time;
idVec3 v1 = startPos;
idVec3 v2 = endPos;
v1 *= (1.0 - percent);
v2 *= percent;
v1 += v2;
interpolatedPos = v1;
return &interpolatedPos;
}
void idCameraFOV::parse(const char *(*text) ) {
const char *token;
Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !strcmp (token, "}") ) {
break;
}
// here we may have to jump over brush epairs ( only used in editor )
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "fov") == 0) {
fov = atof(token);
} else if (Q_stricmp(key.c_str(), "startFOV") == 0) {
startFOV = atof(token);
} else if (Q_stricmp(key.c_str(), "endFOV") == 0) {
endFOV = atof(token);
} else if (Q_stricmp(key.c_str(), "time") == 0) {
time = atoi(token);
}
token = Com_Parse(text);
} while (1);
if ( !strcmp (token, "}") ) {
break;
}
} while (1);
Com_UngetToken();
Com_MatchToken( text, "}" );
}
bool idCameraPosition::parseToken(const char *key, const char *(*text)) {
const char *token = Com_Parse(text);
if (Q_stricmp(key, "time") == 0) {
time = atol(token);
return true;
} else if (Q_stricmp(key, "type") == 0) {
type = static_cast<idCameraPosition::positionType>(atoi(token));
return true;
} else if (Q_stricmp(key, "velocity") == 0) {
long t = atol(token);
token = Com_Parse(text);
long d = atol(token);
token = Com_Parse(text);
float s = atof(token);
addVelocity(t, d, s);
return true;
} else if (Q_stricmp(key, "baseVelocity") == 0) {
baseVelocity = atof(token);
return true;
} else if (Q_stricmp(key, "name") == 0) {
name = token;
return true;
} else if (Q_stricmp(key, "time") == 0) {
time = atoi(token);
return true;
}
Com_UngetToken();
return false;
}
void idFixedPosition::parse(const char *(*text) ) {
const char *token;
Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !strcmp (token, "}") ) {
break;
}
// here we may have to jump over brush epairs ( only used in editor )
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "pos") == 0) {
Com_UngetToken();
Com_Parse1DMatrix( text, 3, pos );
} else {
Com_UngetToken();
idCameraPosition::parseToken(key.c_str(), text);
}
token = Com_Parse(text);
} while (1);
if ( !strcmp (token, "}") ) {
break;
}
} while (1);
Com_UngetToken();
Com_MatchToken( text, "}" );
}
void idInterpolatedPosition::parse(const char *(*text) ) {
const char *token;
Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !strcmp (token, "}") ) {
break;
}
// here we may have to jump over brush epairs ( only used in editor )
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "startPos") == 0) {
Com_UngetToken();
Com_Parse1DMatrix( text, 3, startPos );
} else if (Q_stricmp(key.c_str(), "endPos") == 0) {
Com_UngetToken();
Com_Parse1DMatrix( text, 3, endPos );
} else {
Com_UngetToken();
idCameraPosition::parseToken(key.c_str(), text);
}
token = Com_Parse(text);
} while (1);
if ( !strcmp (token, "}") ) {
break;
}
} while (1);
Com_UngetToken();
Com_MatchToken( text, "}" );
}
void idSplinePosition::parse(const char *(*text) ) {
const char *token;
Com_MatchToken( text, "{" );
do {
token = Com_Parse( text );
if ( !token[0] ) {
break;
}
if ( !strcmp (token, "}") ) {
break;
}
// here we may have to jump over brush epairs ( only used in editor )
do {
// if token is not a brace, it is a key for a key/value pair
if ( !token[0] || !strcmp (token, "(") || !strcmp(token, "}")) {
break;
}
Com_UngetToken();
idStr key = Com_ParseOnLine(text);
const char *token = Com_Parse(text);
if (Q_stricmp(key.c_str(), "target") == 0) {
target.parse(text);
} else {
Com_UngetToken();
idCameraPosition::parseToken(key.c_str(), text);
}
token = Com_Parse(text);
} while (1);
if ( !strcmp (token, "}") ) {
break;
}
} while (1);
Com_UngetToken();
Com_MatchToken( text, "}" );
}
void idCameraFOV::write(fileHandle_t file, const char *p) {
idStr s = va("\t%s {\n", p);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tfov %f\n", fov);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tstartFOV %f\n", startFOV);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tendFOV %f\n", endFOV);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\ttime %i\n", time);
FS_Write(s.c_str(), s.length(), file);
s = "\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
void idCameraPosition::write(fileHandle_t file, const char *p) {
idStr s = va("\t\ttime %i\n", time);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\ttype %i\n", static_cast<int>(type));
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tname %s\n", name.c_str());
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tbaseVelocity %f\n", baseVelocity);
FS_Write(s.c_str(), s.length(), file);
for (int i = 0; i < velocities.Num(); i++) {
s = va("\t\tvelocity %i %i %f\n", velocities[i]->startTime, velocities[i]->time, velocities[i]->speed);
FS_Write(s.c_str(), s.length(), file);
}
}
void idFixedPosition::write(fileHandle_t file, const char *p) {
idStr s = va("\t%s {\n", p);
FS_Write(s.c_str(), s.length(), file);
idCameraPosition::write(file, p);
s = va("\t\tpos ( %f %f %f )\n", pos.x, pos.y, pos.z);
FS_Write(s.c_str(), s.length(), file);
s = "\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
void idInterpolatedPosition::write(fileHandle_t file, const char *p) {
idStr s = va("\t%s {\n", p);
FS_Write(s.c_str(), s.length(), file);
idCameraPosition::write(file, p);
s = va("\t\tstartPos ( %f %f %f )\n", startPos.x, startPos.y, startPos.z);
FS_Write(s.c_str(), s.length(), file);
s = va("\t\tendPos ( %f %f %f )\n", endPos.x, endPos.y, endPos.z);
FS_Write(s.c_str(), s.length(), file);
s = "\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
void idSplinePosition::write(fileHandle_t file, const char *p) {
idStr s = va("\t%s {\n", p);
FS_Write(s.c_str(), s.length(), file);
idCameraPosition::write(file, p);
target.write(file, "target");
s = "\t}\n";
FS_Write(s.c_str(), s.length(), file);
}
void idCameraDef::addTarget(const char *name, idCameraPosition::positionType type) {
const char *text = (name == NULL) ? va("target0%d", numTargets()+1) : name;
idCameraPosition *pos = newFromType(type);
if (pos) {
pos->setName(name);
targetPositions.Append(pos);
activeTarget = numTargets()-1;
if (activeTarget == 0) {
// first one
addEvent(idCameraEvent::EVENT_TARGET, name, 0);
}
}
}
const idVec3 *idSplinePosition::getPosition(long t) {
static idVec3 interpolatedPos;
float velocity = getVelocity(t);
float timePassed = t - lastTime;
lastTime = t;
// convert to seconds
timePassed /= 1000;
float distToTravel = timePassed * velocity;
distSoFar += distToTravel;
double tempDistance = target.totalDistance();
double percent = (double)(distSoFar) / tempDistance;
double targetDistance = percent * tempDistance;
tempDistance = 0;
double lastDistance1,lastDistance2;
lastDistance1 = lastDistance2 = 0;
idVec3 temp;
int count = target.numSegments();
int i;
for(i = 1; i < count; i++) {
temp = *target.getSegmentPoint(i-1);
temp -= *target.getSegmentPoint(i);
tempDistance += temp.Length();
if (i & 1) {
lastDistance1 = tempDistance;
} else {
lastDistance2 = tempDistance;
}
if (tempDistance >= targetDistance) {
break;
}
}
if ( i >= count - 1) {
interpolatedPos = *target.getSegmentPoint(i-1);
} else {
#if 0
double timeHi = target.getSegmentTime(i + 1);
double timeLo = target.getSegmentTime(i - 1);
double percent = (timeHi - t) / (timeHi - timeLo);
idVec3 v1 = *target.getSegmentPoint(i - 1);
idVec3 v2 = *target.getSegmentPoint(i + 1);
v2 *= (1.0 - percent);
v1 *= percent;
v2 += v1;
interpolatedPos = v2;
#else
if (lastDistance1 > lastDistance2) {
double d = lastDistance2;
lastDistance2 = lastDistance1;
lastDistance1 = d;
}
idVec3 v1 = *target.getSegmentPoint(i - 1);
idVec3 v2 = *target.getSegmentPoint(i);
double percent = (lastDistance2 - targetDistance) / (lastDistance2 - lastDistance1);
v2 *= (1.0 - percent);
v1 *= percent;
v2 += v1;
interpolatedPos = v2;
#endif
}
return &interpolatedPos;
}