Blitz3D C++ geometry code

Blitz3D Forums/Blitz3D Programming/Blitz3D C++ geometry code

marksibly(Posted 2005) [#1]
Hi,

I frequently get asked questions about Blitz3D's internal math routines.

So here, in all its glory, is the geom.h file from Blitz3D. It's in C++, so it may be a bit confusing, but the math should be pretty obvious.

Have fun - and please let me know if you find any bugs!

Mark

#ifndef GEOM_H
#define GEOM_H

#include <math.h>

class Vector;
class Line;
class Plane;
class Matrix;
class Transform;

const float PI=3.14159265359f;		//180 degrees
const float TWOPI=PI*2.0f;			//360 degrees
const float HALFPI=PI*.5f;			//90  degrees
const float QUARTERPI=PI*.25f;		//45  degrees
const float EPSILON=.000001f;		//small value
const float INFINITY=10000000.0f;	//big value

class Vector{
public:
	float x,y,z;

	Vector():x(0),y(0),z(0){
	}
	Vector( float x,float y,float z ):x(x),y(y),z(z){
	}
	operator float*(){
		return &x;
	}
	operator const float *(){
		return &x;
	}
	float &operator[]( int n ){
		return (&x)[n]; 
	}
	float operator[]( int n )const{
		return (&x)[n]; 
	}
	Vector operator-()const{
		return Vector( -x,-y,-z ); 
	}
	Vector operator*( float scale )const{
		return Vector( x*scale,y*scale,z*scale );
	}
	Vector operator*( const Vector &q )const{
		return Vector( x*q.x,y*q.y,z*q.z );
	}
	Vector operator/( float scale )const{
		return Vector( x/scale,y/scale,z/scale );
	}
	Vector operator/( const Vector &q )const{
		return Vector( x/q.x,y/q.y,z/q.z );
	}
	Vector operator+( const Vector &q )const{
		return Vector( x+q.x,y+q.y,z+q.z );
	}
	Vector operator-( const Vector &q )const{
		return Vector( x-q.x,y-q.y,z-q.z );
	}
	Vector &operator*=( float scale ){
		x*=scale;y*=scale;z*=scale;return *this;
	}
	Vector &operator*=( const Vector &q ){
		x*=q.x;y*=q.y;z*=q.z;return *this;
	}
	Vector &operator/=( float scale ){
		x/=scale;y/=scale;z/=scale;return *this;
	}
	Vector &operator/=( const Vector &q ){
		x/=q.x;y/=q.y;z/=q.z;return *this;
	}
	Vector &operator+=( const Vector &q ){
		x+=q.x;y+=q.y;z+=q.z;return *this;
	}
	Vector &operator-=( const Vector &q ){
		x-=q.x;y-=q.y;z-=q.z;return *this;
	}
	bool operator<( const Vector &q )const{
		if( fabs(x-q.x)>EPSILON ) return x<q.x ? true : false;
		if( fabs(y-q.y)>EPSILON ) return y<q.y ? true : false;
		return fabs(z-q.z)>EPSILON && z<q.z;
	}
	bool operator==( const Vector &q )const{
		return fabs(x-q.x)<=EPSILON && fabs(y-q.y)<=EPSILON && fabs(z-q.z)<=EPSILON;
	}
	bool operator!=( const Vector &q )const{
		return fabs(x-q.x)>EPSILON || fabs(y-q.y)>EPSILON || fabs(z-q.z)>EPSILON;
	}
	float dot( const Vector &q )const{
		return x*q.x+y*q.y+z*q.z;
	}
	Vector cross( const Vector &q )const{
		return Vector( y*q.z-z*q.y,z*q.x-x*q.z,x*q.y-y*q.x );
	}
	float length()const{
		return sqrtf(x*x+y*y+z*z);
	}
	float distance( const Vector &q )const{
		float dx=x-q.x,dy=y-q.y,dz=z-q.z;return sqrtf(dx*dx+dy*dy+dz*dz);
	}
	Vector normalized()const{
		float l=length();return Vector( x/l,y/l,z/l );
	}
	void normalize(){
		float l=length();x/=l;y/=l;z/=l;
	}
	float yaw()const{
		return -atan2f( x,z );
	}
	float pitch()const{
		return -atan2f( y,sqrtf( x*x+z*z ) );
	}
	void clear(){
		x=y=z=0;
	}
};

class Line{
public:
	Vector o,d;
	Line(){
	}
	Line( const Vector &o,const Vector &d ):o(o),d(d){
	}
	Line operator+( const Vector &q )const{
		return Line( o+q,d );
	}
	Line operator-( const Vector &q )const{
		return Line( o-q,d );
	}
	Vector operator*( float q )const{
		return o+d*q;
	}
	Vector nearest( const Vector &q )const{
		return o+d*(d.dot(q-o)/d.dot(d));
	}
};

class Plane{
public:
	Vector n;
	float d;

	Plane():d(0){
	}
	//normal/offset form
	Plane( const Vector &n,float d ):n(n),d(d){
	}
	//point/normal form
	Plane( const Vector &p,const Vector &n ):n(n),d(-n.dot(p)){
	}
	//create plane from tri
	Plane( const Vector &v0,const Vector &v1,const Vector &v2 ){
		n=(v1-v0).cross(v2-v0).normalized();d=-n.dot(v0);
	}
	Plane operator-()const{
		return Plane( -n,-d );
	}
	float t_intersect( const Line &q )const{
		return -distance(q.o)/n.dot(q.d);
	}
	Vector intersect( const Line &q )const{
		return q*t_intersect(q);
	}
	Line intersect( const Plane &q )const{
		Vector lv=n.cross( q.n ).normalized();
		return Line( q.intersect( Line( nearest( n*-d ),n.cross(lv) ) ),lv );
	}
	Vector nearest( const Vector &q )const{
		return q-n*distance(q);
	}
	void negate(){
		n=-n;d=-d;
	}
	float distance( const Vector &q )const{
		return n.dot(q)+d;
	}
};

struct Quat{
	float w;
	Vector v;
	Quat():w(1){
	}
	Quat( float w,const Vector &v ):w(w),v(v){
	}
	Quat operator-()const{
		return Quat( w,-v );
	}
	Quat operator+( const Quat &q )const{
		return Quat( w+q.w,v+q.v );
	}
	Quat operator-( const Quat &q )const{
		return Quat( w-q.w,v-q.v );
	}
	Quat operator*( const Quat &q )const{
		return Quat( w*q.w-v.dot(q.v),q.v.cross(v)+q.v*w+v*q.w );
	}
	Vector operator*( const Vector &q )const{
		return (*this * Quat(0,q) * -*this).v;
	}
	Quat operator*( float q )const{
		return Quat( w*q,v*q );
	}
	Quat operator/( float q )const{
		return Quat( w/q,v/q );
	}
	float dot( const Quat &q )const{
		return v.x*q.v.x+v.y*q.v.y+v.z*q.v.z+w*q.w;
	}
	float length()const{
		return sqrtf( w*w+v.x*v.x+v.y*v.y+v.z*v.z );
	}
	void normalize(){
		*this=*this/length();
	}
	Quat normalized()const{
		return *this/length();
	}
	Quat slerpTo( const Quat &q,float a )const{
		Quat t=q;
		float d=dot(q),b=1-a;
		if( d<0 ){ t.w=-t.w;t.v=-t.v;d=-d; }
		if( d<1-EPSILON ){
			float om=acosf( d );
			float si=sinf( om );
			a=sinf( a*om )/si;
			b=sinf( b*om )/si;
		}
		return *this*b + t*a;
	}
	Vector i()const{
		float xz=v.x*v.z,wy=w*v.y;
		float xy=v.x*v.y,wz=w*v.z;
		float yy=v.y*v.y,zz=v.z*v.z;
		return Vector( 1-2*(yy+zz),2*(xy-wz),2*(xz+wy) );
	}
	Vector j()const{
		float yz=v.y*v.z,wx=w*v.x;
		float xy=v.x*v.y,wz=w*v.z;
		float xx=v.x*v.x,zz=v.z*v.z;
		return Vector( 2*(xy+wz),1-2*(xx+zz),2*(yz-wx) );
	}
	Vector k()const{
		float xz=v.x*v.z,wy=w*v.y;
		float yz=v.y*v.z,wx=w*v.x;
		float xx=v.x*v.x,yy=v.y*v.y;
		return Vector( 2*(xz-wy),2*(yz+wx),1-2*(xx+yy) );
	}
};

class Matrix{
	static Matrix tmps[64];
	static Matrix &alloc_tmp(){ static int tmp=0;return tmps[tmp++&63];	}
	friend class Transform;
public:
	Vector i,j,k;

	Matrix():i(Vector(1,0,0)),j(Vector(0,1,0)),k(Vector(0,0,1)){
	}
	Matrix( const Vector &i,const Vector &j,const Vector &k ):i(i),j(j),k(k){
	}
	Matrix( const Quat &q ){
		float xx=q.v.x*q.v.x,yy=q.v.y*q.v.y,zz=q.v.z*q.v.z;
		float xy=q.v.x*q.v.y,xz=q.v.x*q.v.z,yz=q.v.y*q.v.z;
		float wx=q.w*q.v.x,wy=q.w*q.v.y,wz=q.w*q.v.z;
		i=Vector( 1-2*(yy+zz),2*(xy-wz),2*(xz+wy) ),
		j=Vector( 2*(xy+wz),1-2*(xx+zz),2*(yz-wx) ),
		k=Vector( 2*(xz-wy),2*(yz+wx),1-2*(xx+yy) );
	}
	Matrix( float angle,const Vector &axis ){
		const Vector &u=axis;
		float c=cosf(angle),s=sinf(angle);
		float x2=axis.x*axis.x,y2=axis.y*axis.y,z2=axis.z*axis.z;
		i=Vector( x2+c*(1-x2),u.x*u.y*(1-c)-u.z*s,u.z*u.x*(1-c)+u.y*s );
		j=Vector( u.x*u.y*(1-c)+u.z*s,y2+c*(1-y2),u.y*u.z*(1-c)-u.x*s );
		k=Vector( u.z*u.x*(1-c)-u.y*s,u.y*u.z*(1-c)+u.x*s,z2+c*(1-z2) );
	}
	Vector &operator[]( int n ){
		return (&i)[n]; 
	}
	const Vector &operator[]( int n )const{
		return (&i)[n];
	}
	Matrix &operator~()const{
		Matrix &m=alloc_tmp();
		m.i.x=i.x;m.i.y=j.x;m.i.z=k.x;
		m.j.x=i.y;m.j.y=j.y;m.j.z=k.y;
		m.k.x=i.z;m.k.y=j.z;m.k.z=k.z;
		return m;
	}
	float determinant()const{
		return i.x*(j.y*k.z-j.z*k.y )-i.y*(j.x*k.z-j.z*k.x )+i.z*(j.x*k.y-j.y*k.x );
	}
	Matrix &operator-()const{
		Matrix &m=alloc_tmp();
		float t=1.0f/determinant();
		m.i.x= t*(j.y*k.z-j.z*k.y);m.i.y=-t*(i.y*k.z-i.z*k.y);m.i.z= t*(i.y*j.z-i.z*j.y);
		m.j.x=-t*(j.x*k.z-j.z*k.x);m.j.y= t*(i.x*k.z-i.z*k.x);m.j.z=-t*(i.x*j.z-i.z*j.x);
		m.k.x= t*(j.x*k.y-j.y*k.x);m.k.y=-t*(i.x*k.y-i.y*k.x);m.k.z= t*(i.x*j.y-i.y*j.x);
		return m;
	}
	Matrix &cofactor()const{
		Matrix &m=alloc_tmp();
		m.i.x= (j.y*k.z-j.z*k.y);m.i.y=-(j.x*k.z-j.z*k.x);m.i.z= (j.x*k.y-j.y*k.x);
		m.j.x=-(i.y*k.z-i.z*k.y);m.j.y= (i.x*k.z-i.z*k.x);m.j.z=-(i.x*k.y-i.y*k.x);
		m.k.x= (i.y*j.z-i.z*j.y);m.k.y=-(i.x*j.z-i.z*j.x);m.k.z= (i.x*j.y-i.y*j.x);
		return m;
	}
	bool operator==( const Matrix &q )const{
		return i==q.i && j==q.j && k==q.k;
	}
	bool operator!=( const Matrix &q )const{
		return i!=q.i || j!=q.j || k!=q.k;
	}
	Vector operator*( const Vector &q )const{
		return Vector( i.x*q.x+j.x*q.y+k.x*q.z,i.y*q.x+j.y*q.y+k.y*q.z,i.z*q.x+j.z*q.y+k.z*q.z );
	}
	Matrix &operator*( const Matrix &q )const{
		Matrix &m=alloc_tmp();
		m.i.x=i.x*q.i.x+j.x*q.i.y+k.x*q.i.z;m.i.y=i.y*q.i.x+j.y*q.i.y+k.y*q.i.z;m.i.z=i.z*q.i.x+j.z*q.i.y+k.z*q.i.z;
		m.j.x=i.x*q.j.x+j.x*q.j.y+k.x*q.j.z;m.j.y=i.y*q.j.x+j.y*q.j.y+k.y*q.j.z;m.j.z=i.z*q.j.x+j.z*q.j.y+k.z*q.j.z;
		m.k.x=i.x*q.k.x+j.x*q.k.y+k.x*q.k.z;m.k.y=i.y*q.k.x+j.y*q.k.y+k.y*q.k.z;m.k.z=i.z*q.k.x+j.z*q.k.y+k.z*q.k.z;
		return m;
	}
	void orthogonalize(){
		k.normalize();
		i=j.cross( k ).normalized();
		j=k.cross( i );
	}
	Matrix &orthogonalized()const{
		Matrix &m=alloc_tmp();
		m=*this;m.orthogonalize();
		return m;
	}
};

class Box{
public:
	Vector a,b;
	Box():a( Vector(INFINITY,INFINITY,INFINITY) ),b( Vector(-INFINITY,-INFINITY,-INFINITY) ){
	}
	Box( const Vector &q ):a(q),b(q){
	}
	Box( const Vector &a,const Vector &b ):a(a),b(b){
	}
	Box( const Line &l ):a(l.o),b(l.o){
		update( l.o+l.d );
	}
	void clear(){
		a.x=a.y=a.z=INFINITY;
		b.x=b.y=b.z=-INFINITY;
	}
	bool empty()const{
		return b.x<a.x || b.y<a.y || b.z<a.z;
	}
	Vector centre()const{
		return Vector( (a.x+b.x)*.5f,(a.y+b.y)*.5f,(a.z+b.z)*.5f );
	}
	Vector corner( int n )const{
		return Vector( ((n&1)?b:a).x,((n&2)?b:a).y,((n&4)?b:a).z );
	}
	void update( const Vector &q ){
		if( q.x<a.x ) a.x=q.x;if( q.y<a.y ) a.y=q.y;if( q.z<a.z ) a.z=q.z;
		if( q.x>b.x ) b.x=q.x;if( q.y>b.y ) b.y=q.y;if( q.z>b.z ) b.z=q.z;
	}
	void update( const Box &q ){
		if( q.a.x<a.x ) a.x=q.a.x;if( q.a.y<a.y ) a.y=q.a.y;if( q.a.z<a.z ) a.z=q.a.z;
		if( q.b.x>b.x ) b.x=q.b.x;if( q.b.y>b.y ) b.y=q.b.y;if( q.b.z>b.z ) b.z=q.b.z;
	}
	bool overlaps( const Box &q )const{
		return
		(b.x<q.b.x?b.x:q.b.x)>=(a.x>q.a.x?a.x:q.a.x) &&
		(b.y<q.b.y?b.y:q.b.y)>=(a.y>q.a.y?a.y:q.a.y) &&
		(b.z<q.b.z?b.z:q.b.z)>=(a.z>q.a.z?a.z:q.a.z);
	}
	void expand( float n ){
		a.x-=n;a.y-=n;a.z-=n;b.x+=n;b.y+=n;b.z+=n;
	}
	float width()const{
		return b.x-a.x;
	}
	float height()const{
		return b.y-a.y;
	}
	float depth()const{
		return b.z-a.z;
	}
	bool contains( const Vector &q ){
		return q.x>=a.x && q.x<=b.x && q.y>=a.y && q.y<=b.y && q.z>=a.z && q.z<=b.z;
	}
};

class Transform{
	static Transform tmps[64];
	static Transform &alloc_tmp(){ static int tmp=0;return tmps[tmp++&63]; }
public:
	Matrix m;
	Vector v;

	Transform(){
	}
	Transform( const Matrix &m ):m(m){
	}
	Transform( const Vector &v ):v(v){
	}
	Transform( const Matrix &m,const Vector &v ):m(m),v(v){
	}
	Transform &operator-()const{
		Transform &t=alloc_tmp();
		t.m=-m;t.v=t.m*-v;
		return t;
	}
	Transform &operator~()const{
		Transform &t=alloc_tmp();
		t.m=~m;t.v=t.m*-v;
		return t;
	}
	Vector operator*( const Vector &q )const{
		return m*q+v;
	}
	Line operator*( const Line &q )const{
		Vector t=(*this)*q.o;
		return Line( t,(*this)*(q.o+q.d)-t );
	}
	Box operator*( const Box &q )const{
		Box t( (*this*q.corner(0) ) );
		for( int k=1;k<8;++k ) t.update( *this*q.corner(k) );
		return t;
	}
	Transform &operator*( const Transform &q )const{
		Transform &t=alloc_tmp();
		t.m=m*q.m;t.v=m*q.v+v;
		return t;
	}
	bool operator==( const Transform &q )const{
		return m==q.m && v==q.v;
	}
	bool operator!=( const Transform &q )const{
		return !operator==( q );
	}
};

inline float transformRadius( float r,const Matrix &t ){
	static const float sq_3=sqrtf(1.0f/3.0f);
	return (t * Vector( sq_3,sq_3,sq_3 )).length()*r;
}

inline Matrix pitchMatrix( float q ){
	return Matrix( Vector(1,0,0),Vector(0,cosf(q),sinf(q)),Vector(0,-sinf(q),cosf(q)) );
}

inline Matrix yawMatrix( float q ){
	return Matrix( Vector(cosf(q),0,sinf(q)),Vector(0,1,0),Vector(-sinf(q),0,cosf(q)) );
}

inline Matrix rollMatrix( float q ){
	return Matrix( Vector(cosf(q),sinf(q),0),Vector(-sinf(q),cosf(q),0),Vector(0,0,1) );
}

inline float matrixPitch( const Matrix &m ){
	return m.k.pitch();
}

inline float matrixYaw( const Matrix &m ){
	return m.k.yaw();
}

inline float matrixRoll( const Matrix &m ){
	return atan2f( m.i.y,m.j.y );
}

inline Matrix scaleMatrix( float x,float y,float z ){
	return Matrix( Vector( x,0,0 ),Vector( 0,y,0 ),Vector( 0,0,z ) );
}

inline Matrix scaleMatrix( const Vector &scale ){
	return Matrix( Vector( scale.x,0,0 ),Vector( 0,scale.y,0 ),Vector( 0,0,scale.z ) );
}

inline Quat pitchQuat( float p ){
	return Quat( cosf(p/-2),Vector( sinf(p/-2),0,0 ) );
}

inline Quat yawQuat( float y ){
	return Quat( cosf(y/2),Vector( 0,sinf(y/2),0 ) );
}

inline Quat rollQuat( float r ){
	return Quat( cosf(r/-2),Vector( 0,0,sinf(r/-2) ) );
}

inline Matrix rotationMatrix( float p,float y,float r ){
	return yawMatrix(y)*pitchMatrix(p)*rollMatrix(r);
}

inline Matrix rotationMatrix( const Vector &rot ){
	return yawMatrix(rot.y)*pitchMatrix(rot.x)*rollMatrix(rot.z);
}

inline float quatPitch( const Quat &q ){
	return q.k().pitch();
}

inline float quatYaw( const Quat &q ){
	return q.k().yaw();
}

inline float quatRoll( const Quat &q ){
	return matrixRoll( q );
}

inline Quat matrixQuat( const Matrix &p ){
	Matrix m=p;
	m.orthogonalize();
	float t=m.i.x+m.j.y+m.k.z,w,x,y,z;
	if( t>EPSILON ){
		t=sqrtf( t+1 )*2;
		x=(m.k.y-m.j.z)/t;
		y=(m.i.z-m.k.x)/t;
		z=(m.j.x-m.i.y)/t;
		w=t/4;
	}else if( m.i.x>m.j.y && m.i.x>m.k.z ){
		t=sqrtf( m.i.x-m.j.y-m.k.z+1 )*2;
		x=t/4;
		y=(m.j.x+m.i.y)/t;
		z=(m.i.z+m.k.x)/t;
		w=(m.k.y-m.j.z)/t;
	}else if( m.j.y>m.k.z ){
		t=sqrtf( m.j.y-m.k.z-m.i.x+1 )*2;
		x=(m.j.x+m.i.y)/t;
		y=t/4;
		z=(m.k.y+m.j.z)/t;
		w=(m.i.z-m.k.x)/t;
	}else{
		t=sqrtf( m.k.z-m.j.y-m.i.x+1 )*2;
		x=(m.i.z+m.k.x)/t;
		y=(m.k.y+m.j.z)/t;
		z=t/4;
		w=(m.j.x-m.i.y)/t;
	}
	return Quat( w,Vector( x,y,z ) );
}

#endif



Tom(Posted 2005) [#2]
Thank you very much!

Tom
p.s. More more more!!! :)


fredborg(Posted 2005) [#3]
Thank you!

PS: I agree with Toms PS :)


JoshK(Posted 2005) [#4]
Wow, sweet.

Does Blitz store 3x3 matrices? I've been storing Eulers, converting to quats, transforming the necessary mat elements, and converting back to eulers. It might be faster to store a 3x3 matrix, and use gl matrix commands instead of glRotatef().

But then, I finally got my transformations working (using Blitz3D results as a guide) so maybe I should leave it damn well alone.


slenkar(Posted 2005) [#5]
Lucky I never tried to make my own 3D engine, (looks a bit complicated!)


SoggyP(Posted 2005) [#6]
Greetings Puppies,

Nice job Mark. I can understand the code but have no idea what it actually means :0)

Peace,

Jes


John Blackledge(Posted 2005) [#7]
Now I remember why I'm quite happy to pay Mark Sibly whatever he charges, and just walk away with the result!
Cheers, Mark.


Picklesworth(Posted 2005) [#8]
Thanks Mark
I'm always very impressed by code that I can not understand :D


wizzlefish(Posted 2005) [#9]
Me too! :P


JoshK(Posted 2005) [#10]
It would be VERY useful if someone converted this to Blitz, for all the people doing OpenGL stuff.


WolRon(Posted 2005) [#11]
I'm kind of confused. I thought header files weren't really intended to contain the implementation (except for Inlines), but it appears that that's how Mark uses them.


Braincell(Posted 2005) [#12]
Awesome! Its a great beginning Mark, hopefully throughout the years you'll keep 'em coming. Nice to see the true faces of all those commands we use. Not trivial stuff.


RiverRatt(Posted 2005) [#13]
What is and ESPILON?


DJWoodgate(Posted 2005) [#14]
It is the fifth letter of the Greek alphabet. Apparently first used by the Hungarian mathmetician Paul Erdos to denote a very small or insignificant quantity.

In this case it is a very small value, that is still representable using the floating point numbering system. It is used to determine if two numbers are approximately equal, because floating point numbers usually accumulate small rounding errors which make exact comparisons unlikely to suceeed. So here Epsilon is just about significant, and anything less than Epsilon is deemed to be insignificant

I guess all these Greek letters get used in maths because the Greeks were apparently rather good at it. So I am pretty sure I have no Greek ancestors. In any event If that is the only question you have about the above then you are a lot Greeker than me.


WolRon(Posted 2005) [#15]
Did he say Greeker or geekier? :)


Tom(Posted 2005) [#16]
hehe :)

Here's me being 'geek'

<Sarge> scouse did you see the tutorial i sent
<Scouse> no sir
<Sarge> i will send it to you in a bit
<Scouse> wow, what kind of compression is that?
<Scouse> nerd joke, sorry :P

Tom


DJWoodgate(Posted 2005) [#17]
Very good. Try this.
http://www.cs.bris.ac.uk/Research/QuantumComputing/compression.html

"Although compression for sources of pure state is now quite well understood,..."
Not by us non Greeks it isn't. :(


poopla(Posted 2005) [#18]
Oh hell, you are a saint Mark :). Thanks a ton!


morduun(Posted 2005) [#19]
LMAO @ Tom XD


N(Posted 2005) [#20]
Out of curiosity, are you making this source public domain or is there a specific license you want people to adhere to?


JoshK(Posted 2005) [#21]
I don't think Mark has patented basic 3D maths.


BlackD(Posted 2005) [#22]
I don't think Mark has patented basic 3D maths.

Yeah - someone else already tried that. :p


N(Posted 2005) [#23]
I patented eye balls. Pay up or I cut them out ;)


ShadowTurtle(Posted 2005) [#24]
btw, blitz3d source code...

i want a good compiler sdk for add more cool features in compiling


marksibly(Posted 2005) [#25]

Out of curiosity, are you making this source public domain or is there a specific license you want people to adhere to?



This code is completely public domain - do whatever you want with it!


poopla(Posted 2005) [#26]
Oh lord, now you've done it... Noel will surely defile it.


ckob(Posted 2005) [#27]
sweet :)

now if we could get you to release the rest of the blitz3d code :P


N(Posted 2005) [#28]
"Right now I could stand to hug you. I won't. But you know what I mean."

Futurama quotes are nice.


(tu) ENAY(Posted 2005) [#29]
I'm pleased to see the use of
return *this.

Nice and fast.

You 'could' save a few nano seconds by converting your math labels to #DEFINE's

eg.

const float PI=3.14159265359f;//180 degrees

#DEFINE PI=3.14159265359f;//180 degrees


boomboom(Posted 2005) [#30]
you know the old saying, take care of the nano seconds and the seconds will take care of themselves.


Ruz(Posted 2005) [#31]
I understood everything up until the green lettering.


shamanenCoder(Posted 2005) [#32]
thx thx thx mark!!!


Sir Gak(Posted 2005) [#33]
As long as we're talking about Greek letters (like EPSILON), why not replace "INFINITY" with "OMEGA", the Greek letter normally used to represent Infinity? :)


Sweenie(Posted 2005) [#34]
Small question.

The Transform-class has a method called QuatRoll.
This method calls the Matrixroll method passing a Quat as parameter, but the Matrixroll method is defined to only accept a Matrix.
Is this wrong or is it just me that doesn't get it?


flying willy(Posted 2005) [#35]
I'd like to find out how to convert quat to euler using the above code. I will then impliment it in the ode wrapper.


DJWoodgate(Posted 2005) [#36]
Flying Willy, See the matrix class. My guess is that this is the quat to euler rotation matrix conversion. (as below - the matrix itself is defined as the three vectors, i,j,k)

Sweenie, I do not use C++ (seems I am going to have to make the effort - if only to make sense of this sort of stuff) but maybe the code is using this to implicitly cast a quat to a matrix?

  	Matrix( const Quat &q ){
		float xx=q.v.x*q.v.x,yy=q.v.y*q.v.y,zz=q.v.z*q.v.z;
		float xy=q.v.x*q.v.y,xz=q.v.x*q.v.z,yz=q.v.y*q.v.z;
		float wx=q.w*q.v.x,wy=q.w*q.v.y,wz=q.w*q.v.z;
		i=Vector( 1-2*(yy+zz),2*(xy-wz),2*(xz+wy) ),
		j=Vector( 2*(xy+wz),1-2*(xx+zz),2*(yz-wx) ),
		k=Vector( 2*(xz-wy),2*(yz+wx),1-2*(xx+yy) );
        }

There is also some stuff on the code archives dealing with quats. Not tested though, I have never used them directly.


Sweenie(Posted 2005) [#37]
Aha, you mean that if a function "ask" for a reference to a matrix and you instead pass a quaternion as reference, the matrix constructor above will kick in and convert it for you.
I never knew that could be done, but then again , my C++ knowledge is pretty limited as well.


EddieRay(Posted 2005) [#38]
The above code looks like a version of the constructor for a Matrix object that initializes the i,j,k vectors of the Matrix object from the given Quat object. I'm guessing it is just computing the rotation matrix that corresponds to the quaternion... but I don't know enough about quaternions to verify that statement... ;-)

Hope this helps...


Jeroen(Posted 2005) [#39]
I really don't understand the math that's going on!


Drago(Posted 2005) [#40]
yeah, that is one of the advantages (or disadvantages since it makes a temp var that may not be released at all.) of C++, since what that will compile to is basically this.

MatrixRoll(Matrix(q));
' which the compiler then changes to
Matrix tmp = Matrix(q);
MatrixRoll(tmp);


which is why some programmers, make sure they do the conversion themselves, since then they can keep account of ALL vars that are created.


SkyCube(Posted 2005) [#41]
Guess this is nice for the real "hardcore" 3d math kind of people. Don't get me wrong I like trigonometry (especially when using for game development), but I don't think I would be able to program it like Mark does here.

My whole reason for using Blitz was to not have to worry about this type of thing (no offense intended).


my_name_is_alex_hello__(Posted 2005) [#42]
Its great that you are posting up source code from BB3D like that Mark, it would be greater if I could understand it, but thats a seperate issue...


EddieRay(Posted 2005) [#43]
@Drago: You said it bruthah! I hate this aspect of C++, and this is the typical and accepted way people use the language all the time. I steer clear of big C++ class libraries because of this sort of nonsense. And, don't even get me started on C++ "templates" or the "let's try (and fail) to code our way around the fundamental shortcomings of C++" that is called "STL" (the Standard Templates Library). LOL!


aab(Posted 2005) [#44]
This is exactly what im doing in maths right now...
neat!


podperson(Posted 2005) [#45]
1) Epsilon is often used in mathematics to represent an arbitrary small number. Possibly the most commonly written sentence fragment in analysis is: "For every epsilon > 0, there exists delta such that..." (substitute the actual epsilon and delta characters, hastily scrawled in chalk, for additional realism).

Paul Erdos did not invent this convention (which is far older than he) but is, apparently, known for referring to children as epsilons.

2) Omega is not used to represent infinity (not that I've seen, anyway). Usually infinity is represented by:

Aleph (hebrew letter) with a numerical subscript.
The infinity symbol (sideways 8), although this is almost always used to represent countable infinity (Aleph 0)
The italic c (for continuum), representing the degree of infiniteness of the Real Numbers (which is at least Aleph 1).

3) Very elegant C++ -- I love it.


Damien Sturdy(Posted 2005) [#46]
He's a better programmer than me, thats for sure.

I got lost at
#ifndef GEOM_H

:P


Just kidding- but i never got much farther...


AntonyWells(Posted 2005) [#47]
Anyone had any luck getting this to compile?

I tried to import it in Bmax, and got about a dozen parse errors.(MingW is set up.)


LAB[au](Posted 2005) [#48]
It compiles fine in visual studio.


FredMe(Posted 2005) [#49]
Thank you to show us this part of code,
it shows the IDE is very good coded.


Dark Ray(Posted 2005) [#50]
Yeah, Thanks!


poopla(Posted 2006) [#51]
Huh, comming back and looking at this now I understand everything but the quats.

I get them a little but require more study to make any use of them :).


aab(Posted 2006) [#52]
Same here: Apart from Quaternions the adventurous high school student can make sense of the code.
I know 'of' them though..and their use in interpolating a coordinate through a spline. The imaginery side of maths is quite something.


RGR(Posted 2006) [#53]
Quaternions in most Animation Files do not need the multi line code examples you usually find (and most of them do not function anyway, endless formulas and Codelines produce wrong results - at least when I wanted to convert *.wrl or *.LiA files to B3D)
After some try and error tests I found this:
For instance to convert a Quaternion of the animation keys from a VRML-File (extension .wrl) to use it on a *.b3d file, all you need is this:
w#=w#*180/Pi
pitch#=x#*w#
yaw#=-y#*w#
roll#=z#*w#

or
w#=w#*180/Pi
RotateEntity bone,x#*w#,-y#*w#,z#*w#

After years of research I'm convinced the Quaternion thing is just a Hoax ;-)
.


sonokong(Posted 2007) [#54]
Am I the only one who doesn't understand all the code?


Warren(Posted 2007) [#55]
You're about 8 months too slow with that "joke".


sonokong(Posted 2007) [#56]
You would be right if it actually WAS a joke. :)
Although it's 8 months slow, it still is related to the other posts.


sonokong(Posted 2007) [#57]
So there!
pwned


Oddball(Posted 2007) [#58]
I've just spent the last few hours scouring nigh on every math site on the net, and wouldn't you know it, the equation I was after could be found here all along. That'll teach me to go looking elsewhere without checking here first. Thanks for the code.


simonh(Posted 2007) [#59]
Here's a BMax version:

Const TWOPI:Float=Pi*2.0		'360 degrees
Const HALFPI:Float=Pi*.5		'90  degrees
Const QUARTERPI:Float=Pi*.25	'45  degrees
Const EPSILON:Float=.000001		'small value
Const INFINITY:Float=10000000.0	'big value

Type TVec

	Field x:Float,y:Float,z:Float

	Method Copy:TVec()
		Local q:TVec=New TVec
		q.x=x; q.y=y; q.z=z
		Return q
	End Method

	Function Create0:TVec()
		Local q:TVec=New TVec
		q.x=0.0; q.y=0.0; q.z=0.0
		Return q
	End Function
	
	Function Create:TVec(x#,y#,z#)
		Local q:TVec=New TVec
		q.x=x#; q.y=y#; q.z=z#
		Return q
	End Function
	
	Rem
	operator Float*() ' ? operator overload
		Return &x;
	}
	
	operator Const Float *() ' ? operator overload
		Return &x;
	}
	
	Float &operator[]( Int n ) ' ? operator overload
		Return (&x)[n]; 
	}
	
	Float operator[]( Int n ) ' ? operator overload
		Return (&x)[n]; 
	}
	End Rem
	
	Method Negate:TVec() ' - operator overload
		Return TVec.Create( -x,-y,-z )
	End Method
	
	Method Multiply:TVec( scale:Float ) ' * operator overload
		Return TVec.Create( x*scale,y*scale,z*scale )
	End Method
	
	Method MultiplyVector:TVec( q:TVec ) ' * operator overload
		Return TVec.Create( x*q.x,y*q.y,z*q.z )
	End Method
	
	Method Divide:TVec( scale:Float ) ' / operator overload
		Return TVec.Create( x/scale,y/scale,z/scale )
	End Method
	
	Method DivideVector:TVec( q:TVec ) ' / operator overload
		Return TVec.Create( x/q.x,y/q.y,z/q.z )
	End Method
	
	Method Add:TVec( q:TVec ) ' + operator overload
		Return TVec.Create( x+q.x,y+q.y,z+q.z )
	End Method
	
	Method Subtract:TVec( q:TVec ) ' - operator overload
		Return TVec.Create( x-q.x,y-q.y,z-q.z )
	End Method
	
	Rem
	Vector &operator*=( Float scale ) ' *= operator overload
		x*=scale;y*=scale;z*=scale;Return *this;
	}
	Vector &operator*=( Const Vector &q ) ' *= operator overload
		x*=q.x;y*=q.y;z*=q.z;Return *this;
	}
	Vector &operator/=( Float scale ) ' /= operator overload
		x/=scale;y/=scale;z/=scale;Return *this;
	}
	Vector &operator/=( Const Vector &q ) ' /= operator overload
		x/=q.x;y/=q.y;z/=q.z;Return *this;
	}
	End Rem
	
	Method Inc( q:TVec ) ' += operator overload
		x:+q.x;y:+q.y;z:+q.z
	End Method
	
	Method Dec( q:TVec ) ' -= operator overload
		x:-q.x;y:-q.y;z:-q.z
	End Method
	
	Method LessThan:Int( q:TVec ) ' < operator overload
		If( Abs(x-q.x)>EPSILON )
			If x<q.x Then Return True Else Return False
		EndIf
		If( Abs(y-q.y)>EPSILON )
			If y<q.y Then Return True Else Return False
		EndIf
		Return Abs(z-q.z)>EPSILON And z<q.z
	End Method
	
	Method Equals:Int( q:TVec ) ' = operator overload
		Return Abs(x-q.x)<=EPSILON And Abs(y-q.y)<=EPSILON And Abs(z-q.z)<=EPSILON
	End Method
	
	Method Inequality:Int( q:TVec ) ' != operator overload
		Return Abs(x-q.x)>EPSILON Or Abs(y-q.y)>EPSILON Or Abs(z-q.z)>EPSILON
	End Method
	
	Method Dot:Float( q:TVec )
		Return x*q.x+y*q.y+z*q.z
	End Method
	
	Method Cross:TVec( q:TVec )
		Return TVec.Create( y*q.z-z*q.y,z*q.x-x*q.z,x*q.y-y*q.x )
	End Method
	
	Method Length:Float()
		Return Sqr(x*x+y*y+z*z)
	End Method
	
	Method Distance:Float( q:TVec )
		Local dx:Float=x-q.x; Local dy:Float=y-q.y; Local dz:Float=z-q.z; Return Sqr(dx*dx+dy*dy+dz*dz)
	End Method
	
	Method Normalized:TVec()
		Local l:Float=Length(); Return TVec.Create( x/l,y/l,z/l )
	End Method
	
	Method Normalize()
		Local l:Float=Length(); x:/l; y:/l; z:/l
	End Method
	
	Method Yaw:Float()
		Return -ATan2( x,z )
	End Method
	
	Method Pitch:Float()
		Return -ATan2( y,Sqr( x*x+z*z ) )
	End Method
	
	Method Clear()
		x=0;y=0;z=0
	End Method
	
End Type

Type TLine

	Field o:TVec,d:TVec
	
	Function Create0:TLine()
		Local line:TLine=New Tline
		line.o=TVec.Create(0.0,0.0,0.0)
		line.d=TVec.Create(0.0,0.0,0.0)
		Return line
	End Function

	Function Create:TLine(o:TVec,d:TVec)
		Local line:TLine=New Tline
		line.o=TVec.Create(o.x,o.y,o.z)
		line.d=TVec.Create(d.x,d.y,d.z)
		Return line
	End Function
	
	Method Add:TLine( q:TVec ) ' + operator overload
		Return Create( o.Add(q),d );
	End Method
	
	Method Subtract:TLine( q:TVec ) ' - operator overload
		Return Create( o.Subtract(q),d );
	End Method
	
	Method Multiply:TVec( q:Float ) ' * operator overload
		Return o.Add(d.Multiply(q))
	End Method
	
	Method Nearest:TVec( q:TVec )
		Return o.Add( d.Multiply( ( d.Dot(q.Subtract(o)) / d.Dot(d) ) ) )
	End Method
	
End Type

Type TPlane

	Field n:TVec
	Field d:Float
	
	Method Copy:TPlane()
		Local plane:TPlane=New TPlane
		plane.n=TVec.Create(n.x,n.y,n.z)
		plane.d=d
		Return plane
	End Method

	Function Create0:TPlane()
		Local plane:TPlane=New TPlane
		plane.n=TVec.Create(0.0,0.0,0.0)
		plane.d=0.0
		Return plane
	End Function

	' normal/offset form
	Function Create:TPlane(n:TVec,d:Float)
		Local plane:TPlane=New TPlane
		plane.n=TVec.Create(n.x,n.y,n.z)
		plane.d=d
		Return plane
	End Function

	' point/normal form
	Function Create1:TPlane(p:TVec,n:TVec)
		Local plane:TPlane=New TPlane
		plane.n=TVec.Create(n.x,n.y,n.z)
		plane.d=n.Negate().Dot(p)
		Return plane
	End Function

	' create plane from tri
	Function CreateFromTri:TPlane(v0:TVec,v1:TVec,v2:TVec)
		Local plane:TPlane=New TPlane
		plane.n=(v1.Subtract(v0)).Cross(v2.Subtract(v0)).normalized()
		plane.d=plane.n.Negate().Dot(v0)
		Return plane
	End Function

	Method Negate:TPlane() ' - operator overload
		Return TPlane.Create( n.Negate(),-d )
	End Method
	
	Method t_intersect:Float( q:TLine )
		Return -Distance(q.o)/n.Dot(q.d)
	End Method
	
	Method IntersectLine:TVec( q:TLine )
		Return q.Multiply(t_intersect(q))
	End Method
	
	Method IntersectPlane:TLine( q:TPlane )
		Local lv:TVec=n.Cross( q.n ).Normalized()
		Return TLine.Create( q.IntersectLine( TLine.Create( Nearest( n.Multiply(-d) ),n.Cross(lv) ) ),lv )
	End Method
	
	Method Nearest:TVec( q:TVec )
		Return q.Subtract(n.Multiply(Distance(q)))
	End Method
	
	Method Negate2()
		n=n.Negate();d=-d
	End Method
	
	Method Distance:Float(q:TVec)
		Return n.Dot(q)+d
	End Method

End Type

Type TQuat

	Field w:Float
	Field v:TVec
	
	Function Create0:TQuat()
		Local q:TQuat=New TQuat
		q.w=1.0
		Return q
	End Function
	
	Function Create:TQuat( w:Float,v:TVec )
		Local q:TQuat=New TQuat
		q.w=1.0
		q.v.x=v.x
		q.v.y=v.y
		q.v.z=v.z
		Return q
	End Function
	
	Method Negate:TQuat() ' - operator overload
		Return TQuat.Create( w,v.Negate() )
	End Method
	
	Method Add:TQuat( q:TQuat ) ' + operator overload
		Return TQuat.Create( w+q.w,v.Add(q.v) )
	End Method
	
	Method Subtract:TQuat( q:TQuat ) ' - operator overload
		Return TQuat.Create( w-q.w,v.Subtract(q.v) )
	End Method
	
	Method Multiply:TQuat( q:TQuat ) ' * operator overload
		Return TQuat.Create( w*q.w-v.dot(q.v),(q.v.cross(v)).Add(q.v.Multiply(w)).Add(v.Multiply(q.w)) )
	End Method
	
	Method MultiplyVector:TVec( q:TVec ) ' * operator overload
		Return ( Self.Multiply(TQuat.Create(0,q)).Multiply(Self.Negate()) ).v
	End Method
	
	Method MultiplyFloat:TQuat( q:Float ) ' * operator overload
		Return TQuat.Create( w*q,v.Multiply(q) )
	End Method
	
	Method Divide:TQuat( q:Float ) ' / operator overload
		Return TQuat.Create( w/q,v.Divide(q) )
	End Method
	
	Method Dot:Float( q:TQuat )
		Return v.x*q.v.x+v.y*q.v.y+v.z*q.v.z+w*q.w
	End Method
	
	Method Length:Float()
		Return Sqr( w*w+v.x*v.x+v.y*v.y+v.z*v.z )
	End Method
	
	Method Normalize()
		w=w/Length()
		v.x=v.x/Length()
		v.y=v.y/Length()
		v.z=v.z/Length()
	End Method
	
	Method Normalized:TQuat()
		Local q:TQuat=New TQuat
		q.w=q.w/Length()
		q.v.x=q.v.x/Length()
		q.v.y=q.v.y/Length()
		q.v.z=q.v.z/Length()
		Return q
	End Method
	
	Method SlerpTo:TQuat( q:TQuat,a:Float )
		Local t:TQuat=q
		Local d:Float=dot(q),b:Float=1-a
		If( d<0 ) t.w=-t.w; t.v=t.v.Negate(); d=-d
		If( d<1-EPSILON )
			Local om:Float=ACos( d )
			Local si:Float=Sin( om )
			a=Sin( a*om )/si
			b=Sin( b*om )/si
		EndIf
		Return (Self.MultiplyFloat(b)).Add(t.MultiplyFloat(a))
	End Method
	
	Method i:TVec()
		Local xz:Float=v.x*v.z,wy:Float=w*v.y
		Local xy:Float=v.x*v.y,wz:Float=w*v.z
		Local yy:Float=v.y*v.y,zz:Float=v.z*v.z
		Return TVec.Create( 1-2*(yy+zz),2*(xy-wz),2*(xz+wy) )
	End Method
	
	Method j:TVec()
		Local yz:Float=v.y*v.z,wx:Float=w*v.x
		Local xy:Float=v.x*v.y,wz:Float=w*v.z
		Local xx:Float=v.x*v.x,zz:Float=v.z*v.z
		Return TVec.Create( 2*(xy+wz),1-2*(xx+zz),2*(yz-wx) )
	End Method
	
	Method k:TVec()
		Local xz:Float=v.x*v.z,wy:Float=w*v.y
		Local yz:Float=v.y*v.z,wx:Float=w*v.x
		Local xx:Float=v.x*v.x,yy:Float=v.y*v.y
		Return TVec.Create( 2*(xz-wy),2*(yz+wx),1-2*(xx+yy) )
	End Method
	
End Type

Type TMat

	Field i:TVec,j:TVec,k:TVec
	
	Method Copy:TMat()
		Local m:TMat=New TMat	
		m.i=TVec.Create(i.x,i.y,i.z)
		m.j=TVec.Create(j.x,j.y,j.z)
		m.k=TVec.Create(k.x,k.y,k.z)
		Return m
	End Method

	Function Create0:TMat()
		Local m:TMat=New TMat
		m.i=TVec.Create(1,0,0); m.j=TVec.Create(0,1,0); m.k=TVec.Create(0,0,1)
		Return m
	End Function
	
	Function Create:TMat( i:TVec,j:TVec,k:TVec )
		Local m:TMat=New TMat
		m.i=i; m.j=j; m.k=k
		Return m
	End Function
	
	Function CreateFromQuat:TMat( q:TQuat )
		Local m:TMat=New TMat
		Local xx:Float=q.v.x*q.v.x,yy:Float=q.v.y*q.v.y,zz:Float=q.v.z*q.v.z
		Local xy:Float=q.v.x*q.v.y,xz:Float=q.v.x*q.v.z,yz:Float=q.v.y*q.v.z
		Local wx:Float=q.w*q.v.x,wy:Float=q.w*q.v.y,wz:Float=q.w*q.v.z
		m.i=TVec.Create( 1-2*(yy+zz),2*(xy-wz),2*(xz+wy) )
		m.j=TVec.Create( 2*(xy+wz),1-2*(xx+zz),2*(yz-wx) )
		m.k=TVec.Create( 2*(xz-wy),2*(yz+wx),1-2*(xx+yy) )
		Return m
	End Function
	
	Function CreateFromAngle:TMat( angle:Float,axis:TVec )
		Local m:TMat=New TMat
		Local u:TVec=axis
		Local c:Float=Cos(angle),s:Float=Sin(angle)
		Local x2:Float=axis.x*axis.x,y2:Float=axis.y*axis.y,z2:Float=axis.z*axis.z
		m.i=TVec.Create( x2+c*(1-x2),u.x*u.y*(1-c)-u.z*s,u.z*u.x*(1-c)+u.y*s )
		m.j=TVec.Create( u.x*u.y*(1-c)+u.z*s,y2+c*(1-y2),u.y*u.z*(1-c)-u.x*s )
		m.k=TVec.Create( u.z*u.x*(1-c)-u.y*s,u.y*u.z*(1-c)+u.x*s,z2+c*(1-z2) )
		Return m
	End Function
	
	Rem
	Vector &operator[]( Int n ){
		Return (&i)[n]; 
	}
	Const Vector &operator[]( Int n )Const{
		Return (&i)[n];
	}
	End Rem
	
	Method Inverse:TMat() ' ~ operator overload - inverse?
		Local m:TMat=TMat.Create0()
		m.i.x=i.x;m.i.y=j.x;m.i.z=k.x
		m.j.x=i.y;m.j.y=j.y;m.j.z=k.y
		m.k.x=i.z;m.k.y=j.z;m.k.z=k.z
		Return m
	End Method
	
	Method Determinant:Float()
		Return i.x*(j.y*k.z-j.z*k.y )-i.y*(j.x*k.z-j.z*k.x )+i.z*(j.x*k.y-j.y*k.x )
	End Method
	
	Method Negate:TMat() ' - operator overload
		Local m:TMat=TMat.Create0()
		Local t:Float=1.0/Determinant()
		m.i.x= t*(j.y*k.z-j.z*k.y);m.i.y=-t*(i.y*k.z-i.z*k.y);m.i.z= t*(i.y*j.z-i.z*j.y)
		m.j.x=-t*(j.x*k.z-j.z*k.x);m.j.y= t*(i.x*k.z-i.z*k.x);m.j.z=-t*(i.x*j.z-i.z*j.x)
		m.k.x= t*(j.x*k.y-j.y*k.x);m.k.y=-t*(i.x*k.y-i.y*k.x);m.k.z= t*(i.x*j.y-i.y*j.x)
		Return m
	End Method

	Method Cofactor:TMat()
		Local m:TMat=TMat.Create0()
		m.i.x= (j.y*k.z-j.z*k.y);m.i.y=-(j.x*k.z-j.z*k.x);m.i.z= (j.x*k.y-j.y*k.x)
		m.j.x=-(i.y*k.z-i.z*k.y);m.j.y= (i.x*k.z-i.z*k.x);m.j.z=-(i.x*k.y-i.y*k.x)
		m.k.x= (i.y*j.z-i.z*j.y);m.k.y=-(i.x*j.z-i.z*j.x);m.k.z= (i.x*j.y-i.y*j.x)
		Return m
	End Method	
	
	Method Equals:Int( q:TMat ) ' == operator overload
		Return i=q.i And j=q.j And k=q.k
	End Method
	
	Method Inequality:Int( q:TMat  ) ' != operator overload
		Return i<>q.i Or j<>q.j Or k<>q.k
	End Method
	
	Method MultiplyVector:TVec( q:TVec ) ' * operator overload
		Return TVec.Create( i.x*q.x+j.x*q.y+k.x*q.z,i.y*q.x+j.y*q.y+k.y*q.z,i.z*q.x+j.z*q.y+k.z*q.z )
	End Method
	
	Method Multiply:TMat( q:TMat ) ' * operator overload
		Local m:TMat=TMat.Create0()
		m.i.x=i.x*q.i.x+j.x*q.i.y+k.x*q.i.z;m.i.y=i.y*q.i.x+j.y*q.i.y+k.y*q.i.z;m.i.z=i.z*q.i.x+j.z*q.i.y+k.z*q.i.z
		m.j.x=i.x*q.j.x+j.x*q.j.y+k.x*q.j.z;m.j.y=i.y*q.j.x+j.y*q.j.y+k.y*q.j.z;m.j.z=i.z*q.j.x+j.z*q.j.y+k.z*q.j.z
		m.k.x=i.x*q.k.x+j.x*q.k.y+k.x*q.k.z;m.k.y=i.y*q.k.x+j.y*q.k.y+k.y*q.k.z;m.k.z=i.z*q.k.x+j.z*q.k.y+k.z*q.k.z
		Return m
	End Method
	
	Method Orthogonalize()
		k.Normalize()
		i=j.Cross( k ).Normalized()
		j=k.Cross( i )
	End Method
	
	Method Orthogonalized:TMat()
		Local m:TMat=TMat.Create0()
		m.Orthogonalize()
		Return m
	End Method
	
End Type

Type TBox

	Field a:TVec,b:TVec
	
	Function Create0:TBox()
		Local box:TBox=New TBox
		box.a=TVec.Create(INFINITY,INFINITY,INFINITY)
		box.b=TVec.Create(-INFINITY,-INFINITY,-INFINITY)
		Return box
	End Function
	
	Function CreateFromPoint:TBox( q:TVec )
		Local box:TBox=New TBox
		box.a=TVec.Create(q.x,q.y,q.z)
		box.b=TVec.Create(q.x,q.y,q.z)
		Return box
	End Function
	
	Function Create:TBox( a:TVec,b:TVec )
		Local box:TBox=New TBox
		box.a=TVec.Create(a.x,a.y,a.z)
		box.b=TVec.Create(b.x,b.y,b.z)
		Return box
	End Function
	
	Function CreateFromLine:TBox( l:TLine )
		Local box:TBox=New TBox
		box.a=TVec.Create(l.o.x,l.o.y,l.o.z)
		box.b=TVec.Create(l.o.x,l.o.y,l.o.z)
		box.UpdateFromVector( l.o.Add(l.d) )
		Return box
	End Function
	
	Method Clear()
		a.x=INFINITY;a.y=INFINITY;a.z=INFINITY
		b.x=-INFINITY;b.y=-INFINITY;b.z=-INFINITY
	End Method
	
	Method Empty:Int()
		Return b.x<a.x Or b.y<a.y Or b.z<a.z
	End Method
	
	Method Centre:TVec()
		Return TVec.Create( (a.x+b.x)*.5,(a.y+b.y)*.5,(a.z+b.z)*.5 )
	End Method
	
	Method Corner:TVec( n:Int )
		Local x#=n&1;If x Then x=b.x Else x=a.x
		Local y#=n&2;If y Then y=b.y Else y=a.y
		Local z#=n&4;If z Then z=b.z Else z=a.z
		Return TVec.Create( x,y,z )
	End Method
	
	Method UpdateFromVector( q:TVec )
		If q.x<a.x Then a.x=q.x
		If q.y<a.y Then a.y=q.y
		If q.z<a.z Then a.z=q.z
		If q.x>b.x Then b.x=q.x
		If q.y>b.y Then b.y=q.y
		If q.z>b.z Then b.z=q.z
	End Method
	
	Method UpdateFromBox( q:TBox )
		If q.a.x<a.x Then a.x=q.a.x
		If q.a.y<a.y Then a.y=q.a.y
		If q.a.z<a.z Then a.z=q.a.z
		If q.b.x>b.x Then b.x=q.b.x
		If q.b.y>b.y Then b.y=q.b.y
		If q.b.z>b.z Then b.z=q.b.z
	End Method
	
	Method Overlaps:Int( q:TBox )
		Local x#=b.x<q.b.x;If x Then x=b.x Else x=q.b.x
		Local y#=b.y<q.b.y;If y Then y=b.y Else y=q.b.y
		Local z#=b.z<q.b.z;If z Then z=b.z Else z=q.b.z
		Return x And y And z
	End Method
	
	Method Expand( n:Float )
		a.x:-n;a.y:-n;a.z:-n;b.x:+n;b.y:+n;b.z:+n
	End Method
	
	Method Width:Int()
		Return b.x-a.x
	End Method
	
	Method Height:Int()
		Return b.y-a.y
	End Method
	
	Method Depth:Int()
		Return b.z-a.z
	End Method
	
	Method Contains:Int( q:TVec )
		Return q.x>=a.x And q.x<=b.x And q.y>=a.y And q.y<=b.y And q.z>=a.z And q.z<=b.z
	End Method
	
End Type

Type TTransform

	Field m:TMat
	Field v:TVec

	Function Create0:TTransform()
		Local tform:TTransform=New TTransform
		tform.m:TMat=TMat.Create0()
		tform.v:TVec=TVec.Create0()
		Return tform
	End Function

	Function CreateFromMatrix:TTransform(m:TMat)
		Local tform:TTransform=New TTransform
		tform.m=m.Copy()
		Return tform
	End Function
	
	Function CreateFromVector:TTransform(v:TVec)
		Local tform:TTransform=New TTransform
		tform.v=TVec.Create(v.x,v.y,v.z)
		Return tform
	End Function
	
	Function Create:TTransform(m:TMat,v:TVec)
		Local tform:TTransform=New TTransform
		tform.m=m.Copy()
		tform.v=TVec.Create(v.x,v.y,v.z)
		Return tform
	End Function
	
	Method Negate:TTransform() ' - operator overload
		Local t:TTransform=New TTransform
		t.m=m.Negate();t.v=t.m.MultiplyVector(v.Negate())
		Return t
	End Method
	
	Method Inverse:TTransform() ' ~ operator overload
		Local t:TTransform=New TTransform
		t.m=m.Inverse();t.v=t.m.MultiplyVector(v.Negate())
		Return t
	End Method
	
	Method MultiplyVector:TVec( q:TVec ) ' * operator overload
		Return m.MultiplyVector(q).Add(v)
	End Method
	
	Method MultiplyLine:TLine( q:TLine ) ' * operator overload
		Local t:TVec=Self.MultiplyVector(q.o)
		Return TLine.Create( t,Self.MultiplyVector(q.o.Add(q.d)).Subtract(t) )
	End Method
	
	Method MultiplyBox:TBox( q:TBox ) ' * operator overload
		Local t:TBox=TBox.CreateFromPoint(Self.MultiplyVector(q.corner(0)))
		For Local k:Int=1 To 8
			t.UpdateFromVector( Self.MultiplyVector(q.corner(k)) )
		Next
		Return t
	End Method
	
	Method MultiplyTransform:TTransform( q:TTransform ) ' operator overload
		Local t:TTransform=New TTransform
		t.m=m.Multiply(q.m);t.v=m.MultiplyVector(q.v.Add(v))
		Return t
	End Method
	
	Method Equals:Int( q:TTransform ) ' == operator overload
		Return m.Equals(q.m) And v.Equals(q.v)
	End Method
	
	Method Inequality:Int( q:TTransform ) ' != operator overload
		Return Not Equals( q )
	End Method
	
End Type

Function TransformRadius:Float( r:Float,t:TMat )
	Local sq_3:Float=Sqr(1.0/3.0)
	Return t.MultiplyVector( TVec.Create(sq_3,sq_3,sq_3) ).length()*r
End Function

Function PitchMatrix:TMat( q:Float )
	Return TMat.Create( TVec.Create(1,0,0),TVec.Create(0,Cos(q),Sin(q)),TVec.Create(0,-Sin(q),Cos(q)) )
End Function

Function YawMatrix:TMat( q:Float )
	Return TMat.Create( TVec.Create(Cos(q),0,Sin(q)),TVec.Create(0,1,0),TVec.Create(-Sin(q),0,Cos(q)) )
End Function

Function RollMatrix:TMat( q:Float )
	Return TMat.Create( TVec.Create(Cos(q),Sin(q),0),TVec.Create(-Sin(q),Cos(q),0),TVec.Create(0,0,1) )
End Function

Function MatrixPitch:Float( m:TMat )
	Return m.k.pitch()
End Function

Function MatrixYaw:Float( m:TMat )
	Return m.k.yaw()
End Function

Function MatrixRoll:Float( m:TMat )
	Return ATan2( m.i.y,m.j.y )
End Function

Function ScaleMatrix:TMat( x:Float,y:Float,z:Float )
	Return TMat.Create( TVec.Create( x,0,0 ),TVec.Create( 0,y,0 ),TVec.Create( 0,0,z ) )
End Function

Function ScaleMatrix1:TMat( scale:TVec )
	Return TMat.Create( TVec.Create( scale.x,0,0 ),TVec.Create( 0,scale.y,0 ),TVec.Create( 0,0,scale.z ) )
End Function

Function PitchQuat:TQuat( p:Float )
	Return TQuat.Create( Cos(p/-2),TVec.Create( Sin(p/-2),0,0 ) )
End Function

Function YawQuat:TQuat( y:Float )
	Return TQuat.Create( Cos(y/2),TVec.Create( 0,Sin(y/2),0 ) )
End Function

Function RollQuat:TQuat( r:Float )
	Return TQuat.Create( Cos(r/-2),TVec.Create( 0,0,Sin(r/-2) ) )
End Function

Function RotationMatrix:TMat( p:Float,y:Float,r:Float )
	Return YawMatrix(y).Multiply(PitchMatrix(p)).Multiply(RollMatrix(r))
End Function

Function RotationMatrix1:TMat( rot:TVec )
	Return YawMatrix(rot.y).Multiply(PitchMatrix(rot.x)).Multiply(RollMatrix(rot.z))
EndFunction

Function QuatPitch:Float( q:TQuat )
	Return q.k().Pitch()
End Function

Function QuatYaw:Float( q:TQuat )
	Return q.k().Yaw()
End Function

Function QuatRoll:Float( q:TQuat )
	Return MatrixRoll( TMat.CreateFromQuat(q) )
End Function

Function MatrixQuat:TQuat( p:TMat )
	Local m:TMat m=p
	m.Orthogonalize()
	Local t:Float=m.i.x+m.j.y+m.k.z,w:Float,x:Float,y:Float,z:Float
	If( t>EPSILON )
		t=Sqr( t+1 )*2
		x=(m.k.y-m.j.z)/t
		y=(m.i.z-m.k.x)/t
		z=(m.j.x-m.i.y)/t
		w=t/4
	Else If( m.i.x>m.j.y And m.i.x>m.k.z )
		t=Sqr( m.i.x-m.j.y-m.k.z+1 )*2
		x=t/4
		y=(m.j.x+m.i.y)/t
		z=(m.i.z+m.k.x)/t
		w=(m.k.y-m.j.z)/t
	Else If( m.j.y>m.k.z )
		t=Sqr( m.j.y-m.k.z-m.i.x+1 )*2
		x=(m.j.x+m.i.y)/t
		y=t/4
		z=(m.k.y+m.j.z)/t
		w=(m.i.z-m.k.x)/t
	Else
		t=Sqr( m.k.z-m.j.y-m.i.x+1 )*2
		x=(m.i.z+m.k.x)/t
		y=(m.k.y+m.j.z)/t
		z=t/4
		w=(m.j.x-m.i.y)/t
	EndIf
	Return TQuat.Create( w,TVec.Create( x,y,z ) )
End Function

Heavy useage of this is likely to be quite a bit slower than C++ due to the Bmax functions not being inlined and also due to the GC having to collect lots of vector objects.


big10p(Posted 2007) [#60]
const float EPSILON=.000001f;		//small value
const float INFINITY=10000000.0f;	//big value
How come EPSILON uses 6 decimal places but INFINITY uses 8?


Bobysait(Posted 2007) [#61]
@SimonH

in "Type TQuat"
You'd better store the lenght locally...
	Method Length:Float()
		Return Sqr( w*w+v.x*v.x+v.y*v.y+v.z*v.z )
	End Method
	
	Method Normalize()
		local l_lenght:float=Lenght()
		w=w/l_lenght
		v.x=v.x/l_lenght
		v.y=v.y/l_lenght
		v.z=v.z/l_lenght
	End Method


idem for Method Normalized:TQuat()

@ Mark Sibly
not sure, but it seems to be the same
(i'm not easy with C++ programing ... so... )
I don't know how pointer "this" is archived, and don't know about redondant routines...

	void normalize(){
		float l_lenght=length();
		*this=*this/l_length;
	}
	Quat normalized()const{
		float l_lenght=length();
		return *this/l_length;
	}



Bobysait(Posted 2007) [#62]
in the quat code ( bmax version )
	Method Normalized:TQuat()
		Local q:TQuat=New TQuat
		q.w=q.w/Length()
		q.v.x=q.v.x/Length()
		q.v.y=q.v.y/Length()
		q.v.z=q.v.z/Length()
		Return q
	End Method

if we operand "/lentght()" to the new quat, the result is "0" => q is a "new" quat, so (as there is no "Method New()" to set w as "1"), all its value are 0,0,0,0 , instead of copying the callback quat

you should add a New() method as the original C++ file and use the copy constructor instead of new
	Method New()
		w=1
		v = TVec.Create(0,0,0)
	End Method
	Method Copy:TQuat()
		local q:TQuat = New TQuat
		q.w=w; q.v = v.copy()
		return q
	End Method
	Method Normalized:TQuat()
		Local q:TQuat=Copy()
		q.w=q.w/Length()
		q.v.x=q.v.x/Length()
		q.v.y=q.v.y/Length()
		q.v.z=q.v.z/Length()
		Return q
	End Method





[EDIT 1]
in Type TMat
	Method Orthogonalized:TMat()
		Local m:TMat=TMat.Create0()
		m.Orthogonalize()
		Return m
	End Method


It does not copy the matrix, then ortogonalize... it only return a new "Identity + orthogonalized"

should be :
	Method Orthogonalized:TMat()
		local m:TMat = Copy()
		m.Orthogonalize()
		return m
	End Method


Last edited 2012

Last edited 2012


Bobysait(Posted 2007) [#63]
[double post, sorry for that]


AJ00200(Posted 2009) [#64]
Mark, have you ever considered making a math website.
Or a gsme; Mark Math (ages 2 and up)?


BLaBZ(Posted 2013) [#65]
Is there a tutorial or reference that shows how the b3d entity commands can be represented using these classes?

ie: TranslateEntity,MoveEntity,RotateEntity,TurnEntity,PositionEntity, etc?

I think I have it figured out but it'd be great if someone else could should more light on this.

Thanks!


Ching(Posted 2013) [#66]
My brain hurts.


virtlands(Posted 2013) [#67]
My brain hurts too.


SLotman(Posted 2013) [#68]
Something hurts here too... but much lower...

























...my feet, what did you expect?

Seriously now, you guys resurrected a 9 years old thread?! Holy necroposting Batman!!!