mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-11-25 11:19:59 +01:00
873 lines
17 KiB
C
873 lines
17 KiB
C
/*
|
|
xash3d_mathlib.c - internal mathlib
|
|
Copyright (C) 2010 Uncle Mike
|
|
|
|
This program 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 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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.
|
|
*/
|
|
#include "port.h"
|
|
#include "xash3d_types.h"
|
|
#include "const.h"
|
|
#include "com_model.h"
|
|
#include "xash3d_mathlib.h"
|
|
#include "eiface.h"
|
|
|
|
#define NUM_HULL_ROUNDS ARRAYSIZE( hull_table )
|
|
#define HULL_PRECISION 4
|
|
|
|
vec3_t vec3_origin = { 0, 0, 0 };
|
|
|
|
static word hull_table[] = { 2, 4, 6, 8, 12, 16, 18, 24, 28, 32, 36, 40, 48, 54, 56, 60, 64, 72, 80, 112, 120, 128, 140, 176 };
|
|
|
|
int boxpnt[6][4] =
|
|
{
|
|
{ 0, 4, 6, 2 }, // +X
|
|
{ 0, 1, 5, 4 }, // +Y
|
|
{ 0, 2, 3, 1 }, // +Z
|
|
{ 7, 5, 1, 3 }, // -X
|
|
{ 7, 3, 2, 6 }, // -Y
|
|
{ 7, 6, 4, 5 }, // -Z
|
|
};
|
|
|
|
// pre-quantized table normals from Quake1
|
|
const float m_bytenormals[NUMVERTEXNORMALS][3] =
|
|
{
|
|
#include "anorms.h"
|
|
};
|
|
|
|
/*
|
|
=================
|
|
anglemod
|
|
=================
|
|
*/
|
|
float anglemod( float a )
|
|
{
|
|
a = (360.0f / 65536) * ((int)(a*(65536/360.0f)) & 65535);
|
|
return a;
|
|
}
|
|
|
|
word FloatToHalf( float v )
|
|
{
|
|
unsigned int i = FloatAsUint( v );
|
|
unsigned int e = (i >> 23) & 0x00ff;
|
|
unsigned int m = i & 0x007fffff;
|
|
unsigned short h;
|
|
|
|
if( e <= 127 - 15 )
|
|
h = ((m | 0x00800000) >> (127 - 14 - e)) >> 13;
|
|
else h = (i >> 13) & 0x3fff;
|
|
|
|
h |= (i >> 16) & 0xc000;
|
|
|
|
return h;
|
|
}
|
|
|
|
float HalfToFloat( word h )
|
|
{
|
|
unsigned int f = (h << 16) & 0x80000000;
|
|
unsigned int em = h & 0x7fff;
|
|
|
|
if( em > 0x03ff )
|
|
{
|
|
f |= (em << 13) + ((127 - 15) << 23);
|
|
}
|
|
else
|
|
{
|
|
unsigned int m = em & 0x03ff;
|
|
|
|
if( m != 0 )
|
|
{
|
|
unsigned int e = (em >> 10) & 0x1f;
|
|
|
|
while(( m & 0x0400 ) == 0 )
|
|
{
|
|
m <<= 1;
|
|
e--;
|
|
}
|
|
|
|
m &= 0x3ff;
|
|
f |= ((e + (127 - 14)) << 23) | (m << 13);
|
|
}
|
|
}
|
|
|
|
return UintAsFloat( f );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
RoundUpHullSize
|
|
|
|
round the hullsize to nearest 'right' value
|
|
=================
|
|
*/
|
|
void RoundUpHullSize( vec3_t size )
|
|
{
|
|
int i, j;
|
|
|
|
for( i = 0; i < 3; i++)
|
|
{
|
|
qboolean negative = false;
|
|
float result, value;
|
|
|
|
value = size[i];
|
|
if( value < 0.0f ) negative = true;
|
|
value = Q_ceil( fabs( value ));
|
|
result = Q_ceil( size[i] );
|
|
|
|
// lookup hull table to find nearest supposed value
|
|
for( j = 0; j < NUM_HULL_ROUNDS; j++ )
|
|
{
|
|
if( value > hull_table[j] )
|
|
continue; // ceil only
|
|
|
|
if( negative )
|
|
{
|
|
result = ( value - hull_table[j] );
|
|
if( result <= HULL_PRECISION )
|
|
{
|
|
result = -hull_table[j];
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = ( value - hull_table[j] );
|
|
if( result <= HULL_PRECISION )
|
|
{
|
|
result = hull_table[j];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
size[i] = result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SignbitsForPlane
|
|
|
|
fast box on planeside test
|
|
=================
|
|
*/
|
|
int SignbitsForPlane( const vec3_t normal )
|
|
{
|
|
int bits, i;
|
|
|
|
for( bits = i = 0; i < 3; i++ )
|
|
if( normal[i] < 0.0f ) bits |= 1<<i;
|
|
return bits;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
PlaneTypeForNormal
|
|
=================
|
|
*/
|
|
int PlaneTypeForNormal( const vec3_t normal )
|
|
{
|
|
if( normal[0] == 1.0f )
|
|
return PLANE_X;
|
|
if( normal[1] == 1.0f )
|
|
return PLANE_Y;
|
|
if( normal[2] == 1.0f )
|
|
return PLANE_Z;
|
|
return PLANE_NONAXIAL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
NearestPOW
|
|
=================
|
|
*/
|
|
int NearestPOW( int value, qboolean roundDown )
|
|
{
|
|
int n = 1;
|
|
|
|
if( value <= 0 ) return 1;
|
|
while( n < value ) n <<= 1;
|
|
|
|
if( roundDown )
|
|
{
|
|
if( n > value ) n >>= 1;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
rsqrt
|
|
=================
|
|
*/
|
|
float rsqrt( float number )
|
|
{
|
|
int i;
|
|
float x, y;
|
|
|
|
if( number == 0.0f )
|
|
return 0.0f;
|
|
|
|
x = number * 0.5f;
|
|
i = FloatAsInt( number ); // evil floating point bit level hacking
|
|
i = 0x5f3759df - (i >> 1); // what the fuck?
|
|
y = IntAsFloat( i );
|
|
y = y * (1.5f - (x * y * y)); // first iteration
|
|
|
|
return y;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
VectorCompareEpsilon
|
|
|
|
==============
|
|
*/
|
|
qboolean VectorCompareEpsilon( const vec3_t vec1, const vec3_t vec2, vec_t epsilon )
|
|
{
|
|
vec_t ax, ay, az;
|
|
|
|
ax = fabs( vec1[0] - vec2[0] );
|
|
ay = fabs( vec1[1] - vec2[1] );
|
|
az = fabs( vec1[2] - vec2[2] );
|
|
|
|
if(( ax <= epsilon ) && ( ay <= epsilon ) && ( az <= epsilon ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
float VectorNormalizeLength2( const vec3_t v, vec3_t out )
|
|
{
|
|
float length, ilength;
|
|
|
|
length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
length = sqrt( length );
|
|
|
|
if( length )
|
|
{
|
|
ilength = 1.0f / length;
|
|
out[0] = v[0] * ilength;
|
|
out[1] = v[1] * ilength;
|
|
out[2] = v[2] * ilength;
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up )
|
|
{
|
|
float d;
|
|
|
|
right[0] = forward[2];
|
|
right[1] = -forward[0];
|
|
right[2] = forward[1];
|
|
|
|
d = DotProduct( forward, right );
|
|
VectorMA( right, -d, forward, right );
|
|
VectorNormalize( right );
|
|
CrossProduct( right, forward, up );
|
|
VectorNormalize( up );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
AngleVectors
|
|
|
|
=================
|
|
*/
|
|
void GAME_EXPORT AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up )
|
|
{
|
|
float sr, sp, sy, cr, cp, cy;
|
|
|
|
SinCos( DEG2RAD( angles[YAW] ), &sy, &cy );
|
|
SinCos( DEG2RAD( angles[PITCH] ), &sp, &cp );
|
|
SinCos( DEG2RAD( angles[ROLL] ), &sr, &cr );
|
|
|
|
if( forward )
|
|
{
|
|
forward[0] = cp * cy;
|
|
forward[1] = cp * sy;
|
|
forward[2] = -sp;
|
|
}
|
|
|
|
if( right )
|
|
{
|
|
right[0] = (-1.0f * sr * sp * cy + -1.0f * cr * -sy );
|
|
right[1] = (-1.0f * sr * sp * sy + -1.0f * cr * cy );
|
|
right[2] = (-1.0f * sr * cp);
|
|
}
|
|
|
|
if( up )
|
|
{
|
|
up[0] = (cr * sp * cy + -sr * -sy );
|
|
up[1] = (cr * sp * sy + -sr * cy );
|
|
up[2] = (cr * cp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
VectorAngles
|
|
|
|
=================
|
|
*/
|
|
void GAME_EXPORT VectorAngles( const float *forward, float *angles )
|
|
{
|
|
float tmp, yaw, pitch;
|
|
|
|
if( !forward || !angles )
|
|
{
|
|
if( angles ) VectorClear( angles );
|
|
return;
|
|
}
|
|
|
|
if( forward[1] == 0 && forward[0] == 0 )
|
|
{
|
|
// fast case
|
|
yaw = 0;
|
|
if( forward[2] > 0 )
|
|
pitch = 90.0f;
|
|
else pitch = 270.0f;
|
|
}
|
|
else
|
|
{
|
|
yaw = ( atan2( forward[1], forward[0] ) * 180 / M_PI_F );
|
|
if( yaw < 0 ) yaw += 360;
|
|
|
|
tmp = sqrt( forward[0] * forward[0] + forward[1] * forward[1] );
|
|
pitch = ( atan2( forward[2], tmp ) * 180 / M_PI_F );
|
|
if( pitch < 0 ) pitch += 360;
|
|
}
|
|
|
|
VectorSet( angles, pitch, yaw, 0 );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
VectorsAngles
|
|
|
|
=================
|
|
*/
|
|
void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, vec3_t angles )
|
|
{
|
|
float pitch, cpitch, yaw, roll;
|
|
|
|
pitch = -asin( forward[2] );
|
|
cpitch = cos( pitch );
|
|
|
|
if( fabs( cpitch ) > EQUAL_EPSILON ) // gimball lock?
|
|
{
|
|
cpitch = 1.0f / cpitch;
|
|
pitch = RAD2DEG( pitch );
|
|
yaw = RAD2DEG( atan2( forward[1] * cpitch, forward[0] * cpitch ));
|
|
roll = RAD2DEG( atan2( -right[2] * cpitch, up[2] * cpitch ));
|
|
}
|
|
else
|
|
{
|
|
pitch = forward[2] > 0 ? -90.0f : 90.0f;
|
|
yaw = RAD2DEG( atan2( right[0], -right[1] ));
|
|
roll = 180.0f;
|
|
}
|
|
|
|
angles[PITCH] = pitch;
|
|
angles[YAW] = yaw;
|
|
angles[ROLL] = roll;
|
|
}
|
|
|
|
//
|
|
// bounds operations
|
|
//
|
|
/*
|
|
=================
|
|
AddPointToBounds
|
|
=================
|
|
*/
|
|
void AddPointToBounds( const vec3_t v, vec3_t mins, vec3_t maxs )
|
|
{
|
|
float val;
|
|
int i;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
val = v[i];
|
|
if( val < mins[i] ) mins[i] = val;
|
|
if( val > maxs[i] ) maxs[i] = val;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ExpandBounds (not used anywhere?)
|
|
=================
|
|
*/
|
|
void ExpandBounds( vec3_t mins, vec3_t maxs, float offset )
|
|
{
|
|
mins[0] -= offset;
|
|
mins[1] -= offset;
|
|
mins[2] -= offset;
|
|
maxs[0] += offset;
|
|
maxs[1] += offset;
|
|
maxs[2] += offset;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SphereIntersect
|
|
=================
|
|
*/
|
|
qboolean SphereIntersect( const vec3_t vSphereCenter, float fSphereRadiusSquared, const vec3_t vLinePt, const vec3_t vLineDir )
|
|
{
|
|
float a, b, c, insideSqr;
|
|
vec3_t p;
|
|
|
|
// translate sphere to origin.
|
|
VectorSubtract( vLinePt, vSphereCenter, p );
|
|
|
|
a = DotProduct( vLineDir, vLineDir );
|
|
b = 2.0f * DotProduct( p, vLineDir );
|
|
c = DotProduct( p, p ) - fSphereRadiusSquared;
|
|
|
|
insideSqr = b * b - 4.0f * a * c;
|
|
if( insideSqr <= 0.000001f )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
PlaneIntersect
|
|
|
|
find point where ray
|
|
was intersect with plane
|
|
=================
|
|
*/
|
|
void PlaneIntersect( const mplane_t *plane, const vec3_t p0, const vec3_t p1, vec3_t out )
|
|
{
|
|
float distToPlane = PlaneDiff( p0, plane );
|
|
float planeDotRay = DotProduct( plane->normal, p1 );
|
|
float sect = -(distToPlane) / planeDotRay;
|
|
|
|
VectorMA( p0, sect, p1, out );
|
|
}
|
|
|
|
/*
|
|
=================
|
|
RadiusFromBounds
|
|
=================
|
|
*/
|
|
float RadiusFromBounds( const vec3_t mins, const vec3_t maxs )
|
|
{
|
|
vec3_t corner;
|
|
int i;
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
corner[i] = fabs( mins[i] ) > fabs( maxs[i] ) ? fabs( mins[i] ) : fabs( maxs[i] );
|
|
}
|
|
return VectorLength( corner );
|
|
}
|
|
|
|
//
|
|
// studio utils
|
|
//
|
|
/*
|
|
====================
|
|
AngleQuaternion
|
|
|
|
====================
|
|
*/
|
|
void AngleQuaternion( const vec3_t angles, vec4_t q, qboolean studio )
|
|
{
|
|
float sr, sp, sy, cr, cp, cy;
|
|
|
|
if( studio )
|
|
{
|
|
SinCos( angles[ROLL] * 0.5f, &sy, &cy );
|
|
SinCos( angles[YAW] * 0.5f, &sp, &cp );
|
|
SinCos( angles[PITCH] * 0.5f, &sr, &cr );
|
|
}
|
|
else
|
|
{
|
|
SinCos( DEG2RAD( angles[YAW] ) * 0.5f, &sy, &cy );
|
|
SinCos( DEG2RAD( angles[PITCH] ) * 0.5f, &sp, &cp );
|
|
SinCos( DEG2RAD( angles[ROLL] ) * 0.5f, &sr, &cr );
|
|
}
|
|
|
|
q[0] = sr * cp * cy - cr * sp * sy; // X
|
|
q[1] = cr * sp * cy + sr * cp * sy; // Y
|
|
q[2] = cr * cp * sy - sr * sp * cy; // Z
|
|
q[3] = cr * cp * cy + sr * sp * sy; // W
|
|
}
|
|
|
|
/*
|
|
====================
|
|
QuaternionAngle
|
|
|
|
====================
|
|
*/
|
|
void QuaternionAngle( const vec4_t q, vec3_t angles )
|
|
{
|
|
matrix3x4 mat;
|
|
Matrix3x4_FromOriginQuat( mat, q, vec3_origin );
|
|
Matrix3x4_AnglesFromMatrix( mat, angles );
|
|
}
|
|
|
|
/*
|
|
====================
|
|
QuaternionAlign
|
|
|
|
make sure quaternions are within 180 degrees of one another,
|
|
if not, reverse q
|
|
====================
|
|
*/
|
|
void QuaternionAlign( const vec4_t p, const vec4_t q, vec4_t qt )
|
|
{
|
|
// decide if one of the quaternions is backwards
|
|
float a = 0.0f;
|
|
float b = 0.0f;
|
|
int i;
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
a += (p[i] - q[i]) * (p[i] - q[i]);
|
|
b += (p[i] + q[i]) * (p[i] + q[i]);
|
|
}
|
|
|
|
if( a > b )
|
|
{
|
|
for( i = 0; i < 4; i++ )
|
|
qt[i] = -q[i];
|
|
}
|
|
else
|
|
{
|
|
for( i = 0; i < 4; i++ )
|
|
qt[i] = q[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
QuaternionSlerpNoAlign
|
|
====================
|
|
*/
|
|
void QuaternionSlerpNoAlign( const vec4_t p, const vec4_t q, float t, vec4_t qt )
|
|
{
|
|
float omega, cosom, sinom, sclp, sclq;
|
|
int i;
|
|
|
|
// 0.0 returns p, 1.0 return q.
|
|
cosom = p[0] * q[0] + p[1] * q[1] + p[2] * q[2] + p[3] * q[3];
|
|
|
|
if(( 1.0f + cosom ) > 0.000001f )
|
|
{
|
|
if(( 1.0f - cosom ) > 0.000001f )
|
|
{
|
|
omega = acos( cosom );
|
|
sinom = sin( omega );
|
|
sclp = sin( (1.0f - t) * omega) / sinom;
|
|
sclq = sin( t * omega ) / sinom;
|
|
}
|
|
else
|
|
{
|
|
sclp = 1.0f - t;
|
|
sclq = t;
|
|
}
|
|
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
qt[i] = sclp * p[i] + sclq * q[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qt[0] = -q[1];
|
|
qt[1] = q[0];
|
|
qt[2] = -q[3];
|
|
qt[3] = q[2];
|
|
sclp = sin(( 1.0f - t ) * ( 0.5f * M_PI_F ));
|
|
sclq = sin( t * ( 0.5f * M_PI_F ));
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
qt[i] = sclp * p[i] + sclq * qt[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
QuaternionSlerp
|
|
|
|
Quaternion sphereical linear interpolation
|
|
====================
|
|
*/
|
|
void QuaternionSlerp( const vec4_t p, const vec4_t q, float t, vec4_t qt )
|
|
{
|
|
vec4_t q2;
|
|
|
|
// 0.0 returns p, 1.0 return q.
|
|
// decide if one of the quaternions is backwards
|
|
QuaternionAlign( p, q, q2 );
|
|
|
|
QuaternionSlerpNoAlign( p, q2, t, qt );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
BoxOnPlaneSide
|
|
|
|
Returns 1, 2, or 1 + 2
|
|
==================
|
|
*/
|
|
int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p )
|
|
{
|
|
float dist1, dist2;
|
|
int sides = 0;
|
|
|
|
// general case
|
|
switch( p->signbits )
|
|
{
|
|
case 0:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 1:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 2:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 3:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
break;
|
|
case 4:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 5:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 6:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
case 7:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2];
|
|
break;
|
|
default:
|
|
// shut up compiler
|
|
dist1 = dist2 = 0;
|
|
break;
|
|
}
|
|
|
|
if( dist1 >= p->dist )
|
|
sides = 1;
|
|
if( dist2 < p->dist )
|
|
sides |= 2;
|
|
|
|
return sides;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
StudioSlerpBones
|
|
|
|
====================
|
|
*/
|
|
void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], const vec4_t q2[], const float pos2[][3], float s )
|
|
{
|
|
int i;
|
|
|
|
s = bound( 0.0f, s, 1.0f );
|
|
|
|
for( i = 0; i < numbones; i++ )
|
|
{
|
|
QuaternionSlerp( q1[i], q2[i], s, q1[i] );
|
|
VectorLerp( pos1[i], s, pos2[i], pos1[i] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
StudioCalcBoneQuaternion
|
|
|
|
====================
|
|
*/
|
|
void R_StudioCalcBoneQuaternion( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec4_t q )
|
|
{
|
|
vec3_t angles1;
|
|
vec3_t angles2;
|
|
int j, k;
|
|
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
if( !panim || panim->offset[j+3] == 0 )
|
|
{
|
|
angles2[j] = angles1[j] = pbone->value[j+3]; // default;
|
|
}
|
|
else
|
|
{
|
|
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]);
|
|
|
|
k = frame;
|
|
|
|
// debug
|
|
if( panimvalue->num.total < panimvalue->num.valid )
|
|
k = 0;
|
|
|
|
// find span of values that includes the frame we want
|
|
while( panimvalue->num.total <= k )
|
|
{
|
|
k -= panimvalue->num.total;
|
|
panimvalue += panimvalue->num.valid + 1;
|
|
|
|
// debug
|
|
if( panimvalue->num.total < panimvalue->num.valid )
|
|
k = 0;
|
|
}
|
|
|
|
// bah, missing blend!
|
|
if( panimvalue->num.valid > k )
|
|
{
|
|
angles1[j] = panimvalue[k+1].value;
|
|
|
|
if( panimvalue->num.valid > k + 1 )
|
|
{
|
|
angles2[j] = panimvalue[k+2].value;
|
|
}
|
|
else
|
|
{
|
|
if( panimvalue->num.total > k + 1 )
|
|
angles2[j] = angles1[j];
|
|
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
angles1[j] = panimvalue[panimvalue->num.valid].value;
|
|
if( panimvalue->num.total > k + 1 )
|
|
angles2[j] = angles1[j];
|
|
else angles2[j] = panimvalue[panimvalue->num.valid+2].value;
|
|
}
|
|
|
|
angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3];
|
|
angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3];
|
|
}
|
|
|
|
if( pbone->bonecontroller[j+3] != -1 && adj != NULL )
|
|
{
|
|
angles1[j] += adj[pbone->bonecontroller[j+3]];
|
|
angles2[j] += adj[pbone->bonecontroller[j+3]];
|
|
}
|
|
}
|
|
|
|
if( !VectorCompare( angles1, angles2 ))
|
|
{
|
|
vec4_t q1, q2;
|
|
|
|
AngleQuaternion( angles1, q1, true );
|
|
AngleQuaternion( angles2, q2, true );
|
|
QuaternionSlerp( q1, q2, s, q );
|
|
}
|
|
else
|
|
{
|
|
AngleQuaternion( angles1, q, true );
|
|
}
|
|
}
|
|
|
|
/*
|
|
====================
|
|
StudioCalcBonePosition
|
|
|
|
====================
|
|
*/
|
|
void R_StudioCalcBonePosition( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec3_t pos )
|
|
{
|
|
vec3_t origin1;
|
|
vec3_t origin2;
|
|
int j, k;
|
|
|
|
for( j = 0; j < 3; j++ )
|
|
{
|
|
if( !panim || panim->offset[j] == 0 )
|
|
{
|
|
origin2[j] = origin1[j] = pbone->value[j]; // default;
|
|
}
|
|
else
|
|
{
|
|
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]);
|
|
|
|
k = frame;
|
|
|
|
// debug
|
|
if( panimvalue->num.total < panimvalue->num.valid )
|
|
k = 0;
|
|
|
|
// find span of values that includes the frame we want
|
|
while( panimvalue->num.total <= k )
|
|
{
|
|
k -= panimvalue->num.total;
|
|
panimvalue += panimvalue->num.valid + 1;
|
|
|
|
// debug
|
|
if( panimvalue->num.total < panimvalue->num.valid )
|
|
k = 0;
|
|
}
|
|
|
|
// bah, missing blend!
|
|
if( panimvalue->num.valid > k )
|
|
{
|
|
origin1[j] = panimvalue[k+1].value;
|
|
|
|
if( panimvalue->num.valid > k + 1 )
|
|
{
|
|
origin2[j] = panimvalue[k+2].value;
|
|
}
|
|
else
|
|
{
|
|
if( panimvalue->num.total > k + 1 )
|
|
origin2[j] = origin1[j];
|
|
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
origin1[j] = panimvalue[panimvalue->num.valid].value;
|
|
if( panimvalue->num.total > k + 1 )
|
|
origin2[j] = origin1[j];
|
|
else origin2[j] = panimvalue[panimvalue->num.valid+2].value;
|
|
}
|
|
|
|
origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j];
|
|
origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j];
|
|
}
|
|
|
|
if( pbone->bonecontroller[j] != -1 && adj != NULL )
|
|
{
|
|
origin1[j] += adj[pbone->bonecontroller[j]];
|
|
origin2[j] += adj[pbone->bonecontroller[j]];
|
|
}
|
|
}
|
|
|
|
if( !VectorCompare( origin1, origin2 ))
|
|
{
|
|
VectorLerp( origin1, s, origin2, pos );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( origin1, pos );
|
|
}
|
|
}
|