sin-sdk/bspline.cpp
1999-11-02 00:00:00 +00:00

859 lines
16 KiB
C++

//-----------------------------------------------------------------------------
//
// $Logfile:: /Quake 2 Engine/Sin/code/game/bspline.cpp $
// $Revision:: 14 $
// $Author:: Markd $
// $Date:: 8/07/99 1:53p $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/bspline.cpp $
//
// 14 8/07/99 1:53p Markd
// Fixed a bunch of more code for version 1.06
//
// 13 5/19/99 11:30a Markd
// Added new camera support
//
// 12 10/19/98 9:51p Jimdose
// fixed savegame bugs with bspline
//
// 11 10/10/98 9:12p Markd
// Fixed angle errors with bsplines
//
// 10 8/15/98 2:39p Markd
// fixed up some bspline stuff
//
// 9 7/11/98 6:31p Markd
// removed valid orientation, simplified code
//
// 8 7/10/98 1:11p Markd
// Added additional two paramter append control point
//
// 7 7/08/98 12:41p Markd
// Added speed and quaternion support
//
// 6 7/02/98 9:48p Markd
// added orientation
//
// 5 5/26/98 7:55p Jimdose
// Added Drawcurve with offset
//
// 4 5/07/98 10:40p Jimdose
// Added spline type for selecting between looping and non-looping curves
//
// 3 5/05/98 2:37p Jimdose
// Added code to allow spline loop and clamping the spline start and end
//
// 2 5/03/98 4:42p Jimdose
// Added file to Sin
//
// DESCRIPTION:
// Uniform non-rational bspline class.
//
#include "g_local.h"
#include "BSpline.h"
void BSpline::Set
(
Vector *control_points_,
int num_control_points_,
splinetype_t type
)
{
int i;
SetType( type );
has_orientation = false;
if ( control_points )
{
delete [] control_points;
control_points = NULL;
}
num_control_points = num_control_points_;
if ( num_control_points )
{
control_points = new BSplineControlPoint[ num_control_points ];
assert( control_points );
for( i = 0; i < num_control_points; i++ )
{
control_points[ i ].Set( control_points_[ i ] );
}
}
}
void BSpline::Set
(
Vector *control_points_,
Vector *control_orients_,
float *control_speeds_,
int num_control_points_,
splinetype_t type
)
{
int i;
SetType( type );
has_orientation = true;
if ( control_points )
{
delete [] control_points;
control_points = NULL;
}
num_control_points = num_control_points_;
if ( num_control_points )
{
control_points = new BSplineControlPoint[ num_control_points ];
assert( control_points );
for( i = 0; i < num_control_points; i++ )
{
control_points[ i ].Set( control_points_[ i ], control_orients_[ i ], control_speeds_[ i ] );
}
}
}
void BSpline::Clear
(
void
)
{
if( control_points )
{
delete [] control_points;
control_points = NULL;
}
num_control_points = 0;
has_orientation = false;
}
inline float BSpline::EvalNormal
(
float u,
Vector& pos,
Vector& orient
)
{
int segment_id;
float B[ 4 ];
float tmp;
float u_2;
float u_3;
Vector ang;
float roll;
float speed;
segment_id = ( int )u;
if ( segment_id < 0 )
{
segment_id = 0;
}
if ( segment_id > num_control_points - 4 )
{
segment_id = num_control_points - 4;
}
u -= ( float )segment_id;
u_2 = u * u;
u_3 = u * u_2;
tmp = 1 - u;
B[ 0 ] = ( tmp * tmp * tmp ) * ( 1.0f / 6.0f );
B[ 1 ] = ( 3.0f * u_3 - 6.0f * u_2 + 4.0f ) * ( 1.0f / 6.0f );
B[ 2 ] = ( -3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1 ) * ( 1.0f / 6.0f );
B[ 3 ] = u_3 * ( 1.0f / 6.0f );
pos =
*control_points[ 0 + segment_id ].GetPosition() * B[ 0 ] +
*control_points[ 1 + segment_id ].GetPosition() * B[ 1 ] +
*control_points[ 2 + segment_id ].GetPosition() * B[ 2 ] +
*control_points[ 3 + segment_id ].GetPosition() * B[ 3 ];
ang =
*control_points[ 0 + segment_id ].GetOrientation() * B[ 0 ] +
*control_points[ 1 + segment_id ].GetOrientation() * B[ 1 ] +
*control_points[ 2 + segment_id ].GetOrientation() * B[ 2 ] +
*control_points[ 3 + segment_id ].GetOrientation() * B[ 3 ];
roll =
*control_points[ 0 + segment_id ].GetRoll() * B[ 0 ] +
*control_points[ 1 + segment_id ].GetRoll() * B[ 1 ] +
*control_points[ 2 + segment_id ].GetRoll() * B[ 2 ] +
*control_points[ 3 + segment_id ].GetRoll() * B[ 3 ];
speed =
*control_points[ 0 + segment_id ].GetSpeed() * B[ 0 ] +
*control_points[ 1 + segment_id ].GetSpeed() * B[ 1 ] +
*control_points[ 2 + segment_id ].GetSpeed() * B[ 2 ] +
*control_points[ 3 + segment_id ].GetSpeed() * B[ 3 ];
orient = ang.toAngles();
orient[ ROLL ] = roll;
return speed;
}
inline float BSpline::EvalLoop
(
float t,
Vector& pos,
Vector& orient
)
{
Vector retval;
Vector ang;
float speed;
float roll;
int segment_id;
int next_id;
float B[ 4 ];
float tmp;
float u;
float u_2;
float u_3;
int i;
int j;
segment_id = ( int )floor( t );
u = t - floor( t );
segment_id %= num_control_points;
if ( segment_id < 0 )
{
segment_id += num_control_points;
}
u_2 = u * u;
u_3 = u * u_2;
tmp = 1 - u;
B[ 0 ] = ( tmp * tmp * tmp ) * ( 1.0f / 6.0f );
B[ 1 ] = ( 3.0f * u_3 - 6.0f * u_2 + 4.0f ) * ( 1.0f / 6.0f );
B[ 2 ] = ( -3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1 ) * ( 1.0f / 6.0f );
B[ 3 ] = u_3 * ( 1.0f / 6.0f );
speed = 0;
roll = 0;
for( i = 0, j = segment_id; i < 4; i++, j++ )
{
if ( j >= num_control_points )
{
j -= ( num_control_points - loop_control_point );
}
retval += *control_points[ j ].GetPosition() * B[ i ];
ang += *control_points[ j ].GetOrientation() * B[ i ];
speed += *control_points[ j ].GetSpeed() * B[ i ];
roll += *control_points[ j ].GetRoll() * B[ i ];
}
pos = retval;
next_id = segment_id + 1;
if ( next_id >= num_control_points )
{
next_id -= ( num_control_points - loop_control_point );
}
orient = ang.toAngles();
orient[ ROLL ] = roll;
return speed;
}
inline float BSpline::EvalClamp
(
float t,
Vector& pos,
Vector& orient
)
{
Vector retval;
Vector ang;
int segment_id;
int next_id;
float B[ 4 ];
float tmp;
float u;
float u_2;
float u_3;
int i;
int j;
float speed;
float roll;
segment_id = ( int )floor( t );
u = t - floor( t );
u_2 = u * u;
u_3 = u * u_2;
tmp = 1 - u;
B[ 0 ] = ( tmp * tmp * tmp ) * ( 1.0f / 6.0f );
B[ 1 ] = ( 3.0f * u_3 - 6.0f * u_2 + 4.0f ) * ( 1.0f / 6.0f );
B[ 2 ] = ( -3.0f * u_3 + 3.0f * u_2 + 3.0f * u + 1 ) * ( 1.0f / 6.0f );
B[ 3 ] = u_3 * ( 1.0f / 6.0f );
speed = 0;
roll = 0;
for( i = 0; i < 4; i++, segment_id++ )
{
j = segment_id;
if ( j < 0 )
{
j = 0;
}
else if ( j >= num_control_points )
{
j = num_control_points - 1;
}
retval += *control_points[ j ].GetPosition() * B[ i ];
ang += *control_points[ j ].GetOrientation() * B[ i ];
speed += *control_points[ j ].GetSpeed() * B[ i ];
roll += *control_points[ j ].GetRoll() * B[ i ];
}
pos = retval;
next_id = segment_id + 1;
if ( segment_id < 0 )
{
segment_id = 0;
}
if ( segment_id >= num_control_points )
{
segment_id = num_control_points - 1;
}
if ( next_id < 0 )
{
next_id = 0;
}
if ( next_id >= num_control_points )
{
next_id = num_control_points - 1;
}
orient = ang.toAngles();
orient[ ROLL ] = roll;
return speed;
}
Vector BSpline::Eval
(
float u
)
{
Vector pos;
Vector orient;
switch( curvetype )
{
default:
case SPLINE_NORMAL :
EvalNormal( u, pos, orient );
break;
case SPLINE_CLAMP:
EvalClamp( u, pos, orient );
break;
case SPLINE_LOOP:
EvalLoop( u, pos, orient );
break;
}
return pos;
}
float BSpline::Eval
(
float u,
Vector &pos,
Vector &orient
)
{
switch( curvetype )
{
default:
case SPLINE_NORMAL :
return EvalNormal( u, pos, orient );
break;
case SPLINE_CLAMP:
return EvalClamp( u, pos, orient );
break;
case SPLINE_LOOP:
return EvalLoop( u, pos, orient );
break;
}
}
void BSpline::DrawControlSegments
(
void
)
{
int i;
G_BeginLine();
for( i = 0; i < num_control_points; i++ )
{
G_Vertex( *control_points[ i ].GetPosition() );
}
G_EndLine();
}
void BSpline::DrawCurve
(
int num_subdivisions
)
{
float u;
float du;
du = 1.0f / ( float )num_subdivisions;
G_BeginLine();
for( u = 0.0f; u <= ( float )num_control_points; u += du )
{
G_Vertex( ( Vector )Eval( u ) );
}
G_EndLine();
}
void BSpline::DrawCurve
(
Vector offset,
int num_subdivisions
)
{
float u;
float du;
du = 1.0f / ( float )num_subdivisions;
G_BeginLine();
for( u = 0.0f; u <= ( float )num_control_points; u += du )
{
G_Vertex( offset + ( Vector )Eval( u ) );
}
G_EndLine();
}
void BSpline::AppendControlPoint
(
const Vector& new_control_point
)
{
BSplineControlPoint *old_control_points;
int i;
old_control_points = control_points;
num_control_points++;
control_points = new BSplineControlPoint[num_control_points];
assert( control_points );
if ( old_control_points )
{
for( i = 0; i < num_control_points - 1; i++ )
{
control_points[ i ] = old_control_points[ i ];
}
delete [] old_control_points;
}
control_points[ num_control_points - 1 ].Set( new_control_point );
}
void BSpline::AppendControlPoint
(
const Vector& new_control_point,
const float& speed
)
{
BSplineControlPoint *old_control_points;
int i;
old_control_points = control_points;
num_control_points++;
control_points = new BSplineControlPoint[num_control_points];
assert( control_points );
if ( old_control_points )
{
for( i = 0; i < num_control_points - 1; i++ )
{
control_points[ i ] = old_control_points[ i ];
}
delete [] old_control_points;
}
control_points[ num_control_points - 1 ].Set( new_control_point, speed );
}
void BSpline::AppendControlPoint
(
const Vector& new_control_point,
const Vector& new_control_orient,
const float& new_control_speed
)
{
BSplineControlPoint *old_control_points;
int i;
has_orientation = true;
old_control_points = control_points;
num_control_points++;
control_points = new BSplineControlPoint[num_control_points];
assert( control_points );
if ( old_control_points )
{
for( i = 0; i < num_control_points - 1; i++ )
{
control_points[ i ] = old_control_points[ i ];
}
delete [] old_control_points;
}
control_points[ num_control_points - 1 ].Set( new_control_point, new_control_orient, new_control_speed );
}
void BSpline::SetLoopPoint
(
const Vector& pos
)
{
int i;
for( i = 0; i < num_control_points; i++ )
{
if ( pos == *control_points[ i ].GetPosition() )
{
loop_control_point = i;
break;
}
}
}
int BSpline::PickControlPoint
(
const Vector& window_point,
float pick_size
)
{
int i;
float closest_dist_2;
int closest_index;
float dist_2;
Vector delta;
closest_index = -1;
closest_dist_2 = 1000000.0f;
for( i = 0; i < num_control_points; i++ )
{
delta = window_point - *control_points[ i ].GetPosition();
dist_2 = delta * delta;
if ( dist_2 < closest_dist_2 )
{
closest_dist_2 = dist_2;
closest_index = i;
}
}
if ( pick_size * pick_size >= closest_dist_2 )
{
return closest_index;
}
else
{
return -1;
}
}
CLASS_DECLARATION( Entity, SplinePath, "info_splinepath" );
Event EV_SplinePath_Create( "SplinePath_create" );
Event EV_SplinePath_Loop( "loop", EV_CONSOLE );
Event EV_SplinePath_Speed( "speed" );
ResponseDef SplinePath::Responses[] =
{
{ &EV_SplinePath_Create, ( Response )SplinePath::CreatePath },
{ &EV_SplinePath_Loop, ( Response )SplinePath::SetLoop },
{ &EV_SplinePath_Speed, ( Response )SplinePath::SetSpeed },
{ NULL, NULL }
};
SplinePath::SplinePath()
{
owner = this;
next = NULL;
loop = NULL;
doWatch = false;
watchEnt = "";
loop_name = G_GetStringArg( "loop" );
angles = G_GetVectorArg( "angles" );
speed = G_GetFloatArg( "speed", 1 );
setMoveType( MOVETYPE_NONE );
setSolidType( SOLID_NOT );
hideModel();
if ( !LoadingSavegame )
{
PostEvent( EV_SplinePath_Create, 0 );
}
}
SplinePath::~SplinePath()
{
// disconnect from the chain
if ( owner != this )
{
owner->SetNext( next );
owner = this;
}
else if ( next )
{
next->SetPrev( NULL );
next = NULL;
}
assert( owner == this );
assert( next == NULL );
}
void SplinePath::CreatePath
(
Event *ev
)
{
const char *target;
int num;
// Make the path from the targetlist.
target = Target();
if ( target[ 0 ] )
{
if ( num = G_FindTarget( 0, target ) )
{
next = ( SplinePath * )G_GetEntity( num );
next->owner = this;
}
else
{
gi.error( "SplinePath::CreatePath: target %s not found\n", target );
}
}
if ( loop_name.length() )
{
if ( num = G_FindTarget( 0, loop_name.c_str() ) )
{
loop = ( SplinePath * )G_GetEntity( num );
}
}
}
SplinePath *SplinePath::GetNext
(
void
)
{
return next;
}
SplinePath *SplinePath::GetLoop
(
void
)
{
return loop;
}
void SplinePath::SetLoop
(
Event *ev
)
{
loop_name = ev->GetString( 1 );
}
void SplinePath::SetSpeed
(
Event *ev
)
{
speed = ev->GetFloat( 1 );
}
SplinePath *SplinePath::GetPrev
(
void
)
{
if ( owner == this )
{
return NULL;
}
return owner;
}
void SplinePath::SetNext
(
SplinePath *node
)
{
if ( next )
{
// remove ourselves from the chain
next->owner = next;
}
next = node;
if ( next )
{
// disconnect next from it's previous node
if ( next->owner != next )
{
next->owner->next = NULL;
}
next->owner = this;
}
}
void SplinePath::SetPrev
(
SplinePath *node
)
{
if ( owner != this )
{
owner->next = NULL;
}
if ( node && ( node != this ) )
{
// safely remove the node from its chain
if ( node->next )
{
node->next->owner = node->next;
}
node->next = this;
owner = node;
}
else
{
owner = this;
}
}
void SplinePath::SetWatch
(
const char *name
)
{
doWatch = true;
watchEnt = name;
}
void SplinePath::NoWatch
(
void
)
{
doWatch = true;
watchEnt = "";
}
Entity *SplinePath::GetWatch
(
void
)
{
const char *name;
int t;
name = watchEnt.c_str();
if ( name[ 0 ] == '$' )
{
t = G_FindTarget( 0, &name[ 1 ] );
if ( !t )
{
warning( "GetWatch", "Entity with targetname of '%s' not found", &name[ 1 ] );
return NULL;
}
}
else
{
if ( name[ 0 ] != '*' )
{
warning( "GetWatch", "Expecting a '*'-prefixed entity number but found '%s'.", name );
return NULL;
}
if ( !IsNumeric( &name[ 1 ] ) )
{
warning( "GetWatch", "Expecting a numeric value but found '%s'.", &name[ 1 ] );
return NULL;
}
else
{
t = atoi( &name[ 1 ] );
}
}
if ( ( t < 0 ) || ( t > game.maxentities ) )
{
warning( "GetWatch", "%d out of valid range for entity.", t );
return NULL;
}
return G_GetEntity( t );
}