/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at
golgotha_source@usa.net (Subject should have "GOLG" in it)
***********************************************************************/
#include "math/spline.hh"
#include "memory/lalloc.hh"
#include "math/matrix.hh"
#include "error/error.hh"
#include "file/file.hh"
i4_linear_allocator *i4_spline_class::p_alloc=0;
w32 i4_spline_class::point_refs=0;
void i4_spline_class::move(i4_float x_add, i4_float y_add, i4_float z_add)
{
for (point *p=points; p; p=p->next)
{
p->x+=x_add;
p->y+=y_add;
p->z+=z_add;
}
}
void i4_spline_class::save(i4_file_class *fp)
{
fp->write_32(t_points);
for (point *p=points; p; p=p->next)
{
fp->write_32(p->frame);
fp->write_8(p->selected);
fp->write_float(p->x);
fp->write_float(p->y);
fp->write_float(p->z);
}
}
void i4_spline_class::load(i4_file_class *fp)
{
cleanup();
t_points=fp->read_32();
point *last=0;
for (w32 i=0; ialloc();
p->frame=fp->read_32();
p->selected=fp->read_8();
p->x=fp->read_float();
p->y=fp->read_float();
p->z=fp->read_float();
p->next=0;
if (last)
last->next=p;
else points=p;
last=p;
}
}
class i4_1x4_vector
{
public:
i4_float x,y,z,t;
i4_1x4_vector() { ; }
i4_1x4_vector(i4_float x, i4_float y, i4_float z, i4_float t) : x(x), y(y), z(z), t(t) { ; }
i4_float dot(const i4_1x4_vector &b) { return b.x*x + b.y*y + b.z*z + b.t*t; }
i4_1x4_vector& operator*=(i4_float b)
{
x *= b;
y *= b;
z *= b;
t *= b;
return *this;
}
};
class i4_bspline_basis : public i4_4x4_matrix_class
{
public:
i4_bspline_basis()
{
sw16 b[16]= { -1,3,-3,1,3,-6,3,0,-3,0,3,0,1,4,1,0};
for (w32 i=0; i<16; i++)
elt[i]=(i4_float)b[i];
}
void transform(i4_1x4_vector &b, i4_1x4_vector &result)
{
i4_float vx,vy,vz,vt;
vx=b.x;
vy=b.y;
vz=b.z;
vt=b.t;
result.x = vx*elt[0] + vy*elt[1] + vz*elt[2] + vt*elt[3];
result.y = vx*elt[4] + vy*elt[5] + vz*elt[6] + vt*elt[7];
result.z = vx*elt[8] + vy*elt[9] + vz*elt[10] + vt*elt[11];
result.t = vx*elt[12] + vy*elt[13] + vz*elt[14] + vt*elt[15];
}
};
void i4_spline_class::delete_selected()
{
point *p,*last=0;
for (p=points; p; )
{
if (p->selected)
{
if (last)
last->next=p->next;
else points=p->next;
point *q=p;
p=p->next;
p_alloc->free(q);
t_points--;
}
else
{
last=p;
p=p->next;
}
}
}
i4_spline_class::i4_spline_class()
{
if (!p_alloc)
p_alloc=new i4_linear_allocator(sizeof(point), 512, 512, "spline point allocator");
point_refs++;
points=0;
t_points=0;
}
void i4_spline_class::cleanup()
{
while (points)
{
point *q=points;
points=points->next;
t_points--;
p_alloc->free(q);
}
}
i4_spline_class::~i4_spline_class()
{
cleanup();
point_refs--;
if (point_refs==0)
{
delete p_alloc;
p_alloc=0;
}
}
i4_spline_class::point *i4_spline_class::add_control_point(i4_float x,
i4_float y,
i4_float z,
w32 frame)
{
point *p=points,*last=0;
while (p && p->frame<=frame)
{
last=p;
p=p->next;
}
if (last && last->frame==frame) // you can't add two different points at the same time
{
last->x=x;
last->y=y;
last->z=z;
return last;
}
point *n=(point *)p_alloc->alloc();
n->x=x;
n->y=y;
n->z=z;
n->frame=frame;
n->selected=i4_F;
if (last)
{
n->next=last->next;
last->next=n;
}
else
{
n->next=points;
points=n;
}
t_points++;
return n;
}
i4_spline_class::point *i4_spline_class::get_control_point(w32 p)
{
point *q=points;
while (p && q)
{
p--;
q=q->next;
}
return q;
}
i4_bool i4_spline_class::get_point(w32 frame, i4_float &x, i4_float &y, i4_float &z)
{
if (!points || points->frame>frame)
return i4_F;
if (!points->next)
{
x=points->x;
y=points->y;
z=points->z;
return i4_T;
}
point *l1=points, *l2=points, *l3=points, *l4=points;
while (l4->next && l2->frame<=frame)
{
l4=l3;
l3=l2;
l2=l1;
if (l1->next)
l1=l1->next;
}
i4_bspline_basis b;
i4_float u;
if (l2->frame==l3->frame)
u=0;
else
u=(frame-l3->frame)/(i4_float)(l2->frame-l3->frame);
i4_1x4_vector px=i4_1x4_vector(l4->x, l3->x, l2->x, l1->x),rx;
i4_1x4_vector py=i4_1x4_vector(l4->y, l3->y, l2->y, l1->y),ry;
i4_1x4_vector pz=i4_1x4_vector(l4->z, l3->z, l2->z, l1->z),rz;
i4_1x4_vector u_vector=i4_1x4_vector(u*u*u, u*u, u, 1);
b.transform(px,rx);
b.transform(py,ry);
b.transform(pz,rz);
rx*=1/6.0;
ry*=1/6.0;
rz*=1/6.0;
x=u_vector.dot(rx);
y=u_vector.dot(ry);
z=u_vector.dot(rz);
return i4_T;
}
w32 i4_spline_class::last_frame()
{
if (!points) return 0;
point *p;
for (p=points; p->next; p=p->next);
return p->frame;
}
void i4_spline_class::insert_control_points()
{
for (point *p=points; p; p=p->next)
{
if (p->selected && p->next && p->next->frame>p->frame+1)
{
point *n=(point *)p_alloc->alloc();
n->x=(p->x + p->next->x)/2.0;
n->y=(p->y + p->next->y)/2.0;
n->z=(p->z + p->next->z)/2.0;
n->frame=(p->frame + p->next->frame)/2;
n->selected=i4_F;
n->next=p->next;
p->next=n;
p=p->next;
t_points++;
}
}
}
i4_spline_class::point *i4_spline_class::get_control_point_previous_to_frame(w32 frame)
{
point *p=points;
for (; p && p->framenext);
return p;
}