st/code/ui/ui_splines.c
2008-04-04 00:00:00 +00:00

373 lines
No EOL
8.4 KiB
C

/*
===========================================================================
Copyright (C) 2006 HermitWorks Entertainment Corporation
This file is part of Space Trader source code.
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 2
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
===========================================================================
*/
#include "ui_local.h"
/*
Ye Olde Cubic NURBS code.
*/
//little struct to cache the aweful coefficients
typedef struct blendFunc3_t
{
float c1_shift, c1_coeff,
d1_coeff; // = 3 / c1_coeff
float c2_cubic, c2_quadratic, c2_linear, c2_const,
d2_quadratic, // = 3 * c2_cubic
d2_linear; // = 2 * c2_quadratic, d2_const = c2_linear
float c3_cubic, c3_quadratic, c3_linear, c3_const,
d3_quadratic, // = 3 * c3_cubic
d3_linear; // = 2 * c3_quadratic, d3_const = c3_linear
float c4_shift, c4_coeff,
d4_coeff; // = 3 / c4_coeff
const float *u;
} blendFunc3_t;
static void Spl_BlendFunc3Init( blendFunc3_t *f, const float *knots )
{
const float *u;
float a, b, c, d;
float c3, c2, c1, c0;
u = knots;
f->u = knots;
f->c1_shift = -u[0];
a = ((u[3] - u[0]) * (u[2] - u[0]) * (u[1] - u[0]));
f->c1_coeff = 1.0F / a;
f->d1_coeff = 3.0F / a;
a = u[3] - u[0];
b = (u[2] - u[0]) * (u[2] - u[1]);
c = (u[3] - u[1]) * (u[2] - u[1]);
d = (u[4] - u[1]) * (u[3] - u[1]) * (u[2] - u[1]);
b *= a;
c *= a;
if( b != 0 )
{
c3 = -1.0F;
c2 = 2.0F * u[0] + u[2];
c1 = -(u[0] * u[0] + 2.0F * u[0] * u[2]);
c0 = u[0] * u[0] * u[2];
f->c2_cubic = c3 / b;
f->c2_quadratic = c2 / b;
f->c2_linear = c1 / b;
f->c2_const = c0 / b;
}
else
{
f->c2_cubic = 0;
f->c2_quadratic = 0;
f->c2_linear = 0;
f->c2_const = 0;
}
if( c != 0 )
{
c3 = -1.0F;
c2 = u[0] + u[1] + u[3];
c1 = -(u[3] * (u[0] + u[1]) + u[0] * u[1]);
c0 = u[0] * u[1] * u[3];
f->c2_cubic += c3 / c;
f->c2_quadratic += c2 / c;
f->c2_linear += c1 / c;
f->c2_const += c0 / c;
}
if( d != 0 )
{
c3 = -1.0F;
c2 = 2.0F * u[1] + u[4];
c1 = -(u[1] * u[1] + 2.0F * u[1] * u[4]);
c0 = u[1] * u[1] * u[4];
f->c2_cubic += c3 / d;
f->c2_quadratic += c2 / d;
f->c2_linear += c1 / d;
f->c2_const += c0 / d;
}
f->d2_quadratic = f->c2_cubic * 3.0F;
f->d2_linear = f->c2_quadratic * 2.0F;
a = (u[3] - u[0]) * (u[3] - u[1]) * (u[3] - u[2]);
b = u[4] - u[1];
c = (u[3] - u[1]) * (u[3] - u[2]);
d = (u[4] - u[2]) * (u[3] - u[2]);
c *= b;
d *= b;
if( a != 0 )
{
c3 = 1.0F;
c2 = -(2.0F * u[3] + u[0]);
c1 = u[3] * u[3] + 2.0F * u[0] * u[3];
c0 = -(u[3] * u[3] * u[0]);
f->c3_cubic = c3 / a;
f->c3_quadratic = c2 / a;
f->c3_linear = c1 / a;
f->c3_const = c0 / a;
}
else
{
f->c3_cubic = 0;
f->c3_quadratic = 0;
f->c3_linear = 0;
f->c3_const = 0;
}
if( c != 0 )
{
c3 = 1.0F;
c2 = -(u[1] + u[3] + u[4]);
c1 = u[3] * u[4] + u[1] * (u[3] + u[4]);
c0 = -(u[1] * u[3] * u[4]);
f->c3_cubic += c3 / c;
f->c3_quadratic += c2 / c;
f->c3_linear += c1 / c;
f->c3_const += c0 / c;
}
if( d != 0 )
{
c3 = 1.0F;
c2 = -(2.0F * u[4] + u[2]);
c1 = u[4] * u[4] + 2.0F * u[2] * u[4];
c0 = -(u[2] * u[4] * u[4]);
f->c3_cubic += c3 / d;
f->c3_quadratic += c2 / d;
f->c3_linear += c1 / d;
f->c3_const += c0 /d;
}
f->d3_quadratic = f->c3_cubic * 3.0F;
f->d3_linear = f->c3_quadratic * 2.0F;
f->c4_shift = u[4];
a = (u[4] - u[1]) * (u[4] - u[2]) * (u[4] - u[3]);
f->c4_coeff = 1.0F / a;
f->d4_coeff = -3.0F / a;
}
static void Spl_BlendFunc3Eval( /* out */ float *value, /* out */ float *derivative, const blendFunc3_t *f, float u )
{
float a, a2, a3;
*value = 0.0F;
*derivative = 0.0F;
if( u < f->u[0] || u > f->u[4] )
//outside the blending function entirely
return;
//we know that u >= u0
if( u < f->u[1] )
{
a = u + f->c1_shift;
a2 = a * a;
a3 = a2 * a;
*value = a3 * f->c1_coeff;
*derivative = a2 * f->d1_coeff;
}
if( u >= f->u[1] && u < f->u[2] )
{
*value += ((f->c2_cubic * u + f->c2_quadratic) * u + f->c2_linear) * u + f->c2_const;
*derivative += (f->d2_quadratic * u + f->d2_linear) * u + f->c2_linear; //d2_const = c2_linear
}
if( u >= f->u[2] && u < f->u[3] )
{
*value += ((f->c3_cubic * u + f->c3_quadratic) * u + f->c3_linear) * u + f->c3_const;
*derivative += (f->d3_quadratic * u + f->d3_linear) * u + f->c3_linear; //d3_const = c3_linear
}
if( u >= f->u[3] )
{
a = f->c4_shift - u;
a2 = a * a;
a3 = a2 * a;
*value += a3 * f->c4_coeff;
*derivative += a2 * f->d4_coeff;
}
}
static void Spl_NormalizeKnots( float *knots, uint count )
{
uint i;
float sub = knots[0];
float mul = 1.0F / (knots[count - 1] - sub);
for( i = 1; i < count - 1; i++ )
knots[i] = (knots[i] - sub) * mul;
knots[0] = 0.0F;
knots[count - 1] = 1.0F;
}
static void Spl_MakeDefaultKnots( float *knots, uint count, uint rank )
{
uint i;
uint d = min( rank + 1, count / 2 );
float m;
for( i = 0; i < d; i++ )
knots[i] = 0;
m = 0;
for( i = d; i < count - d; i++ )
knots[i] = (m += 1.0F);
for( i = count - d; i < count; i++ )
knots[i] = m;
Spl_NormalizeKnots( knots, count );
}
#define AlignP( p, a ) (byte*)Align( (size_t)(p), a )
typedef struct curve3_t
{
uint numPts;
float *knots;
blendFunc3_t *blends;
vec4_t *controlPoints;
} curve3_t;
size_t Spl_CurveMemGetSize( uint numPoints )
{
if( numPoints < 4 )
return 0;
return
sizeof( curve3_t ) +
sizeof( vec4_t ) * numPoints + 16 + //give space to align
sizeof( blendFunc3_t ) * numPoints + //previous guarantees alignment here
sizeof( float ) * (numPoints + 4); //previous guarantees alignment here
}
curve3_t* Spl_CurveMemInit( void *mem, uint numPoints )
{
curve3_t *ret = (curve3_t*)mem;
byte *m = AlignP( (byte*)mem + sizeof( curve3_t ), 16 );
ret->numPts = numPoints;
ret->controlPoints = (vec4_t*)m;
Com_Memset( ret->controlPoints, 0, sizeof( vec4_t ) * numPoints );
m += sizeof( vec4_t ) * numPoints;
ret->blends = (blendFunc3_t*)m;
Com_Memset( ret->blends, 0, sizeof( blendFunc3_t ) * numPoints );
m += sizeof( blendFunc3_t ) * numPoints;
ret->knots = (float*)m;
Spl_MakeDefaultKnots( ret->knots, ret->numPts + 4, 3 );
Spl_CurveUpdateKnots( ret, ret->knots );
return ret;
}
void Spl_CurveUpdateKnots( curve3_t *cv, const float *knots )
{
uint i;
if( cv->knots != knots )
Com_Memcpy( cv->knots, knots, sizeof( float ) * (cv->numPts + 4) );
for( i = 0; i < cv->numPts; i++ )
Spl_BlendFunc3Init( cv->blends + i, cv->knots + i );
}
vec4_t* Spl_CurveGetPointsData( curve3_t *cv )
{
return cv->controlPoints;
}
void Spl_CurveEval( /* out */ vec3_t position, /* out */ vec3_t tangent, const curve3_t *cv, float u )
{
uint i, j;
vec4_t du;
vec4_t value;
if( u < cv->knots[0] )
{
Vec4_Cpy( value, cv->controlPoints[0] );
if( tangent )
VectorClear( tangent );
goto end;
}
if( u > cv->knots[cv->numPts + 4 - 1] )
{
Vec4_Cpy( value, cv->controlPoints[cv->numPts - 1] );
if( tangent )
VectorClear( tangent );
goto end;
}
for( j = 0; j < 4; j++ )
value[j] = 0;
if( tangent )
for( j = 0; j < 4; j++ )
du[j] = 0;
for( i = 0; i < cv->numPts; i++ )
{
float weight, deriv;
Spl_BlendFunc3Eval( &weight, &deriv, cv->blends + i, u );
for( j = 0; j < 4; j++ )
value[j] += cv->controlPoints[i][j] * weight;
if( tangent )
for( j = 0; j < 4; j++ )
du[j] += cv->controlPoints[i][j] * deriv;
}
if( tangent )
for( j = 0; j < 3; j++ )
tangent[j] = (du[j] * value[3] - value[j] * du[3]) * (value[3] * value[3]);
end:
value[3] = 1.0F / value[3];
for( j = 0; j < 3; j++ )
position[j] = value[j] * value[3];
}