mirror of
https://github.com/aap/librw.git
synced 2024-11-28 22:55:42 +00:00
1122 lines
23 KiB
C++
1122 lines
23 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <ctype.h>
|
|
|
|
#define PSEP_C '/'
|
|
#define PSEP_S "/"
|
|
#ifdef __unix__
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#include "rwbase.h"
|
|
#include "rwerror.h"
|
|
#include "rwplg.h"
|
|
#include "rwpipeline.h"
|
|
#include "rwobjects.h"
|
|
#include "rwengine.h"
|
|
|
|
namespace rw {
|
|
|
|
#define PLUGIN_ID 0
|
|
|
|
int32 version = 0x36003;
|
|
int32 build = 0xFFFF;
|
|
#ifdef RW_PS2
|
|
int32 platform = PLATFORM_PS2;
|
|
#elif RW_WDGL
|
|
int32 platform = PLATFORM_WDGL;
|
|
#elif RW_GL3
|
|
int32 platform = PLATFORM_GL3;
|
|
#elif RW_D3D9
|
|
int32 platform = PLATFORM_D3D9;
|
|
#else
|
|
int32 platform = PLATFORM_NULL;
|
|
#endif
|
|
bool32 streamAppendFrames = 0;
|
|
char *debugFile = nil;
|
|
|
|
static Matrix identMat = {
|
|
{ 1.0f, 0.0f, 0.0f }, Matrix::IDENTITY|Matrix::TYPEORTHONORMAL,
|
|
{ 0.0f, 1.0f, 0.0f }, 0,
|
|
{ 0.0f, 0.0f, 1.0f }, 0,
|
|
{ 0.0f, 0.0f, 0.0f }, 0
|
|
};
|
|
|
|
// lazy implementation
|
|
int
|
|
strcmp_ci(const char *s1, const char *s2)
|
|
{
|
|
char c1, c2;
|
|
for(;;){
|
|
c1 = tolower(*s1);
|
|
c2 = tolower(*s2);
|
|
if(c1 != c2)
|
|
return c1 - c2;
|
|
if(c1 == '\0')
|
|
return 0;
|
|
s1++;
|
|
s2++;
|
|
}
|
|
}
|
|
|
|
int
|
|
strncmp_ci(const char *s1, const char *s2, int n)
|
|
{
|
|
char c1, c2;
|
|
while(n--){
|
|
c1 = tolower(*s1);
|
|
c2 = tolower(*s2);
|
|
if(c1 != c2)
|
|
return c1 - c2;
|
|
if(c1 == '\0')
|
|
return 0;
|
|
s1++;
|
|
s2++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
Quat
|
|
mult(const Quat &q, const Quat &p)
|
|
{
|
|
return makeQuat(q.w*p.w - q.x*p.x - q.y*p.y - q.z*p.z,
|
|
q.w*p.x + q.x*p.w + q.y*p.z - q.z*p.y,
|
|
q.w*p.y + q.y*p.w + q.z*p.x - q.x*p.z,
|
|
q.w*p.z + q.z*p.w + q.x*p.y - q.y*p.x);
|
|
}
|
|
|
|
|
|
Quat*
|
|
Quat::rotate(const V3d *axis, float32 angle, CombineOp op)
|
|
{
|
|
Quat rot = rotation(angle, *axis);
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = rot;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
*this = mult(*this, rot);
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
*this = mult(rot, *this);
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Quat
|
|
lerp(const Quat &q, const Quat &p, float32 r)
|
|
{
|
|
float32 c;
|
|
Quat q1 = q;
|
|
c = dot(q1, p);
|
|
if(c < 0.0f){
|
|
c = -c;
|
|
q1 = negate(q1);
|
|
}
|
|
return makeQuat(q1.w + r*(p.w - q1.w),
|
|
q1.x + r*(p.x - q1.x),
|
|
q1.y + r*(p.y - q1.y),
|
|
q1.z + r*(p.z - q1.z));
|
|
};
|
|
|
|
Quat
|
|
slerp(const Quat &q, const Quat &p, float32 a)
|
|
{
|
|
float32 c;
|
|
Quat q1 = q;
|
|
c = dot(q1, p);
|
|
if(c < 0.0f){
|
|
c = -c;
|
|
q1 = negate(q1);
|
|
}
|
|
float32 phi = acosf(c);
|
|
if(phi > 0.00001f){
|
|
float32 s = sinf(phi);
|
|
return add(scale(q1, sinf((1.0f-a)*phi)/s),
|
|
scale(p, sinf(a*phi)/s));
|
|
}
|
|
return q1;
|
|
}
|
|
|
|
//
|
|
// V3d
|
|
//
|
|
|
|
V3d
|
|
cross(const V3d &a, const V3d &b)
|
|
{
|
|
return makeV3d(a.y*b.z - a.z*b.y,
|
|
a.z*b.x - a.x*b.z,
|
|
a.x*b.y - a.y*b.x);
|
|
}
|
|
|
|
void
|
|
V3d::transformPoints(V3d *out, const V3d *in, int32 n, const Matrix *m)
|
|
{
|
|
int32 i;
|
|
V3d tmp;
|
|
for(i = 0; i < n; i++){
|
|
tmp.x = in[i].x*m->right.x + in[i].y*m->up.x + in[i].z*m->at.x + m->pos.x;
|
|
tmp.y = in[i].x*m->right.y + in[i].y*m->up.y + in[i].z*m->at.y + m->pos.y;
|
|
tmp.z = in[i].x*m->right.z + in[i].y*m->up.z + in[i].z*m->at.z + m->pos.z;
|
|
out[i] = tmp;
|
|
}
|
|
}
|
|
|
|
void
|
|
V3d::transformVectors(V3d *out, const V3d *in, int32 n, const Matrix *m)
|
|
{
|
|
int32 i;
|
|
V3d tmp;
|
|
for(i = 0; i < n; i++){
|
|
tmp.x = in[i].x*m->right.x + in[i].y*m->up.x + in[i].z*m->at.x;
|
|
tmp.y = in[i].x*m->right.y + in[i].y*m->up.y + in[i].z*m->at.y;
|
|
tmp.z = in[i].x*m->right.z + in[i].y*m->up.z + in[i].z*m->at.z;
|
|
out[i] = tmp;
|
|
}
|
|
}
|
|
|
|
//
|
|
// RawMatrix
|
|
//
|
|
|
|
void
|
|
RawMatrix::mult(RawMatrix *dst, RawMatrix *src1, RawMatrix *src2)
|
|
{
|
|
dst->right.x = src1->right.x*src2->right.x + src1->right.y*src2->up.x + src1->right.z*src2->at.x + src1->rightw*src2->pos.x;
|
|
dst->right.y = src1->right.x*src2->right.y + src1->right.y*src2->up.y + src1->right.z*src2->at.y + src1->rightw*src2->pos.y;
|
|
dst->right.z = src1->right.x*src2->right.z + src1->right.y*src2->up.z + src1->right.z*src2->at.z + src1->rightw*src2->pos.z;
|
|
dst->rightw = src1->right.x*src2->rightw + src1->right.y*src2->upw + src1->right.z*src2->atw + src1->rightw*src2->posw;
|
|
dst->up.x = src1->up.x*src2->right.x + src1->up.y*src2->up.x + src1->up.z*src2->at.x + src1->upw*src2->pos.x;
|
|
dst->up.y = src1->up.x*src2->right.y + src1->up.y*src2->up.y + src1->up.z*src2->at.y + src1->upw*src2->pos.y;
|
|
dst->up.z = src1->up.x*src2->right.z + src1->up.y*src2->up.z + src1->up.z*src2->at.z + src1->upw*src2->pos.z;
|
|
dst->upw = src1->up.x*src2->rightw + src1->up.y*src2->upw + src1->up.z*src2->atw + src1->upw*src2->posw;
|
|
dst->at.x = src1->at.x*src2->right.x + src1->at.y*src2->up.x + src1->at.z*src2->at.x + src1->atw*src2->pos.x;
|
|
dst->at.y = src1->at.x*src2->right.y + src1->at.y*src2->up.y + src1->at.z*src2->at.y + src1->atw*src2->pos.y;
|
|
dst->at.z = src1->at.x*src2->right.z + src1->at.y*src2->up.z + src1->at.z*src2->at.z + src1->atw*src2->pos.z;
|
|
dst->atw = src1->at.x*src2->rightw + src1->at.y*src2->upw + src1->at.z*src2->atw + src1->atw*src2->posw;
|
|
dst->pos.x = src1->pos.x*src2->right.x + src1->pos.y*src2->up.x + src1->pos.z*src2->at.x + src1->posw*src2->pos.x;
|
|
dst->pos.y = src1->pos.x*src2->right.y + src1->pos.y*src2->up.y + src1->pos.z*src2->at.y + src1->posw*src2->pos.y;
|
|
dst->pos.z = src1->pos.x*src2->right.z + src1->pos.y*src2->up.z + src1->pos.z*src2->at.z + src1->posw*src2->pos.z;
|
|
dst->posw = src1->pos.x*src2->rightw + src1->pos.y*src2->upw + src1->pos.z*src2->atw + src1->posw*src2->posw;
|
|
}
|
|
|
|
void
|
|
RawMatrix::transpose(RawMatrix *dst, RawMatrix *src)
|
|
{
|
|
dst->right.x = src->right.x;
|
|
dst->up.x = src->right.y;
|
|
dst->at.x = src->right.z;
|
|
dst->pos.x = src->rightw;
|
|
dst->right.y = src->up.x;
|
|
dst->up.y = src->up.y;
|
|
dst->at.y = src->up.z;
|
|
dst->pos.y = src->upw;
|
|
dst->right.z = src->at.x;
|
|
dst->up.z = src->at.y;
|
|
dst->at.z = src->at.z;
|
|
dst->pos.z = src->atw;
|
|
dst->rightw = src->pos.x;
|
|
dst->upw = src->pos.y;
|
|
dst->atw = src->pos.z;
|
|
dst->posw = src->posw;
|
|
}
|
|
|
|
void
|
|
RawMatrix::setIdentity(RawMatrix *dst)
|
|
{
|
|
static RawMatrix identity = {
|
|
{ 1.0f, 0.0f, 0.0f }, 0.0f,
|
|
{ 0.0f, 1.0f, 0.0f }, 0.0f,
|
|
{ 0.0f, 0.0f, 1.0f }, 0.0f,
|
|
{ 0.0f, 0.0f, 0.0f }, 1.0f
|
|
};
|
|
*dst = identity;
|
|
}
|
|
|
|
//
|
|
// Matrix
|
|
//
|
|
|
|
static Matrix::Tolerance matrixDefaultTolerance = { 0.01f, 0.01f, 0.01f };
|
|
|
|
Matrix*
|
|
Matrix::create(void)
|
|
{
|
|
Matrix *m = (Matrix*)rwMalloc(sizeof(Matrix), MEMDUR_EVENT | ID_MATRIX);
|
|
m->setIdentity();
|
|
return m;
|
|
}
|
|
|
|
void
|
|
Matrix::destroy(void)
|
|
{
|
|
rwFree(this);
|
|
}
|
|
|
|
void
|
|
Matrix::setIdentity(void)
|
|
{
|
|
*this = identMat;
|
|
}
|
|
|
|
void
|
|
Matrix::optimize(Tolerance *tolerance)
|
|
{
|
|
bool32 isnormal, isorthogonal, isidentity;
|
|
if(tolerance == nil)
|
|
tolerance = &matrixDefaultTolerance;
|
|
isnormal = normalError() <= tolerance->normal;
|
|
isorthogonal = orthogonalError() <= tolerance->orthogonal;
|
|
isidentity = isnormal && isorthogonal && identityError() <= tolerance->identity;
|
|
if(isnormal)
|
|
flags |= TYPENORMAL;
|
|
else
|
|
flags &= ~TYPENORMAL;
|
|
if(isorthogonal)
|
|
flags |= TYPEORTHOGONAL;
|
|
else
|
|
flags &= ~TYPEORTHOGONAL;
|
|
if(isidentity)
|
|
flags |= IDENTITY;
|
|
else
|
|
flags &= ~IDENTITY;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::mult(Matrix *dst, const Matrix *src1, const Matrix *src2)
|
|
{
|
|
if(src1->flags & IDENTITY)
|
|
*dst = *src2;
|
|
else if(src2->flags & IDENTITY)
|
|
*dst = *src1;
|
|
else{
|
|
mult_(dst, src1, src2);
|
|
dst->flags = src1->flags & src2->flags;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::invert(Matrix *dst, const Matrix *src)
|
|
{
|
|
if(src->flags & IDENTITY)
|
|
*dst = *src;
|
|
else if((src->flags & TYPEMASK) == TYPEORTHONORMAL)
|
|
invertOrthonormal(dst, src);
|
|
else
|
|
return invertGeneral(dst, src);
|
|
return dst;
|
|
}
|
|
|
|
// transpose the 3x3 submatrix, pos is set to 0
|
|
Matrix*
|
|
Matrix::transpose(Matrix *dst, const Matrix *src)
|
|
{
|
|
if(src->flags & IDENTITY)
|
|
*dst = *src;
|
|
dst->right.x = src->right.x;
|
|
dst->up.x = src->right.y;
|
|
dst->at.x = src->right.z;
|
|
dst->right.y = src->up.x;
|
|
dst->up.y = src->up.y;
|
|
dst->at.y = src->up.z;
|
|
dst->right.z = src->at.x;
|
|
dst->up.z = src->at.y;
|
|
dst->at.z = src->at.z;
|
|
dst->pos.x = 0.0;
|
|
dst->pos.y = 0.0;
|
|
dst->pos.z = 0.0;
|
|
return dst;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::rotate(const V3d *axis, float32 angle, CombineOp op)
|
|
{
|
|
Matrix tmp, rot;
|
|
makeRotation(&rot, axis, angle);
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = rot;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
mult(&tmp, &rot, this);
|
|
*this = tmp;
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
mult(&tmp, this, &rot);
|
|
*this = tmp;
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::rotate(const Quat &q, CombineOp op)
|
|
{
|
|
Matrix tmp, rot;
|
|
makeRotation(&rot, q);
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = rot;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
mult(&tmp, &rot, this);
|
|
*this = tmp;
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
mult(&tmp, this, &rot);
|
|
*this = tmp;
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
Matrix*
|
|
Matrix::translate(const V3d *translation, CombineOp op)
|
|
{
|
|
Matrix tmp;
|
|
Matrix trans = identMat;
|
|
trans.pos = *translation;
|
|
trans.flags &= ~IDENTITY;
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = trans;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
mult(&tmp, &trans, this);
|
|
*this = tmp;
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
mult(&tmp, this, &trans);
|
|
*this = tmp;
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::scale(const V3d *scale, CombineOp op)
|
|
{
|
|
Matrix tmp;
|
|
Matrix scl = identMat;
|
|
scl.right.x = scale->x;
|
|
scl.up.y = scale->y;
|
|
scl.at.z = scale->z;
|
|
scl.flags &= ~IDENTITY;
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = scl;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
mult(&tmp, &scl, this);
|
|
*this = tmp;
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
mult(&tmp, this, &scl);
|
|
*this = tmp;
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::transform(const Matrix *mat, CombineOp op)
|
|
{
|
|
Matrix tmp;
|
|
switch(op){
|
|
case COMBINEREPLACE:
|
|
*this = *mat;
|
|
break;
|
|
case COMBINEPRECONCAT:
|
|
mult(&tmp, mat, this);
|
|
*this = tmp;
|
|
break;
|
|
case COMBINEPOSTCONCAT:
|
|
mult(&tmp, this, mat);
|
|
*this = tmp;
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
Quat
|
|
Matrix::getRotation(void)
|
|
{
|
|
Quat q = { 0.0f, 0.0f, 0.0f, 1.0f };
|
|
float32 tr = right.x + up.y + at.z;
|
|
float s;
|
|
if(tr > 0.0f){
|
|
s = sqrtf(1.0f + tr) * 2.0f;
|
|
q.w = s / 4.0f;
|
|
q.x = (up.z - at.y) / s;
|
|
q.y = (at.x - right.z) / s;
|
|
q.z = (right.y - up.x) / s;
|
|
}else if(right.x > up.y && right.x > at.z){
|
|
s = sqrtf(1.0f + right.x - up.y - at.z) * 2.0f;
|
|
q.w = (up.z - at.y) / s;
|
|
q.x = s / 4.0f;
|
|
q.y = (up.x + right.y) / s;
|
|
q.z = (at.x + right.z) / s;
|
|
}else if(up.y > at.z){
|
|
s = sqrtf(1.0f + up.y - right.x - at.z) * 2.0f;
|
|
q.w = (at.x - right.z) / s;
|
|
q.x = (up.x + right.y) / s;
|
|
q.y = s / 4.0f;
|
|
q.z = (at.y + up.z) / s;
|
|
}else{
|
|
s = sqrtf(1.0f + at.z - right.x - up.y) * 2.0f;
|
|
q.w = (right.y - up.x) / s;
|
|
q.x = (at.x + right.z) / s;
|
|
q.y = (at.y + up.z) / s;
|
|
q.z = s / 4.0f;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
void
|
|
Matrix::lookAt(const V3d &dir, const V3d &up)
|
|
{
|
|
// this->right is really pointing left
|
|
this->at = normalize(dir);
|
|
this->right = normalize(cross(up, this->at));
|
|
this->up = cross(this->at, this->right);
|
|
this->flags = TYPEORTHONORMAL;
|
|
}
|
|
|
|
/* For a row-major representation, this calculates src1 * src2.
|
|
* For column-major src2 * src1.
|
|
* i.e. a vector is first xformed by src1, then by src2
|
|
*/
|
|
void
|
|
Matrix::mult_(Matrix *dst, const Matrix *src1, const Matrix *src2)
|
|
{
|
|
dst->right.x = src1->right.x*src2->right.x + src1->right.y*src2->up.x + src1->right.z*src2->at.x;
|
|
dst->right.y = src1->right.x*src2->right.y + src1->right.y*src2->up.y + src1->right.z*src2->at.y;
|
|
dst->right.z = src1->right.x*src2->right.z + src1->right.y*src2->up.z + src1->right.z*src2->at.z;
|
|
dst->up.x = src1->up.x*src2->right.x + src1->up.y*src2->up.x + src1->up.z*src2->at.x;
|
|
dst->up.y = src1->up.x*src2->right.y + src1->up.y*src2->up.y + src1->up.z*src2->at.y;
|
|
dst->up.z = src1->up.x*src2->right.z + src1->up.y*src2->up.z + src1->up.z*src2->at.z;
|
|
dst->at.x = src1->at.x*src2->right.x + src1->at.y*src2->up.x + src1->at.z*src2->at.x;
|
|
dst->at.y = src1->at.x*src2->right.y + src1->at.y*src2->up.y + src1->at.z*src2->at.y;
|
|
dst->at.z = src1->at.x*src2->right.z + src1->at.y*src2->up.z + src1->at.z*src2->at.z;
|
|
dst->pos.x = src1->pos.x*src2->right.x + src1->pos.y*src2->up.x + src1->pos.z*src2->at.x + src2->pos.x;
|
|
dst->pos.y = src1->pos.x*src2->right.y + src1->pos.y*src2->up.y + src1->pos.z*src2->at.y + src2->pos.y;
|
|
dst->pos.z = src1->pos.x*src2->right.z + src1->pos.y*src2->up.z + src1->pos.z*src2->at.z + src2->pos.z;
|
|
}
|
|
|
|
void
|
|
Matrix::invertOrthonormal(Matrix *dst, const Matrix *src)
|
|
{
|
|
dst->right.x = src->right.x;
|
|
dst->right.y = src->up.x;
|
|
dst->right.z = src->at.x;
|
|
dst->up.x = src->right.y;
|
|
dst->up.y = src->up.y;
|
|
dst->up.z = src->at.y;
|
|
dst->at.x = src->right.z;
|
|
dst->at.y = src->up.z;
|
|
dst->at.z = src->at.z;
|
|
dst->pos.x = -(src->pos.x*src->right.x +
|
|
src->pos.y*src->right.y +
|
|
src->pos.z*src->right.z);
|
|
dst->pos.y = -(src->pos.x*src->up.x +
|
|
src->pos.y*src->up.y +
|
|
src->pos.z*src->up.z);
|
|
dst->pos.z = -(src->pos.x*src->at.x +
|
|
src->pos.y*src->at.y +
|
|
src->pos.z*src->at.z);
|
|
dst->flags = TYPEORTHONORMAL;
|
|
}
|
|
|
|
Matrix*
|
|
Matrix::invertGeneral(Matrix *dst, const Matrix *src)
|
|
{
|
|
float32 det, invdet;
|
|
// calculate a few cofactors
|
|
dst->right.x = src->up.y*src->at.z - src->up.z*src->at.y;
|
|
dst->right.y = src->at.y*src->right.z - src->at.z*src->right.y;
|
|
dst->right.z = src->right.y*src->up.z - src->right.z*src->up.y;
|
|
// get the determinant from that
|
|
det = src->up.x * dst->right.y + src->at.x * dst->right.z + dst->right.x * src->right.x;
|
|
invdet = 1.0;
|
|
if(det != 0.0f)
|
|
invdet = 1.0f/det;
|
|
dst->right.x *= invdet;
|
|
dst->right.y *= invdet;
|
|
dst->right.z *= invdet;
|
|
dst->up.x = invdet * (src->up.z*src->at.x - src->up.x*src->at.z);
|
|
dst->up.y = invdet * (src->at.z*src->right.x - src->at.x*src->right.z);
|
|
dst->up.z = invdet * (src->right.z*src->up.x - src->right.x*src->up.z);
|
|
dst->at.x = invdet * (src->up.x*src->at.y - src->up.y*src->at.x);
|
|
dst->at.y = invdet * (src->at.x*src->right.y - src->at.y*src->right.x);
|
|
dst->at.z = invdet * (src->right.x*src->up.y - src->right.y*src->up.x);
|
|
dst->pos.x = -(src->pos.x*dst->right.x + src->pos.y*dst->up.x + src->pos.z*dst->at.x);
|
|
dst->pos.y = -(src->pos.x*dst->right.y + src->pos.y*dst->up.y + src->pos.z*dst->at.y);
|
|
dst->pos.z = -(src->pos.x*dst->right.z + src->pos.y*dst->up.z + src->pos.z*dst->at.z);
|
|
dst->flags &= ~IDENTITY;
|
|
return dst;
|
|
}
|
|
|
|
void
|
|
Matrix::makeRotation(Matrix *dst, const V3d *axis, float32 angle)
|
|
{
|
|
// V3d v = normalize(*axis);
|
|
float32 len = dot(*axis, *axis);
|
|
if(len != 0.0f) len = 1.0f/sqrtf(len);
|
|
V3d v = rw::scale(*axis, len);
|
|
angle = angle*(float)M_PI/180.0f;
|
|
float32 s = sinf(angle);
|
|
float32 c = cosf(angle);
|
|
float32 t = 1.0f - c;
|
|
|
|
dst->right.x = c + v.x*v.x*t;
|
|
dst->right.y = v.x*v.y*t + v.z*s;
|
|
dst->right.z = v.z*v.x*t - v.y*s;
|
|
dst->up.x = v.x*v.y*t - v.z*s;
|
|
dst->up.y = c + v.y*v.y*t;
|
|
dst->up.z = v.y*v.z*t + v.x*s;
|
|
dst->at.x = v.z*v.x*t + v.y*s;
|
|
dst->at.y = v.y*v.z*t - v.x*s;
|
|
dst->at.z = c + v.z*v.z*t;
|
|
dst->pos.x = 0.0;
|
|
dst->pos.y = 0.0;
|
|
dst->pos.z = 0.0;
|
|
dst->flags = TYPEORTHONORMAL;
|
|
}
|
|
|
|
/* q must be normalized */
|
|
void
|
|
Matrix::makeRotation(Matrix *dst, const Quat &q)
|
|
{
|
|
float xx = q.x*q.x;
|
|
float yy = q.y*q.y;
|
|
float zz = q.z*q.z;
|
|
float yz = q.y*q.z;
|
|
float zx = q.z*q.x;
|
|
float xy = q.x*q.y;
|
|
float wx = q.w*q.x;
|
|
float wy = q.w*q.y;
|
|
float wz = q.w*q.z;
|
|
|
|
dst->right.x = 1.0f - 2.0f*(yy + zz);
|
|
dst->right.y = 2.0f*(xy + wz);
|
|
dst->right.z = 2.0f*(zx - wy);
|
|
dst->up.x = 2.0f*(xy - wz);
|
|
dst->up.y = 1.0f - 2.0f*(xx + zz);
|
|
dst->up.z = 2.0f*(yz + wx);
|
|
dst->at.x = 2.0f*(zx + wy);
|
|
dst->at.y = 2.0f*(yz - wx);
|
|
dst->at.z = 1.0f - 2.0f*(xx + yy);
|
|
dst->pos.x = 0.0;
|
|
dst->pos.y = 0.0;
|
|
dst->pos.z = 0.0;
|
|
dst->flags = TYPEORTHONORMAL;
|
|
}
|
|
|
|
float32
|
|
Matrix::normalError(void)
|
|
{
|
|
float32 x, y, z;
|
|
x = dot(right, right) - 1.0f;
|
|
y = dot(up, up) - 1.0f;
|
|
z = dot(at, at) - 1.0f;
|
|
return x*x + y*y + z*z;
|
|
}
|
|
|
|
float32
|
|
Matrix::orthogonalError(void)
|
|
{
|
|
float32 x, y, z;
|
|
x = dot(at, up);
|
|
y = dot(at, right);
|
|
z = dot(up, right);
|
|
return x*x + y*y + z*z;
|
|
}
|
|
|
|
float32
|
|
Matrix::identityError(void)
|
|
{
|
|
V3d r = { right.x-1.0f, right.y, right.z };
|
|
V3d u = { up.x, up.y-1.0f, up.z };
|
|
V3d a = { at.x, at.y, at.z-1.0f };
|
|
return dot(r,r) + dot(u,u) + dot(a,a) + dot(pos,pos);
|
|
}
|
|
|
|
void
|
|
correctPathCase(char *filename)
|
|
{
|
|
#ifdef __unix__
|
|
DIR *direct;
|
|
struct dirent *dirent;
|
|
|
|
char *dir, *arg;
|
|
char copy[1024], sofar[1024] = ".";
|
|
strncpy(copy, filename, 1024);
|
|
arg = copy;
|
|
// hack for absolute paths
|
|
if(filename[0] == '/'){
|
|
sofar[0] = '/';
|
|
sofar[1] = '/';
|
|
sofar[2] = '\0';
|
|
arg++;
|
|
}
|
|
while((dir = strtok(arg, PSEP_S))){
|
|
arg = nil;
|
|
if(direct = opendir(sofar), dir == nil)
|
|
return;
|
|
while(dirent = readdir(direct), dirent != nil)
|
|
if(strncmp_ci(dirent->d_name, dir, 1024) == 0){
|
|
strncat(sofar, PSEP_S, 1024);
|
|
strncat(sofar, dirent->d_name, 1024);
|
|
break;
|
|
}
|
|
closedir(direct);
|
|
if(dirent == nil)
|
|
return;
|
|
}
|
|
strcpy(filename, sofar+2);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
makePath(char *filename)
|
|
{
|
|
size_t len = strlen(filename);
|
|
for(size_t i = 0; i < len; i++)
|
|
if(filename[i] == '/' || filename[i] == '\\')
|
|
filename[i] = PSEP_C;
|
|
#ifdef __unix__
|
|
correctPathCase(filename);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
memNative32_func(void *data, uint32 size)
|
|
{
|
|
uint8 *bytes = (uint8*)data;
|
|
uint32 *words = (uint32*)data;
|
|
size >>= 2;
|
|
while(size--){
|
|
*words++ = (uint32)bytes[0] | (uint32)bytes[1]<<8 |
|
|
(uint32)bytes[2]<<16 | (uint32)bytes[3]<<24;
|
|
bytes += 4;
|
|
}
|
|
}
|
|
|
|
void
|
|
memNative16_func(void *data, uint32 size)
|
|
{
|
|
uint8 *bytes = (uint8*)data;
|
|
uint16 *words = (uint16*)data;
|
|
size >>= 1;
|
|
while(size--){
|
|
*words++ = (uint16)bytes[0] | (uint16)bytes[1]<<8;
|
|
bytes += 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
memLittle32_func(void *data, uint32 size)
|
|
{
|
|
uint32 w;
|
|
uint8 *bytes = (uint8*)data;
|
|
uint32 *words = (uint32*)data;
|
|
size >>= 2;
|
|
while(size--){
|
|
w = *words++;
|
|
*bytes++ = w;
|
|
*bytes++ = w >> 8;
|
|
*bytes++ = w >> 16;
|
|
*bytes++ = w >> 24;
|
|
}
|
|
}
|
|
|
|
void
|
|
memLittle16_func(void *data, uint32 size)
|
|
{
|
|
uint16 w;
|
|
uint8 *bytes = (uint8*)data;
|
|
uint16 *words = (uint16*)data;
|
|
size >>= 1;
|
|
while(size--){
|
|
w = *words++;
|
|
*bytes++ = (uint8)w;
|
|
*bytes++ = w >> 8;
|
|
}
|
|
}
|
|
|
|
uint32
|
|
Stream::write32(const void *data, uint32 length)
|
|
{
|
|
#ifdef BIGENDIAN
|
|
uint8 *src = (uint8*)data;
|
|
uint32 buf[256];
|
|
int32 n, len;
|
|
for(len = length >>= 2; len > 0; len -= 256){
|
|
n = len < 256 ? len : 256;
|
|
memcpy(buf, src, n*4);
|
|
memLittle32(buf, n*4);
|
|
write8(buf, n*4);
|
|
src += n*4;
|
|
}
|
|
return length;
|
|
#else
|
|
return write8(data, length);
|
|
#endif
|
|
}
|
|
|
|
uint32
|
|
Stream::write16(const void *data, uint32 length)
|
|
{
|
|
#ifdef BIGENDIAN
|
|
uint8 *src = (uint8*)data;
|
|
uint16 buf[256];
|
|
int32 n, len;
|
|
for(len = length >>= 1; len > 0; len -= 256){
|
|
n = len < 256 ? len : 256;
|
|
memcpy(buf, src, n*2);
|
|
memLittle16(buf, n*2);
|
|
write8(buf, n*2);
|
|
src += n*2;
|
|
}
|
|
return length;
|
|
#else
|
|
return write8(data, length);
|
|
#endif
|
|
}
|
|
|
|
uint32
|
|
Stream::read32(void *data, uint32 length)
|
|
{
|
|
uint32 ret;
|
|
ret = read8(data, length);
|
|
memNative32(data, length);
|
|
return ret;
|
|
}
|
|
|
|
uint32
|
|
Stream::read16(void *data, uint32 length)
|
|
{
|
|
uint32 ret;
|
|
ret = read8(data, length);
|
|
memNative16(data, length);
|
|
return ret;
|
|
}
|
|
|
|
int32
|
|
Stream::writeI8(int8 val)
|
|
{
|
|
return write8(&val, sizeof(int8));
|
|
}
|
|
|
|
int32
|
|
Stream::writeU8(uint8 val)
|
|
{
|
|
return write8(&val, sizeof(uint8));
|
|
}
|
|
|
|
int32
|
|
Stream::writeI16(int16 val)
|
|
{
|
|
return write16(&val, sizeof(int16));
|
|
}
|
|
|
|
int32
|
|
Stream::writeU16(uint16 val)
|
|
{
|
|
return write16(&val, sizeof(uint16));
|
|
}
|
|
|
|
int32
|
|
Stream::writeI32(int32 val)
|
|
{
|
|
return write32(&val, sizeof(int32));
|
|
}
|
|
|
|
int32
|
|
Stream::writeU32(uint32 val)
|
|
{
|
|
return write32(&val, sizeof(uint32));
|
|
}
|
|
|
|
int32
|
|
Stream::writeF32(float32 val)
|
|
{
|
|
return write32(&val, sizeof(float32));
|
|
}
|
|
|
|
int8
|
|
Stream::readI8(void)
|
|
{
|
|
int8 tmp;
|
|
read8(&tmp, sizeof(int8));
|
|
return tmp;
|
|
}
|
|
|
|
uint8
|
|
Stream::readU8(void)
|
|
{
|
|
uint8 tmp;
|
|
read8(&tmp, sizeof(uint8));
|
|
return tmp;
|
|
}
|
|
|
|
int16
|
|
Stream::readI16(void)
|
|
{
|
|
int16 tmp;
|
|
read16(&tmp, sizeof(int16));
|
|
return tmp;
|
|
}
|
|
|
|
uint16
|
|
Stream::readU16(void)
|
|
{
|
|
uint16 tmp;
|
|
read16(&tmp, sizeof(uint16));
|
|
return tmp;
|
|
}
|
|
|
|
int32
|
|
Stream::readI32(void)
|
|
{
|
|
int32 tmp;
|
|
read32(&tmp, sizeof(int32));
|
|
return tmp;
|
|
}
|
|
|
|
uint32
|
|
Stream::readU32(void)
|
|
{
|
|
uint32 tmp;
|
|
read32(&tmp, sizeof(uint32));
|
|
return tmp;
|
|
}
|
|
|
|
float32
|
|
Stream::readF32(void)
|
|
{
|
|
float32 tmp;
|
|
read32(&tmp, sizeof(float32));
|
|
return tmp;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
StreamMemory::close(void)
|
|
{
|
|
}
|
|
|
|
uint32
|
|
StreamMemory::write8(const void *data, uint32 len)
|
|
{
|
|
if(this->eof())
|
|
return 0;
|
|
uint32 l = len;
|
|
if(this->position+l > this->length){
|
|
if(this->position+l > this->capacity)
|
|
l = this->capacity-this->position;
|
|
this->length = this->position+l;
|
|
}
|
|
memcpy(&this->data[this->position], data, l);
|
|
this->position += l;
|
|
if(len != l)
|
|
this->position = S_EOF;
|
|
return l;
|
|
}
|
|
|
|
uint32
|
|
StreamMemory::read8(void *data, uint32 len)
|
|
{
|
|
if(this->eof())
|
|
return 0;
|
|
uint32 l = len;
|
|
if(this->position+l > this->length)
|
|
l = this->length-this->position;
|
|
memcpy(data, &this->data[this->position], l);
|
|
this->position += l;
|
|
if(len != l)
|
|
this->position = S_EOF;
|
|
return l;
|
|
}
|
|
|
|
void
|
|
StreamMemory::seek(int32 offset, int32 whence)
|
|
{
|
|
if(whence == 0)
|
|
this->position = offset;
|
|
else if(whence == 1)
|
|
this->position += offset;
|
|
else
|
|
this->position = this->length-offset;
|
|
if(this->position > this->length){
|
|
// TODO: ideally this would depend on the mode
|
|
if(this->position > this->capacity)
|
|
this->position = S_EOF;
|
|
else
|
|
this->length = this->position;
|
|
}
|
|
}
|
|
|
|
uint32
|
|
StreamMemory::tell(void)
|
|
{
|
|
return this->position;
|
|
}
|
|
|
|
bool
|
|
StreamMemory::eof(void)
|
|
{
|
|
return this->position == S_EOF;
|
|
}
|
|
|
|
StreamMemory*
|
|
StreamMemory::open(uint8 *data, uint32 length, uint32 capacity)
|
|
{
|
|
this->data = data;
|
|
this->capacity = capacity;
|
|
this->length = length;
|
|
if(this->capacity < this->length)
|
|
this->capacity = this->length;
|
|
this->position = 0;
|
|
return this;
|
|
}
|
|
|
|
uint32
|
|
StreamMemory::getLength(void)
|
|
{
|
|
return this->length;
|
|
}
|
|
|
|
|
|
StreamFile*
|
|
StreamFile::open(const char *path, const char *mode)
|
|
{
|
|
assert(this->file == nil);
|
|
this->file = fopen(path, mode);
|
|
if(this->file == nil){
|
|
RWERROR((ERR_FILE, path));
|
|
return nil;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
void
|
|
StreamFile::close(void)
|
|
{
|
|
assert(this->file);
|
|
fclose(this->file);
|
|
this->file = nil;
|
|
}
|
|
|
|
uint32
|
|
StreamFile::write8(const void *data, uint32 length)
|
|
{
|
|
return (uint32)fwrite(data, 1, length, this->file);
|
|
}
|
|
|
|
uint32
|
|
StreamFile::read8(void *data, uint32 length)
|
|
{
|
|
return (uint32)fread(data, 1, length, this->file);
|
|
}
|
|
|
|
void
|
|
StreamFile::seek(int32 offset, int32 whence)
|
|
{
|
|
fseek(this->file, offset, whence);
|
|
}
|
|
|
|
uint32
|
|
StreamFile::tell(void)
|
|
{
|
|
return ftell(this->file);
|
|
}
|
|
|
|
bool
|
|
StreamFile::eof(void)
|
|
{
|
|
return ( feof(this->file) != 0 );
|
|
}
|
|
|
|
bool
|
|
writeChunkHeader(Stream *s, int32 type, int32 size)
|
|
{
|
|
struct {
|
|
int32 type, size;
|
|
uint32 id;
|
|
} buf = { type, size, libraryIDPack(version, build) };
|
|
s->write32(&buf, 12);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
readChunkHeaderInfo(Stream *s, ChunkHeaderInfo *header)
|
|
{
|
|
struct {
|
|
int32 type, size;
|
|
uint32 id;
|
|
} buf;
|
|
s->read32(&buf, 12);
|
|
if(s->eof())
|
|
return false;
|
|
assert(header != nil);
|
|
header->type = buf.type;
|
|
header->length = buf.size;
|
|
header->version = libraryIDUnpackVersion(buf.id);
|
|
header->build = libraryIDUnpackBuild(buf.id);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
findChunk(Stream *s, uint32 type, uint32 *length, uint32 *version)
|
|
{
|
|
ChunkHeaderInfo header;
|
|
while(readChunkHeaderInfo(s, &header)){
|
|
if(header.type == ID_NAOBJECT)
|
|
return false;
|
|
if(header.type == type){
|
|
if(length)
|
|
*length = header.length;
|
|
if(version)
|
|
*version = header.version;
|
|
return true;
|
|
}
|
|
s->seek(header.length);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int32
|
|
findPointer(void *p, void **list, int32 num)
|
|
{
|
|
int i;
|
|
for(i = 0; i < num; i++)
|
|
if(list[i] == p)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
uint8*
|
|
getFileContents(const char *name, uint32 *len)
|
|
{
|
|
FILE *cf = fopen(name, "rb");
|
|
if(cf == nil)
|
|
return nil;
|
|
fseek(cf, 0, SEEK_END);
|
|
*len = ftell(cf);
|
|
fseek(cf, 0, SEEK_SET);
|
|
uint8 *data = rwNewT(uint8, *len, MEMDUR_EVENT);
|
|
fread(data, 1, *len, cf);
|
|
fclose(cf);
|
|
return data;
|
|
}
|
|
|
|
}
|