vmap/libs/splines/splines.h

1140 lines
21 KiB
C++

/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.
This file is part of GtkRadiant.
GtkRadiant 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.
GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __SPLINES_H
#define __SPLINES_H
#define GTKRADIANT
#ifdef GTKRADIANT
#include "igl.h"
#endif
#include "util_list.h"
#include "util_str.h"
#include "math_vector.h"
typedef int fileHandle_t;
extern void glBox( idVec3 &color, idVec3 &point, float size );
extern void glLabeledPoint( idVec3 &color, idVec3 &point, float size, const char *label );
static idVec4 blue( 0, 0, 1, 1 );
static idVec4 red( 1, 0, 0, 1 );
class idPointListInterface {
public:
idPointListInterface() {
selectedPoints.Clear();
};
virtual ~idPointListInterface() {
};
virtual int numPoints() {
return 0;
}
virtual void addPoint( const float x, const float y, const float z ) {
}
virtual void addPoint( const idVec3 &v ) {
}
virtual void removePoint( int index ) {
}
virtual idVec3 *getPoint( int index ) {
return NULL;
}
int selectPointByRay( float ox, float oy, float oz, float dx, float dy, float dz, bool single ) {
idVec3 origin( ox, oy, oz );
idVec3 dir( dx, dy, dz );
return selectPointByRay( origin, dir, single );
}
int selectPointByRay( const idVec3 origin, const idVec3 direction, bool single ) {
int i, besti, count;
float d, bestd;
idVec3 temp, temp2;
// find the point closest to the ray
besti = -1;
bestd = 8;
count = numPoints();
for ( i = 0; i < count; i++ ) {
temp = *getPoint( i );
temp2 = temp;
temp -= origin;
d = DotProduct( temp, direction );
__VectorMA( origin, d, direction, temp );
temp2 -= temp;
d = temp2.Length();
if ( d <= bestd ) {
bestd = d;
besti = i;
}
}
if ( besti >= 0 ) {
selectPoint( besti, single );
}
return besti;
}
int isPointSelected( int index ) {
int count = selectedPoints.Num();
for ( int i = 0; i < count; i++ ) {
if ( selectedPoints[i] == index ) {
return i;
}
}
return -1;
}
int selectPoint( int index, bool single ) {
if ( index >= 0 && index < numPoints() ) {
if ( single ) {
deselectAll();
}
else {
if ( isPointSelected( index ) >= 0 ) {
selectedPoints.Remove( index );
}
}
return selectedPoints.Append( index );
}
return -1;
}
void selectAll() {
selectedPoints.Clear();
for ( int i = 0; i < numPoints(); i++ ) {
selectedPoints.Append( i );
}
}
void deselectAll() {
selectedPoints.Clear();
}
int numSelectedPoints();
idVec3 *getSelectedPoint( int index ) {
assert( index >= 0 && index < numSelectedPoints() );
return getPoint( selectedPoints[index] );
}
virtual void updateSelection( float x, float y, float z ) {
idVec3 move( x, y, z );
updateSelection( move );
}
virtual void updateSelection( const idVec3 &move ) {
int count = selectedPoints.Num();
for ( int i = 0; i < count; i++ ) {
*getPoint( selectedPoints[i] ) += move;
}
}
void drawSelection() {
int count = selectedPoints.Num();
for ( int i = 0; i < count; i++ ) {
glBox( red, *getPoint( selectedPoints[i] ), 4 );
}
}
protected:
idList<int> selectedPoints;
};
class idSplineList {
public:
idSplineList() {
clear();
}
idSplineList( const char *p ) {
clear();
name = p;
};
~idSplineList() {
clear();
};
void clearControl() {
for ( int i = 0; i < controlPoints.Num(); i++ ) {
delete controlPoints[i];
}
controlPoints.Clear();
}
void clearSpline() {
for ( int i = 0; i < splinePoints.Num(); i++ ) {
delete splinePoints[i];
}
splinePoints.Clear();
}
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
void clear() {
clearControl();
clearSpline();
splineTime.Clear();
selected = NULL;
dirty = true;
activeSegment = 0;
granularity = 0.025f;
pathColor.set( 1.0f, 0.5f, 0.0f );
controlColor.set( 0.7f, 0.0f, 1.0f );
segmentColor.set( 0.0f, 0.0f, 1.0f );
activeColor.set( 1.0f, 0.0f, 0.0f );
}
void initPosition( long startTime, long totalTime );
const idVec3 *getPosition( long time );
void draw( bool editMode );
void addToRenderer();
void setSelectedPoint( idVec3 *p );
idVec3 *getSelectedPoint() {
return selected;
}
void addPoint( const idVec3 &v ) {
controlPoints.Append( new idVec3( v ) );
dirty = true;
}
void addPoint( float x, float y, float z ) {
controlPoints.Append( new idVec3( x, y, z ) );
dirty = true;
}
void updateSelection( const idVec3 &move );
void startEdit() {
editMode = true;
}
void stopEdit() {
editMode = false;
}
void buildSpline();
void setGranularity( float f ) {
granularity = f;
}
float getGranularity() {
return granularity;
}
int numPoints() {
return controlPoints.Num();
}
idVec3 *getPoint( int index ) {
assert( index >= 0 && index < controlPoints.Num() );
return controlPoints[index];
}
idVec3 *getSegmentPoint( int index ) {
assert( index >= 0 && index < splinePoints.Num() );
return splinePoints[index];
}
void setSegmentTime( int index, int time ) {
assert( index >= 0 && index < splinePoints.Num() );
splineTime[index] = time;
}
int getSegmentTime( int index ) {
assert( index >= 0 && index < splinePoints.Num() );
return (int)splineTime[index];
}
void addSegmentTime( int index, int time ) {
assert( index >= 0 && index < splinePoints.Num() );
splineTime[index] += time;
}
float totalDistance();
static idVec3 zero;
int getActiveSegment() {
return activeSegment;
}
void setActiveSegment( int i ) {
//assert(i >= 0 && (splinePoints.Num() > 0 && i < splinePoints.Num()));
activeSegment = i;
}
int numSegments() {
return splinePoints.Num();
}
void setColors( idVec3 &path, idVec3 &segment, idVec3 &control, idVec3 &active ) {
pathColor = path;
segmentColor = segment;
controlColor = control;
activeColor = active;
}
const char *getName() {
return name.c_str();
}
void setName( const char *p ) {
name = p;
}
bool validTime() {
if ( dirty ) {
buildSpline();
}
// gcc doesn't allow static casting away from bools
// why? I've no idea...
return (bool)( splineTime.Num() > 0 && splineTime.Num() == splinePoints.Num() );
}
void setTime( long t ) {
time = t;
}
void setBaseTime( long t ) {
baseTime = t;
}
protected:
idStr name;
float calcSpline( int step, float tension );
idList<idVec3*> controlPoints;
idList<idVec3*> splinePoints;
idList<double> splineTime;
idVec3 *selected;
idVec3 pathColor, segmentColor, controlColor, activeColor;
float granularity;
bool editMode;
bool dirty;
int activeSegment;
long baseTime;
long time;
friend class idCamera;
};
// time in milliseconds
// velocity where 1.0 equal rough walking speed
struct idVelocity {
idVelocity( long start, long duration, float s ) {
startTime = start;
time = duration;
speed = s;
}
long startTime;
long time;
float speed;
};
// can either be a look at or origin position for a camera
//
class idCameraPosition : public idPointListInterface {
public:
virtual void clearVelocities() {
for ( int i = 0; i < velocities.Num(); i++ ) {
delete velocities[i];
velocities[i] = NULL;
}
velocities.Clear();
}
virtual void clear() {
editMode = false;
clearVelocities();
}
idCameraPosition( const char *p ) {
name = p;
}
idCameraPosition() {
time = 0;
name = "position";
}
idCameraPosition( long t ) {
time = t;
}
virtual ~idCameraPosition() {
clear();
}
// this can be done with RTTI syntax but i like the derived classes setting a type
// makes serialization a bit easier to see
//
enum positionType {
FIXED = 0x00,
INTERPOLATED,
SPLINE,
POSITION_COUNT
};
virtual void start( long t ) {
startTime = t;
}
long getTime() {
return time;
}
virtual void setTime( long t ) {
time = t;
}
float getBaseVelocity() {
return baseVelocity;
}
float getVelocity( long t ) {
long check = t - startTime;
for ( int i = 0; i < velocities.Num(); i++ ) {
if ( check >= velocities[i]->startTime && check <= velocities[i]->startTime + velocities[i]->time ) {
return velocities[i]->speed;
}
}
return baseVelocity;
}
void addVelocity( long start, long duration, float speed ) {
velocities.Append( new idVelocity( start, duration, speed ) );
}
virtual const idVec3 *getPosition( long t ) {
assert( true );
return NULL;
}
virtual void draw( bool editMode ) {
};
virtual void parse( const char *( *text ) ) {
};
virtual void write( fileHandle_t file, const char *name );
virtual bool parseToken( const char *key, const char *( *text ) );
const char *getName() {
return name.c_str();
}
void setName( const char *p ) {
name = p;
}
virtual void startEdit() {
editMode = true;
}
virtual void stopEdit() {
editMode = false;
}
virtual void draw() {
};
const char *typeStr() {
return positionStr[static_cast<int>( type )];
}
void calcVelocity( float distance ) {
float secs = (float)time / 1000;
baseVelocity = distance / secs;
}
protected:
static const char* positionStr[POSITION_COUNT];
long startTime;
long time;
idCameraPosition::positionType type;
idStr name;
bool editMode;
idList<idVelocity*> velocities;
float baseVelocity;
};
class idFixedPosition : public idCameraPosition {
public:
void init() {
pos.Zero();
type = idCameraPosition::FIXED;
}
idFixedPosition() : idCameraPosition() {
init();
}
idFixedPosition( idVec3 p ) : idCameraPosition() {
init();
pos = p;
}
virtual void addPoint( const idVec3 &v ) {
pos = v;
}
virtual void addPoint( const float x, const float y, const float z ) {
pos.set( x, y, z );
}
~idFixedPosition() {
}
virtual const idVec3 *getPosition( long t ) {
return &pos;
}
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
virtual int numPoints() {
return 1;
}
virtual idVec3 *getPoint( int index ) {
if ( index != 0 ) {
assert( true );
}
;
return &pos;
}
virtual void draw( bool editMode ) {
glLabeledPoint( blue, pos, ( editMode ) ? 5 : 3, "Fixed point" );
}
protected:
idVec3 pos;
};
class idInterpolatedPosition : public idCameraPosition {
public:
void init() {
type = idCameraPosition::INTERPOLATED;
first = true;
startPos.Zero();
endPos.Zero();
}
idInterpolatedPosition() : idCameraPosition() {
init();
}
idInterpolatedPosition( idVec3 start, idVec3 end, long time ) : idCameraPosition( time ) {
init();
startPos = start;
endPos = end;
}
~idInterpolatedPosition() {
}
virtual const idVec3 *getPosition( long t );
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
virtual int numPoints() {
return 2;
}
virtual idVec3 *getPoint( int index ) {
assert( index >= 0 && index < 2 );
if ( index == 0 ) {
return &startPos;
}
return &endPos;
}
virtual void addPoint( const float x, const float y, const float z ) {
if ( first ) {
startPos.set( x, y, z );
first = false;
}
else {
endPos.set( x, y, z );
first = true;
}
}
virtual void addPoint( const idVec3 &v ) {
if ( first ) {
startPos = v;
first = false;
}
else {
endPos = v;
first = true;
}
}
virtual void draw( bool editMode ) {
glLabeledPoint( blue, startPos, ( editMode ) ? 5 : 3, "Start interpolated" );
glLabeledPoint( blue, endPos, ( editMode ) ? 5 : 3, "End interpolated" );
glBegin( GL_LINES );
glVertex3fv( startPos );
glVertex3fv( endPos );
glEnd();
}
virtual void start( long t ) {
idCameraPosition::start( t );
lastTime = startTime;
distSoFar = 0.0;
idVec3 temp = startPos;
temp -= endPos;
calcVelocity( temp.Length() );
}
protected:
bool first;
idVec3 startPos;
idVec3 endPos;
long lastTime;
float distSoFar;
};
class idSplinePosition : public idCameraPosition {
public:
void init() {
type = idCameraPosition::SPLINE;
}
idSplinePosition() : idCameraPosition() {
init();
}
idSplinePosition( long time ) : idCameraPosition( time ) {
init();
}
~idSplinePosition() {
}
virtual void start( long t ) {
idCameraPosition::start( t );
target.initPosition( t, time );
lastTime = startTime;
distSoFar = 0.0;
calcVelocity( target.totalDistance() );
}
//virtual const idVec3 *getPosition(long t) {
// return target.getPosition(t);
//}
virtual const idVec3 *getPosition( long t );
//virtual const idVec3 *getPosition(long t) const {
void addControlPoint( idVec3 &v ) {
target.addPoint( v );
}
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
virtual int numPoints() {
return target.numPoints();
}
virtual idVec3 *getPoint( int index ) {
return target.getPoint( index );
}
virtual void addPoint( const idVec3 &v ) {
target.addPoint( v );
}
virtual void addPoint( const float x, const float y, const float z ) {
target.addPoint( x, y, z );
}
virtual void draw( bool editMode ) {
target.draw( editMode );
}
virtual void updateSelection( const idVec3 &move ) {
idCameraPosition::updateSelection( move );
target.buildSpline();
}
protected:
idSplineList target;
long lastTime;
float distSoFar;
};
class idCameraFOV {
public:
idCameraFOV() {
time = 0;
length = 0;
fov = 90;
}
idCameraFOV( int v ) {
time = 0;
length = 0;
fov = v;
}
idCameraFOV( int s, int e, long t ) {
startFOV = s;
endFOV = e;
length = t;
}
~idCameraFOV(){
}
void setFOV( float f ) {
fov = f;
}
float getFOV( long t ) {
if ( length ) {
float percent = ( t - startTime ) / length;
if ( percent < 0.0 ) {
percent = 0.0;
}
else if ( percent > 1.0 ) {
percent = 1.0;
}
float temp = endFOV - startFOV;
temp *= percent;
fov = startFOV + temp;
if ( percent == 1.0 ) {
length = 0.0;
}
}
return fov;
}
void start( long t ) {
startTime = t;
}
void reset( float startfov, float endfov, int start, float len ) {
startFOV = startfov;
endFOV = endfov;
startTime = start;
length = len * 1000;
}
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
protected:
float fov;
float startFOV;
float endFOV;
int startTime;
int time;
float length;
};
class idCameraEvent {
public: // parameters
enum eventType {
EVENT_NA = 0x00,
EVENT_WAIT, //
EVENT_TARGETWAIT, //
EVENT_SPEED, //
EVENT_TARGET, // char(name)
EVENT_SNAPTARGET, //
EVENT_FOV, // int(time), int(targetfov)
EVENT_CMD, //
EVENT_TRIGGER, //
EVENT_STOP, //
EVENT_CAMERA, //
EVENT_FADEOUT, // int(time)
EVENT_FADEIN, // int(time)
EVENT_FEATHER, //
EVENT_COUNT
};
static const char* eventStr[EVENT_COUNT];
idCameraEvent() {
paramStr = "";
type = EVENT_NA;
time = 0;
}
idCameraEvent( eventType t, const char *param, long n ) {
type = t;
paramStr = param;
time = n;
}
~idCameraEvent() {
};
eventType getType() {
return type;
}
const char *typeStr() {
return eventStr[static_cast<int>( type )];
}
const char *getParam() {
return paramStr.c_str();
}
long getTime() {
return time;
}
void setTime( long n ) {
time = n;
}
void parse( const char *( *text ) );
void write( fileHandle_t file, const char *name );
void setTriggered( bool b ) {
triggered = b;
}
bool getTriggered() {
return triggered;
}
protected:
eventType type;
idStr paramStr;
long time;
bool triggered;
};
class idCameraDef {
public:
void clear() {
currentCameraPosition = 0;
cameraRunning = false;
lastDirection.Zero();
baseTime = 30;
activeTarget = 0;
name = "camera01";
fov.setFOV( 90 );
int i;
for ( i = 0; i < targetPositions.Num(); i++ ) {
delete targetPositions[i];
}
for ( i = 0; i < events.Num(); i++ ) {
delete events[i];
}
delete cameraPosition;
cameraPosition = NULL;
events.Clear();
targetPositions.Clear();
}
idCameraPosition *startNewCamera( idCameraPosition::positionType type ) {
clear();
if ( type == idCameraPosition::SPLINE ) {
cameraPosition = new idSplinePosition();
}
else if ( type == idCameraPosition::INTERPOLATED ) {
cameraPosition = new idInterpolatedPosition();
}
else {
cameraPosition = new idFixedPosition();
}
return cameraPosition;
}
idCameraDef() {
cameraPosition = NULL;
clear();
}
~idCameraDef() {
clear();
}
void addEvent( idCameraEvent::eventType t, const char *param, long time );
void addEvent( idCameraEvent *event );
void removeEvent( int index );
static int sortEvents( const void *p1, const void *p2 );
int numEvents() {
return events.Num();
}
idCameraEvent *getEvent( int index ) {
assert( index >= 0 && index < events.Num() );
return events[index];
}
void parse( const char *( *text ) );
bool load( const char *filename );
void save( const char *filename );
void buildCamera();
//idSplineList *getcameraPosition() {
// return &cameraPosition;
//}
static idCameraPosition *newFromType( idCameraPosition::positionType t ) {
switch ( t ) {
case idCameraPosition::FIXED: return new idFixedPosition();
case idCameraPosition::INTERPOLATED: return new idInterpolatedPosition();
case idCameraPosition::SPLINE: return new idSplinePosition();
default:
break;
};
return NULL;
}
void addTarget( const char *name, idCameraPosition::positionType type );
idCameraPosition *getActiveTarget() {
if ( targetPositions.Num() == 0 ) {
addTarget( NULL, idCameraPosition::FIXED );
}
return targetPositions[activeTarget];
}
idCameraPosition *getActiveTarget( int index ) {
if ( targetPositions.Num() == 0 ) {
addTarget( NULL, idCameraPosition::FIXED );
return targetPositions[0];
}
return targetPositions[index];
}
int numTargets() {
return targetPositions.Num();
}
void setActiveTargetByName( const char *name ) {
for ( int i = 0; i < targetPositions.Num(); i++ ) {
if ( Q_stricmp( name, targetPositions[i]->getName() ) == 0 ) {
setActiveTarget( i );
return;
}
}
}
void setActiveTarget( int index ) {
assert( index >= 0 && index < targetPositions.Num() );
activeTarget = index;
}
void setRunning( bool b ) {
cameraRunning = b;
}
void setBaseTime( float f ) {
baseTime = f;
}
float getBaseTime() {
return baseTime;
}
float getTotalTime() {
return totalTime;
}
void startCamera( long t );
void stopCamera() {
cameraRunning = true;
}
void getActiveSegmentInfo( int segment, idVec3 &origin, idVec3 &direction, float *fv );
bool getCameraInfo( long time, idVec3 &origin, idVec3 &direction, float *fv );
bool getCameraInfo( long time, float *origin, float *direction, float *fv ) {
idVec3 org, dir;
org[0] = origin[0];
org[1] = origin[1];
org[2] = origin[2];
dir[0] = direction[0];
dir[1] = direction[1];
dir[2] = direction[2];
bool b = getCameraInfo( time, org, dir, fv );
origin[0] = org[0];
origin[1] = org[1];
origin[2] = org[2];
direction[0] = dir[0];
direction[1] = dir[1];
direction[2] = dir[2];
return b;
}
void draw( bool editMode ) {
// gcc doesn't allow casting away from bools
// why? I've no idea...
if ( cameraPosition ) {
cameraPosition->draw( (bool)( ( editMode || cameraRunning ) && cameraEdit ) );
int count = targetPositions.Num();
for ( int i = 0; i < count; i++ ) {
targetPositions[i]->draw( (bool)( ( editMode || cameraRunning ) && i == activeTarget && !cameraEdit ) );
}
}
}
/*
int numSegments() {
if (cameraEdit) {
return cameraPosition.numSegments();
}
return getTargetSpline()->numSegments();
}
int getActiveSegment() {
if (cameraEdit) {
return cameraPosition.getActiveSegment();
}
return getTargetSpline()->getActiveSegment();
}
void setActiveSegment(int i) {
if (cameraEdit) {
cameraPosition.setActiveSegment(i);
} else {
getTargetSpline()->setActiveSegment(i);
}
}
*/
int numPoints() {
if ( cameraEdit ) {
return cameraPosition->numPoints();
}
return getActiveTarget()->numPoints();
}
const idVec3 *getPoint( int index ) {
if ( cameraEdit ) {
return cameraPosition->getPoint( index );
}
return getActiveTarget()->getPoint( index );
}
void stopEdit() {
editMode = false;
if ( cameraEdit ) {
cameraPosition->stopEdit();
}
else {
getActiveTarget()->stopEdit();
}
}
void startEdit( bool camera ) {
cameraEdit = camera;
if ( camera ) {
cameraPosition->startEdit();
for ( int i = 0; i < targetPositions.Num(); i++ ) {
targetPositions[i]->stopEdit();
}
}
else {
getActiveTarget()->startEdit();
cameraPosition->stopEdit();
}
editMode = true;
}
bool waitEvent( int index );
const char *getName() {
return name.c_str();
}
void setName( const char *p ) {
name = p;
}
idCameraPosition *getPositionObj() {
if ( cameraPosition == NULL ) {
cameraPosition = new idFixedPosition();
}
return cameraPosition;
}
protected:
idStr name;
int currentCameraPosition;
idVec3 lastDirection;
bool cameraRunning;
idCameraPosition *cameraPosition;
idList<idCameraPosition*> targetPositions;
idList<idCameraEvent*> events;
idCameraFOV fov;
int activeTarget;
float totalTime;
float baseTime;
long startTime;
bool cameraEdit;
bool editMode;
};
extern bool g_splineMode;
extern idCameraDef *g_splineList;
#endif