// 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. // // 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" ); ResponseDef SplinePath::Responses[] = { { &EV_SplinePath_Create, ( Response )SplinePath::CreatePath }, { NULL, NULL } }; SplinePath::SplinePath() { owner = this; next = NULL; loop = NULL; 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 ); } } 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; }