I don't think the normal from TR_GetPlaneNormal is the sphere normal you want. You are going to have to implement the intersection code yourself.
Do your checks in a think function rather than a timer so you can get 1/66s resolution rather than 1/10s if you want it to be more accurate.
I've written an outline of what it should look like. Note there are some gaps which will depend on the specifics of your code and it's fully not tested.
EDIT: I think IntersectSegmentSphere might be incorrect but I have to go to class in 5 minutes. I'll check in a bit
EDIT2: should be ok now. still untested
(LOL I got quite carried away. add me on steam or something if you want me to explain this)
Code:
#define BOUNCE_LIMIT 5
new Handle:lastPosArray; // = CreateArray( ... )
new Handle:spheres; // = CreateArray( 4 )
Float:IntersectSegmentSphere( const Float:start[ 3 ], const Float:end[ 3 ], const Float:centre[ 3 ], const Float:radius ) {
decl Float:d[ 3 ]; // holds the segment between start and end
decl Float:m[ 3 ]; // holds the segment between start and centre
SubtractVectors( end, start, d );
SubtractVectors( centre, start, m );
new Float:b = GetVectorDotProduct( m, d );
new const Float:c = GetVectorDotProduct( m, m ) - radius * radius;
// if c > 0.0, |m| > r ie we are starting outside the sphere
// if b <= 0.0, d . m < 0.0 ie we are not looking anywhere near the sphere
if( c > 0.0 && b <= 0.0 ) {
return -1.0;
}
new const Float:mag = GetVectorLength( d );
b /= mag;
new const Float:disc = b * b - c;
// did we miss the sphere?
if( disc < 0.0 ) {
return -1.0;
}
return b - SquareRoot( disc );
}
// result = |v|( v' - 2(v'.n)n ), v' = v / |v|
ReflectVector( const Float:vec[ 3 ], const Float:normal[ 3 ], Float:result[ 3 ] ) {
decl Float:vHat[ 3 ];
NormalizeVector( vec, vHat );
new const Float:dot = GetVectorDotProduct( normal, vHat );
new const Float:mag = GetVectorLength( vec );
ScaleVector( vHat, 2 * dot );
SubtractVectors( vec, vHat, result );
ScaleVector( result, mag );
}
SphereNormalAt( const Float:centre[ 3 ], const Float:radius, const Float:pos[ 3 ], Float:result[ 3 ] ) {
SubtractVectors( pos, centre, result );
ScaleVector( result, 1.0 / radius );
}
LerpVectors( const Float:vec1[ 3 ], const Float:vec2[ 3 ], const Float:t, Float:result[ 3 ] ) {
decl Float:d[ 3 ];
SubtractVectors( vec2, vec1, d );
ScaleVector( d, t );
AddVectors( vec1, d, result );
}
CopyVector( const Float:vec[ 3 ], Float:result[ 3 ] ) {
result[ 0 ] = vec[ 0 ];
result[ 1 ] = vec[ 1 ];
result[ 2 ] = vec[ 2 ];
}
AddDelta( const Float:vec1[ 3 ], const Float:d[ 3 ], Float:result[ 3 ] ) {
decl Float:delta[ 3 ]
NormalizeVector( d, delta );
ScaleVector( delta, 0.0001 );
AddVectors( vec1, delta, result );
}
OnThink( rocket ) {
decl Float:lastPos[ 3 ];
// get lastPos from lastPosArray somehow
decl Float:pos[ 3 ];
decl Float:velocity[ 3 ];
GetEntPropVector( rocket, Prop_Send, "m_vecOrigin", pos );
GetEntPropVector( rocket, Prop_Send, "m_vecAbsVelocity", velocity );
new bounces = 0;
while( bounces < BOUNCE_LIMIT ) {
new closestSphere = -1;
decl Float:closestT;
// find the first sphere we intersect with
new numSpheres = GetArraySize( spheres );
for( new i = 0; i < numSpheres; i++ ) {
decl Float:centre[ 3 ];
GetArrayArray( spheres, i, centre, 3 );
new Float:radius = GetArrayCell( spheres, i, 4 );
new const Float:t = IntersectSegmentSphere( lastPos, pos, centre, radius );
if( closestSphere == -1 || t < closestT ) {
closestSphere = i;
closestT = t;
}
}
// we didn't hit anything
if( closestSphere == -1 || closestT > 1.0 ) {
break;
}
decl Float:centre[ 3 ];
GetArrayArray( spheres, closestSphere, centre, 3 );
new Float:radius = GetArrayCell( spheres, closestSphere, 4 );
decl Float:intersection[ 3 ];
LerpVectors( lastPos, pos, closestT, intersection );
decl Float:normal[ 3 ];
SphereNormalAt( centre, radius, intersection, normal );
decl Float:newVelocity[ 3 ];
ReflectVector( velocity, normal, newVelocity );
AddDelta( intersection, newVelocity, intersection );
decl Float:penetration[ 3 ];
CopyVector( velocity, penetration );
ScaleVector( penetration, 1.0 - closestT );
decl Float:reflection[ 3 ];
ReflectVector( penetration, normal, reflection );
decl Float:newPos[ 3 ];
AddVectors( intersection, reflection, newPos );
CopyVector( newVelocity, velocity );
CopyVector( intersection, lastPos );
CopyVector( newPos, pos );
bounces++;
}
if( bounces == BOUNCE_LIMIT ) {
// the rocket is stuck in some terrible situation.
// possible courses of action include:
// - put the rocket out of its misery and make it explode
// - ignore it (but then we are likely to encounter something
// similar next frame)
}
// update lastPosArray with current pos
decl Float:angles[ 3 ];
GetVectorAngles( velocity, angles );
TeleportEntity( rocket, pos, angles, velocity );
}