//----------------------------------------------------------------------------- // // $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 ); }