Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie

Applied Mathematics (hardcore).

  • 14-07-2001 7:05am
    #1
    Closed Accounts Posts: 218 ✭✭


    If you don't know that a "Quaternion" is, then stop reading (it's a maths yoke "discovered" by a certain Professor Hamilton in the 19th Century).
    This is WAYYY beyond LC Hons Maths kiddies...

    Excellent, so you do then? Firstly, I amn't a maths genius (by any stretch of even my own warped imagination). This stuff is pretty ****ing sobering. If I did ascribe to the aforementioned skill I wouldn't need help. Basically, I am trying to write a computer game involving 3d stuff(tm). This game happens to involve complex Newtonian physics(tm).

    Well, no actually. No gravity, no friction, it's actually pretty damned straightforward. PLEASE do not mention Einstein, no relativity here please (Science board, I would expect no less). The game we are trying to write is basically a multiplayer Elite clone. If you don't know what "Elite" is, then I suggest you do some research, rest assured it is nothing to do with "Elite" hax04 m45t045 (w0t h45 m4d 5k1ll5). Further information: http://hosted.quake.ie/void (about the project, not hax04s damnit!)

    Unfortunately, the webpage linked above has not been updated in a while. A chronological search of the Programming boards filtered by the username "Void" should reveal links to recent screenshots/updates (wah wah, we don't have time to update the site properly etc).

    Project Status:
    TCP/IP Multiplayer Architecture:
    Blah blah UDP comms.
    Dead reckoning (AKA Client-side prediction) ala Quakeworld (ala US Military SimNET project, yeah we robbed some of their code, nice.....).

    OpenGL Renderer
    Poxy 3D Graphics rendering thing (FPS sucks monkeys on a GeForce 2 GTS).
    2D Windowing system (yayyy!)
    All the stuff GUI programmers take for granted I coded myself, windows, checkboxes, listboxes, comboboxes, all the usual VB fare is fully functional).
    Multithreaded database access thingy (15 tier! Sorry, no, just 2 tier. What the **** does "tier" mean anyway? Whatever, it works.).
    Enterprise Level Design:
    Well, all you have to do is say "Enterprise" and all of a sudden it's a "professional" bit of code......
    Physics Engine
    This is pretty simple (Quaternions aside). As an example, the default physics function for a game object is listed below.

    AI FRAMEWORK - See above plea for help.
    Consider the following scenario. Two fighters are approaching each other. End of story. How do you code AI for this scenario? It's easy to think of the basics, but the mathematics is possibly the most savage thing I have ever come across. The problem lies with the heavy calculus. I can't handle it. Help please? This is what this post is all about.

    Basically, I have spent the last *three* years coding all this stuff. In all fairness, I did rob ridiculous amounts of code from various places. Robbing code is easy, integrating it into your own design is difficult. I scrapped the entire design recently and have embarked on the 3rd iteration of the project.

    Anyway, excuse the hyperbole, we are looking for people who are good at maths, with a tiny amount of programming skill (optional). Rewards are minimal but do include "I made this" and "I officially have mad skillz". I hope to start my first proper job getting paid to write computer games in August (dependent on IDA funding), so the last three years ****ing around with this stuff have not been in vain.


    CLARIFICATIONS:
    We're talking about C++ here.
    Standalone demo is ALMOST ready to go (you just can't do anything in it!! No AI!!!)
    We = Necrosoft INC. http://hosted.quake.ie/void
    Me = PJW. pjw@oceanfree.net
    Necrosoft = Some c*nt has already registered www.necrosoft.com, and it's a "portal site for the afterlife"? Mother of Jesus.

    Before anyone gets any ideas, my nick "Void" is the same as the project's: "Void". Funnily enough this is because I created the account to publicise the project about a year ago. I just haven't been bothered setting up a new account. And as for the origins of "Void", C++ (I was illumined whilst coding one fine day).

    Anyways,
    ADDENDUM: CHOICE SELECTION OF CODE (MEDIUM RARE)

    Warning: No you won't be able to compile this code, duhhhhh. It is supposed to serve as an example of interfacing into the Void object hierarchy. Notice the incredibly unprofessional coding style/variable naming convention. Quaternion dogs indeed!
    // DEFAULT OBJECT PHYSICAL RESOLUTION
    // etime = elapsed time in milliseconds
    void cGameObject::Update(float etime)
    {
    Quaternion temp;
       // temp = orientation rotated by rotation
       temp.mult(orientation, rotation);
       // interpolate between orientation and temp by an interval of etime
       orientation.QuatSlerp(&orientation, &temp, etime);
    		
       // standard Newtonian jazz
       velocity.x += (acceleration.x * etime);
       velocity.y += (acceleration.y * etime);
       velocity.z += (acceleration.z * etime);
    
       // likewise
       position.x += (velocity.x * etime) / 1000;
       position.y += (velocity.y * etime) / 1000;
       position.z += (velocity.z * etime) / 1000;
       
       // mad ridiculously complicated collision detection stuff (HERE BE DRAGONS!)
       CollisionUpdate();
    }
    
    
    // CODE TO DRAW "ELITE" STYLE RADAR
    static int RadarPerspectiveAngle = 20;
    static int RadarRotationAngle = 30;
    static float RadarMarkStep = 5000;
    static int RadarZoom = 1;
    
    void DrawRadar(Widget& wf) 
    {
    	int i;
    	
    	Point me = game.player->owner->position;
    	float passiveRadar = 10000; //Me->ship->radarPassiveDist;
    	float activeRadar = 100000; //Me->ship->radarActive? Me->ship->radarActiveDist : 100;
    	float rzoom = passiveRadar/RadarZoom;
    	float znear = rzoom*f_cos(RadarPerspectiveAngle*0.5f*fPI/180)/f_sin(RadarPerspectiveAngle*0.5f*fPI/180);
    	
    	// Setup Viewport
    	glViewport(0,0,400,400);
    
    	// Setup projection matrix
    	glMatrixMode(GL_PROJECTION);
    	glPushMatrix();
    	glLoadIdentity();
    	gluPerspective(RadarPerspectiveAngle,1,znear,znear+2*rzoom);
    
    	// Setup modelview matrix
    	glMatrixMode(GL_MODELVIEW);
    	glPushMatrix();
    	glLoadIdentity();
    	gluLookAt(0,0,-(znear+rzoom),  0,0,0,  0,znear,0);
    	glRotated(-RadarRotationAngle,1,0,0);
    
    	// Draw radar's mark lines
    	glColor3ub(56,59,166);
    	//glColor3f(.5f,.5f,.5f);
    	int num = int(rzoom/RadarMarkStep);
    	float edge = 0.3826834323651f; // sin(45./2.);
    	glLineWidth(2);
    	glBegin(GL_LINE_LOOP);
    		glVertex3d(-rzoom,0,-rzoom*edge);
    		glVertex3d(-rzoom,0,+rzoom*edge);
    		glVertex3d(-rzoom*edge,0,+rzoom);
    		glVertex3d(+rzoom*edge,0,+rzoom);
    		glVertex3d(+rzoom,0,+rzoom*edge);
    		glVertex3d(+rzoom,0,-rzoom*edge);
    		glVertex3d(+rzoom*edge,0,-rzoom);
    		glVertex3d(-rzoom*edge,0,-rzoom);
    	glEnd();
    	glColor3ub(122,127,220);
    	glLineWidth(1);
    	glBegin(GL_LINES);
    		for(i=-num; i <= num; i++) 
    		{
    			float range = lf(f_fabs(i*RadarMarkStep),0,rzoom,rzoom*edge,rzoom,rzoom,rzoom*edge);
    			if (i < 0) range = -range;
    			glVertex3f(i*RadarMarkStep,0,-range);
    			glVertex3f(i*RadarMarkStep,0,+range);
    		}
    
    		for(i=-num; i <= num; i++) 
    		{
    			float range = lf(f_fabs(i*RadarMarkStep),0,rzoom,rzoom*edge,rzoom,rzoom,rzoom*edge);
    			if (i < 0) range = -range;
    			glVertex3f(-range,0,i*RadarMarkStep);
    			glVertex3f(+range,0,i*RadarMarkStep);
    		}
    	glEnd();
    
    	// Draw ships in radar
    	Vector up = axeY;
    	Quaternion dogs;
    	dogs = game.player->owner->orientation;
    	Vector eye(0,33000, 55000);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	gluLookAt(eye.x,eye.y,eye.z,0,0,0,up.x,up.y,up.z);
    
    	// Draw world items as points
    	glPointSize(12);
    	
    	cObject *temp = game.world.firstobject;
    	Vector relpos;
    	NormalVector locY = axeY;
    	dogs.Invert();
    	
    	while (temp)
    	{
    		if (temp->objtype >= typemissile)
    		{
    			relpos = temp->position - game.player->owner->position;
    			double dist =  relpos.size()* 1000;
    			relpos.scale(1000);
    			glColor3f(0,1,0);
    			relpos.Rotate(dogs);
    			
    			glBegin(GL_LINES);
    			glVertex3f(relpos.x, 0, relpos.z);
    			glVertex3f(relpos.x, relpos.y, relpos.z);
    			glEnd();			
    
    			glColor3f(1.0f, 0,0);
    			glPointSize(6.0f);
    			
    			glBegin(GL_POINTS);
    			glVertex3f(relpos.x, relpos.y, relpos.z);
    			glEnd();
    			//glVertex3fv(&(relpos.x));
    		}
    		temp = temp->next;
    	}
    
    	
    	// Restore projection matrix
    	glMatrixMode(GL_PROJECTION);
    	glPopMatrix();
    	// Restore modelview matrix
    	glMatrixMode(GL_MODELVIEW);
    	glPopMatrix();
    	// Restore viewport
    	
    	glViewport(0, 0, game.width, game.height);
    }
    
    // MISSILE HOMING CODE
    void cAIMissile::HomeIn()
    {
    float distancetotarget;
    Point relativeposition;
    
    	relativeposition = target->position - owner->position;
    	//game.Log("Target @ %f %f %f\n", relativeposition.x, relativeposition.y, relativeposition.z);
    
    	distancetotarget = relativeposition.size();
    	//game.Log("Targe @ %f km\n", distancetotarget);
    
    	if (distancetotarget > 100 || distancetotarget <0.005)
    	{
    		Detonate();
    	}
    	else
    	{
    		Vector vect1, vect2;
    		vect1 = relativeposition;
    		vect1.scale(1000);
    		vect2 = vect1;
    		vect2.norm(); vect2.scale(sqrt(100*M_PI*vect1.size()));
    		if(vect2.size() < 200)
    		{
    			vect2.norm(); vect2.scale(200);
    		}
    		vect2 = vect2 + target->velocity;
    		//Vect2 is now the velocity i WANT the missile to go.
    		vect2 = vect2 - owner->velocity; //acceleration required
    		if(vect2.size() > 200)
    		{
    			vect2.norm(); vect2.scale(200);
    		}//Cap Acceleration.
    		owner->acceleration = vect2;
    		FaceVector(vect2);
    	}
    }  
    

    [This message has been edited by Void (edited 14-07-2001).]

    [This message has been edited by Void (edited 14-07-2001).]


Comments

  • Closed Accounts Posts: 1,136 ✭✭✭Bob the Unlucky Octopus


    That sounds seriously interesting Void- and I wish you the very best of luck with the project. However, my impression is that this thread belongs in "Work" or "Technology"- I'll go with my gut instinct and move it to "Work" if that's ok- if you don't get a decent response there, WWman could move it to Technology/Programming if that suits.

    Again, the very best of luck with "Void"- let's all hope it's a smashing success smile.gif

    Bob the Unlucky Octopus


  • Registered Users, Registered Users 2 Posts: 897 ✭✭✭Greenbean


    Ok, I'm not able to help sorry :/ I'm just here to do a little pimpage of me own project under the provisional title of Fall From Grace - www.netsoc.ucd.ie/ffg.

    As such its the other end of the scale from void - see are sort of starting a small project first; whereas void is doing one god almighty horrendously hard project.. and on his own. I suppose trying to get 30,000 clients isn't really small, but we've the skills, expertise and best of all, hardware for this. I'm coding the directx client and so far I have to admit my framework is a copy of the directx sdk.. but I'll learn.


  • Closed Accounts Posts: 2,313 ✭✭✭Paladin


    Programming board would be more suitable methinks?


  • Closed Accounts Posts: 1,136 ✭✭✭Bob the Unlucky Octopus


    That's what I thought initially Paladin- but seeing as he's looking for people with applied math skills to fill a work-niche- I figured it belonged here. If the response isn't good enough- then I'm sure WWman would have no problem with moving it to Programming smile.gif

    Bob the Unlucky Octopus


  • Closed Accounts Posts: 218 ✭✭Void


    Good to see you still hard at it GB! The project is horrendously hard alright, it's good that I don't have any expectations at finishing it.....ever! That said, it's a good way to learn. Pity my first games programming job is writing a football management game!! I know some people love that stuff, but as far as I'm concerned it's just a glorified spreadsheet...

    Maybe I was a bit vague in the above post. The programming isn't the issue (that's my problem), 3D AI mathematics are. Also, this is voluntary "work". I think the Science board is the place. I've mailed Bob asking him to sort it out.

    A good example of what I'm looking for is the missile guidance code at the bottom of the post. Fighter AI will be muuuuuch more complicated...


  • Advertisement
  • Registered Users, Registered Users 2 Posts: 897 ✭✭✭Greenbean


    Up late and finally sorted out how the fck directx does its transformations. I'll just stick it down for future reference for anyone.

    Take the directx sdk. Create your own c++ style objects for representing models, which generically renders each object, moves it etc etc. The sdk provides support for exported 3dmax meshes. Great you think! So now I just use the transformation code I saw in a tutorial earlier:

    D3DXMATRIX matWorld,matView,matProj;
    D3DXMatrixRotationY( &matWorld, timeGetTime()/500.0f );
    lpD3DDevice->SetTransform( D3DTS_WORLD, &matWorld );

    This is set just before everytime you goto render a frame. Oh crap you think then, as you realise "matWorld" has just been instanciated at the start of this local code and there seems to be absolutely no reference to the meshes/3dobjects they were working with earlier in the tutorial. Further reading tells you D3DTS_WORLD is a flag for saying in which world space/coordinate system or something you want to do it - this is of no help in targeting your object specifically; so you come to the conclusion that this is a rotation for the entire world/scene.

    You search and you search and every tutorial/document is either based on one object or when in multiple objects it seems to be identical. The trick to seeing the answer is to realise directx is a state machine and SetTransform is only changing the state of the machine. So your matWorld Matrix is only you saying - I want this transformation/rotation/scaling or combination of these to be the next transformation you perform. SetTransform(matWorld) sets it and now directx will perform this on the NEXT object you render. If you want 3 objects doing different things, you do - setTransform(blahscaleblah) render(object1), setTransform(blahblahrotateblah, render(object2), setTransform... etc etc

    Ok glad I got that off my chest, I really should stick it up in a more obvious place (and better written) for any poor souls still stuck on this very badly documented aspect of directx.

    Void, I do actually know of a 1st grade mathematician, masters in mathematics etc, intelligent beyond belief - hes on our games programming team, but hes doing his exams in the coming weeks and I dont' want to bother him. I've seen a **** load of Quaternion stuff all over those programming boards and some on the directx sdk documentation though.

    Here comes a quote of the sdk stuff...

    Vectors, Vertices, and Quaternions
    Throughout Microsoft® Direct3D®, vertices describe position and orientation. Each vertex in a primitive is described by a vector that gives its position, color, texture coordinates, and a normal vector that gives its orientation.

    Quaternions add a fourth element to the [x, y, z] values that define a three-component-vector. Quaternions are an alternative to the matrix methods that are typically used for 3-D rotations. A quaternion represents an axis in 3-D space and a rotation around that axis. For example, a quaternion might represent a (1,1,2) axis and a rotation of 1 radian. Quaternions carry valuable information, but their true power comes from the two operations that you can perform on them: composition and interpolation.

    Performing composition on quaternions is similar to combining them. The composition of two quaternions is notated as follows:

    "The composition of two quaternions applied to a geometry means "rotate the geometry around axis2 by rotation2, then rotate it around axis1 by rotation1." In this case, Q represents a rotation around a single axis that is the result of applying q2, then q1 to the geometry.

    Using quaternion interpolation, an application can calculate a smooth and reasonable path from one axis and orientation to another. Therefore, interpolation between q1 and q2 provides a simple way to animate from one orientation to another.

    When you use composition and interpolation together, they provide you with a simple way to manipulate a geometry in a manner that appears complex. For example, imagine that you have a geometry that you want to rotate to a given orientation. You know that you want to rotate it r2 degrees around axis2, then rotate it r1 degrees around axis1, but you don't know the final quaternion. By using composition, you could combine the two rotations on the geometry to get a single quaternion that is the result. Then, you could interpolate from the original to the composed quaternion to achieve a smooth transition from one to the other.

    The Direct3DX utility library includes functions that help you work with quaternions. For example, the D3DXQuaternionRotationAxis function adds a rotation value to a vector that defines an axis of rotation, and returns the result in a quaternion defined by a D3DXQUATERNION structure. Additionally, the D3DXQuaternionMultiply function composes quaternions and the D3DXQuaternionSlerp performs spherical linear interpolation between two quaternions.

    Direct3D applications can use the following functions to simplify the task of working with quaternions.

    D3DXQuaternionBaryCentric
    D3DXQuaternionConjugate
    D3DXQuaternionDot
    D3DXQuaternionExp
    D3DXQuaternionIdentity
    D3DXQuaternionInverse
    D3DXQuaternionIsIdentity
    D3DXQuaternionLength
    D3DXQuaternionLengthSq
    D3DXQuaternionLn
    D3DXQuaternionMultiply
    D3DXQuaternionNormalize
    D3DXQuaternionRotationAxis
    D3DXQuaternionRotationMatrix
    D3DXQuaternionRotationYawPitchRoll
    D3DXQuaternionSlerp
    D3DXQuaternionSquad
    D3DXQuaternionToAxisAngle
    Direct3D applications can use the following functions to simplify the task of working with three-component-vectors.

    D3DXVec3Add
    D3DXVec3BaryCentric
    D3DXVec3CatmullRom
    D3DXVec3Cross
    D3DXVec3Dot
    D3DXVec3Hermite
    D3DXVec3Length
    D3DXVec3LengthSq
    D3DXVec3Lerp
    D3DXVec3Maximize
    D3DXVec3Minimize
    D3DXVec3Normalize
    D3DXVec3Project
    D3DXVec3Scale
    D3DXVec3Subtract
    D3DXVec3Transform
    D3DXVec3TransformCoord
    D3DXVec3TransformNormal
    D3DXVec3Unproject
    Many additional functions that simplify tasks using two- and four-component-vectors are included among the math functions supplied by the Direct3DX utility library."

    Probably not much of a help since it doesn't tackle the maths, but neither will I when it comes to using them thanks to dx's handy functions.


  • Registered Users, Registered Users 2 Posts: 2,010 ✭✭✭Dr_Teeth


    Nice one Void, I pretty much did exactly what you're doing for my final year project in college back in '98.

    Though mine ran on sun workstations with no hardware acceleration so I was getting like 10fps wink.gif

    Teeth.

    [This message has been edited by Dr_Teeth (edited 16-07-2001).]


  • Registered Users, Registered Users 2 Posts: 1,481 ✭✭✭satchmo


    Void I'd love to help, but unfortunately my maths is probably not up to scratch - I was going to use quaternions in my FYP (the VR 3D modeller), but after a bit of investigation I decided it'd take longer to figure them out than would be reasonable given the time I had to do it (alas I'm very much a last-minute person). However I have a mate who just finished an engineering degree in UCD - his FYP was a simulated missile guidance system to intercept moving objects (done in MATlab afaik), and his maths is the best of anyone I know. He can't program, but wants to learn. I'll put you in touch with him if you want.


  • Closed Accounts Posts: 218 ✭✭Void


    GB: Since when are there maths helper functions in DX? I messed around with DX 7 and I never saw any! And me after spending ages writing my own maths library... I never had that problem you were on about, I learned DX before OpenGL (basically the same, although World/View matrices are combined in OpenGL, which is weird at first). Shouldn't you be pushing and popping the world matrix? Maybe I'm misinterpreting what you are saying.

    Teeth: I remember, how much did you get implemented? I've gotten carried away with the whole 3d engine bit, and I haven't worked on the distributed network architecture part for a while.

    Jazz: There's a reason for using Quaternions, as you'll no doubt discover. I had the luxury of time though, so after getting bogged down with gimbal lock and stuff like that, I learned the basics of Quaternions and I'll never look back. Euler angles are a flawed representation. I hope your FYP went well anyways smile.gif Send me your mates email and I'll have a that with him. Cheers.

    That said, I don't really understand a lot of the theory behind this type of stuff, but I can put it into practice. Strange but true. As for the missile guidance, it's the simplest AI you can write. Dogfighting AI has to plan ahead though. Although, the missile AI code in my post is simplistic, it doesn't take the targets acceleration into account (velocity only). Also, there is no rotational acceleration considered (missile's engine is at the back, so it should maneuver to achieve maximum thrust, this is simplified, it just instantly points in the direction it wants to go). The only game to implement stuff like this is "I-War" as far as I am aware. Most other space shooty games (Freespace, StarLancer) have friction/"stop on a dime" type flight mechanics.


  • Registered Users, Registered Users 2 Posts: 897 ✭✭✭Greenbean


    Dunno since when there were helper functions, but they're certainly there now - heres the areas that the maths library helps with:

    Color D3DXColorAdd
    D3DXColorAdjustContrast
    D3DXColorAdjustSaturation ... and so on
    Creation D3DXCreateMatrixStack
    Plane D3DXPlaneDot
    D3DXPlaneDotCoord
    D3DXPlaneDotNormal ......
    Quaternion D3DXQuaternionBaryCentric
    D3DXQuaternionConjugate
    D3DXQuaternionDot .......
    2-D Vector D3DXVec2Add
    D3DXVec2BaryCentric
    D3DXVec2CatmullRom .....
    3-D Vector D3DXVec3Add
    D3DXVec3BaryCentric
    D3DXVec3CatmullRom ....
    4-D Matrix D3DXMatrixAffineTransformation
    D3DXMatrixfDeterminant
    D3DXMatrixIdentity ......
    4-D Vector D3DXVec4Add
    D3DXVec4BaryCentric
    D3DXVec4CatmullRom
    D3DXVec4Cross ......

    I'll admit my understand of how transformations will work overall isn't quite ironed out, but yesterday I had a coppernican revolution in terms of view point of how directx transforms work.

    I really really really want to have a generic 3dobject class that will contain the 3d model, hold an overridable function for rendering it, deal with any transformations possible, any collision detection and physics. I haven't even looked into in what order I'm supposed to render objects (back of a scene to the front or vice versa), mouse input (how do you represent a 2d mouse plane in 3d?) and the camera control (a function of mouse input). I'm going to learn an awful lot shockingly fast.


  • Advertisement
  • Registered Users, Registered Users 2 Posts: 4,188 ✭✭✭pH


    Nice looking site void - game looks good.

    DirectX as of version 8 is the dogs dangly bits - and yes it has all those lovely D3DX math and matrix and Quat functions. Take a look at www.iol.ie/~holland/screen1.jpg and www.iol.ie/~holland/screen2.jpg to see about 1 weeks work (its an artillery game i'm writing).

    Anyways - the missile tracking IMHO should leave the missiles at a constant velocity, and they should adjust their angle only to close on the target. It can be made more sophisticated if the missile also takes into accout the targets velocity and 'tracks' it and aims to a point in its path rather than its current position. Well - hey you've fired a rocket laucher!!!

    The missile should be restricted in how much turn it can achieve over time.

    anyways keep up the damn fine work

    pH




  • Closed Accounts Posts: 218 ✭✭Void


    Cheers pH, nice screen shots! That's pretty damn good for a weeks work, you obviously aren't new to this stuff. What algorithm are you generating the terrain with? Keep up the good work. By the way, have a look at the missile code again, it does exactly what you were saying.
    vect2 = vect2 + target->velocity; //Vect2 is now the velocity i WANT the missile to go.
    vect2 = vect2 - owner->velocity; //acceleration required
    

    GB: Here's a link to the VB DX engine I wrote yonks ago. It's in DX7 but it does all the stuff you're moaning about so have a look smile.gif No Quaternions or other scary stuff. The DX syntax is the same in VB as C++. It shows how to do mouse camera control via euler angles.

    http://hosted.quake.ie/void/code/vbenginefull.zip

    While I'm at it, here's the maths library I wrote. Handy if you're using OpenGL.

    http://hosted.quake.ie/void/code/maths.h

    [This message has been edited by Void (edited 17-07-2001).]


Advertisement