Jump to content


Quake 3 BSP Gravity issue


7 replies to this topic

#1 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 16 May 2006 - 06:26 AM

Hey! I am making a 3D BSP renderer for my own personal uses probably like virtual galleries, real estate, etc.., but whatever the case the engine is written
in C# by myself and a ton of tutorials and reference material (I can assure you its not a cut and paste job - thats aweful coding!)
and I implemented the collision detection code found at http://www.devmaster...uake3collision/
and it works beautifully with my renderer however when I tried to implement
gravity by tracing a ray under the player the trace never detects a collision
and if it does its returned coordinates are usually nowhere near the Z coordinate of the floor (Z axis being up and down in Quake 3 maps).

I tried to detect the Z coordinate of the floor like in the following example:


m_world.Trace( m_player.position, new Vector3f( m_player.position.X, m_player.position.Y, m_player.position.Z - 5000.0f ) );


Even though my collision detection is almost if not the same as in the link
above I'll post the code anyway. Be warned that I hadn't finished the code
yet so I didn't clean it up or do away with the "input/output" variables like described in the tutorial given above.


		public float TraceRay( Vector3f inputStart, Vector3f inputEnd )

		{

			traceType = TraceType.Ray;

			return Trace( inputStart, inputEnd );

		}


		public float TraceSphere( Vector3f inputStart, Vector3f inputEnd, float radius )

		{

			traceType = TraceType.Sphere;

			traceRadius = radius;

			return Trace( inputStart, inputEnd );

		}


		public float TraceBox( Vector3f inputStart, Vector3f inputEnd, Vector3f inputMins, Vector3f inputMaxs )

		{

			if( inputMins.X == 0 && inputMins.Y == 0 && inputMins.Z == 0 &&

				inputMaxs.X == 0 && inputMaxs.Y == 0 && inputMaxs.Z == 0 )

			{

				return TraceRay( inputStart, inputEnd );

			}

			else

			{

				traceType = TraceType.Box;

				traceMins = inputMins;

				traceMaxs = inputMaxs;

				traceExtents = new Vector3f();

				traceExtents.X = -traceMins.X > traceMaxs.X ? -traceMins.X : traceMaxs.X;

				traceExtents.Y = -traceMins.Y > traceMaxs.Y ? -traceMins.Y : traceMaxs.Y;

				traceExtents.Z = -traceMins.Z > traceMaxs.Z ? -traceMins.Z : traceMaxs.Z;

				return Trace( inputStart, inputEnd );

			}

		}


		private float Trace( Vector3f inputStart, Vector3f inputEnd )

		{


			outputEnd = new Vector3f();

			outputNormal = new Vector3f();

			outputStartsOut = true;

			outputAllSolid = false;

			outputFraction = 1.0f;


			CheckNode( 0, 0.0f, 1.0f, inputStart, inputEnd );


			if( outputFraction == 1.0f )

			{

				outputEnd = inputEnd;

			}

			else

			{

				for( int i = 0; i < 3; i++ )

					outputEnd[i] = inputStart[i] + outputFraction * (inputEnd[i] - inputStart[i]);

			}


			return outputFraction;


		}


		void CheckNode( int nodeIndex, float startFraction, float endFraction, Vector3f start, Vector3f end )

		{


			if( outputFraction < startFraction ) return;


			if( nodeIndex < 0 )

			{


				Leaf_t leaf = m_leafs[-(nodeIndex+1)];


				for( int i = 0; i < leaf.Brushes; i++ )

				{

					Brush_t brush = m_brushes[m_leaf_brushes[leaf.FirstBrush+i]];

					if( brush.Sides > 0 && (m_textures[brush.Texture].Contents & (int)MASK_PLAYERSOLID) > 0 )

						CheckBrush(brush, start, end);

				}


				return;


			}


			Node_t node = m_nodes[nodeIndex];

			Plane_t plane = m_planes[node.Plane];


			float startDistance = Vector3f.DotProduct( start, plane.Normal ) - plane.Distance;

			float endDistance = Vector3f.DotProduct( end, plane.Normal ) - plane.Distance;

			float offset = 0.0f;


			if( traceType == TraceType.Ray )

				offset = 0;

			else if( traceType == TraceType.Sphere )

				offset = traceRadius;

			else if( traceType == TraceType.Box )

				offset = (float)( Math.Abs( traceExtents.X * plane.Normal.X ) + Math.Abs( traceExtents.Y * plane.Normal.Y ) + Math.Abs( traceExtents.Z * plane.Normal.Z ) );


			if( startDistance >= offset && endDistance >= offset )

				CheckNode( node.Children[0], startFraction, endFraction, start, end );

			else if ( startDistance < -offset && endDistance < -offset )

				CheckNode( node.Children[1], startFraction, endFraction, start, end );

			else

			{


				int side;

				float fraction1, fraction2, middleFraction;

				Vector3f middle = new Vector3f();


				if( startDistance < endDistance )

				{

					side = 1;

					float inverseDistance = 1.0f / (startDistance - endDistance);

					fraction1 = (startDistance - offset + EPSILON) * inverseDistance;

					fraction2 = (startDistance + offset + EPSILON) * inverseDistance;

				}

				else if( endDistance < startDistance )

				{

					side = 0;

					float inverseDistance = 1.0f / (startDistance - endDistance);

					fraction1 = (startDistance + offset + EPSILON) * inverseDistance;

					fraction2 = (startDistance - offset - EPSILON) * inverseDistance;

				}

				else

				{

					side = 0;

					fraction1 = 1.0f;

					fraction2 = 0.0f;

				}


				if( fraction1 < 0.0f )

					fraction1 = 0.0f;

				else if( fraction1  > 1.0f )

					fraction1 = 1.0f;


				if( fraction2 < 0.0f )

					fraction2 = 0.0f;

				else if( fraction2  > 1.0f )

					fraction2 = 1.0f;


				middleFraction = startFraction + (endFraction - startFraction) * fraction1;


				for( int i = 0; i < 3; i++ )

					middle[i] = start[i] + fraction1 * (end[i] - start[i]);


				CheckNode( node.Children[side], startFraction, middleFraction, start, middle );


				middleFraction = startFraction + (endFraction - startFraction) * fraction2;


				for( int i = 0; i < 3; i++ )

					middle[i] = start[i] + fraction2 * (end[i] - start[i]);


				CheckNode( node.Children[side^1], middleFraction, endFraction, middle, end );


			}


		}


		void CheckBrush( Brush_t brush, Vector3f inputStart, Vector3f inputEnd )

		{


			float startFraction = -1.0f;

			float endFraction = 1.0f;

			bool startsOut = false;

			bool endsOut = false;


			Vector3f Normal = new Vector3f();


			for( int i = 0; i < brush.Sides; i++ )

			{


				BrushSide_t brushSide = m_brush_sides[brush.StartingSide+i];

				Plane_t plane = m_planes[brushSide.Plane];


				float startDistance = 0.0f;

				float endDistance = 0.0f;


				if( traceType == TraceType.Ray )

				{

					startDistance = Vector3f.DotProduct( inputStart, plane.Normal ) - plane.Distance;

					endDistance = Vector3f.DotProduct( inputEnd, plane.Normal ) - plane.Distance;

				}

				else if( traceType == TraceType.Sphere )

				{

					startDistance = Vector3f.DotProduct( inputStart, plane.Normal ) - (plane.Distance + traceRadius);

					endDistance = Vector3f.DotProduct( inputEnd, plane.Normal ) - (plane.Distance + traceRadius);

				}

				else if( traceType == TraceType.Box )

				{


					Vector3f offset = new Vector3f();


					for( int j = 0; i < 3; i++ )

					{

						if( plane.Normal[j] < 0 )

							offset[j] = traceMaxs[j];

						else

							offset[j] = traceMins[j];

					}

					

					startDistance = ( inputStart.X + offset.X ) * plane.Normal.X +

						( inputStart.Y + offset.Y ) * plane.Normal.Y + 

						( inputStart.Z + offset.Z ) * plane.Normal.Z -

						plane.Distance;


					endDistance = ( inputEnd.X + offset.X ) * plane.Normal.X +

						( inputEnd.Y + offset.Y ) * plane.Normal.Y + 

						( inputEnd.Z + offset.Z ) * plane.Normal.Z -

						plane.Distance;


				}


				if( startDistance > 0 )

					startsOut = true;

				if( endDistance > 0 )

					endsOut = true;


				if( startDistance > 0 && endDistance > 0 )

				{

					return;

				}


				if( startDistance <= 0 && endDistance <= 0 )

				{

					continue;

				}


				if( startDistance > endDistance )

				{

					float fraction = (startDistance - EPSILON) / (startDistance - endDistance);

					if( fraction > startFraction )

					{

						startFraction = fraction;

						Normal = plane.Normal;

					}

				}

				else

				{

					float fraction = (startDistance + EPSILON) / (startDistance - endDistance);

					if( fraction < endFraction )

						endFraction = fraction;

				}


			}


			if( startsOut == false )

			{

				outputStartsOut = false;

				if( endsOut == false )

					outputAllSolid = true;

				return;

			}


			if( startFraction < endFraction )

			{

				if( startFraction > -1 && startFraction < outputFraction )

				{

					if( startFraction < 0 )

						startFraction = 0;

					outputFraction = startFraction;

					outputNormal = Normal;

				}

			}


		}


Thanks for any help!

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4782 posts
  • LocationBellevue, WA

Posted 16 May 2006 - 06:20 PM

In Quake, the origin of the player box is directly on the floor. So your trace is starting at a point precisely on the surface you wish to intersect with, and is probably ignoring it or missing it due to numerical imprecision. Try starting the trace a few units above the player location and tracing to a few units below it. (You probably don't want to trace to 5000 units below, or if the player was falling you would see them as standing on the ground far below.)
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 16 May 2006 - 06:32 PM

Well for now I was just tracing the cameras origin to -5000.0 to find the floor
and I have the camera about 100.0 above the floor so my trace is basically
from (in XYZ order) say for example 10, 20, 100 to 10, 20, -4900

#4 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 16 May 2006 - 06:38 PM

Your right it seems to be a imprecision, I tried it at -100.0 insted of -5000.0
and it seems to work, in fact I doesn't seem to like anything past -100.0, is
there anyway of fixing this so I can make large traces downward like converting
my floats to doubles or something?

#5 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 16 May 2006 - 06:46 PM

hmm.. wait I seem to have been detecting it wrong so now I've got


				if( m_world.TraceRay( destination, destination - new Vector3f( 0.0f, 0.0f, 500.0f ) ) != 1.0f )

				{

					Debug.WriteLine("ITS A HIT!");

					Debug.WriteLine( m_world.outputEnd.Z );

					//m_player.position.Z = m_world.outputEnd.Z + 95.0f;

				}


It SEEMS to work fine that is it returns a collision however my debug messages
ALWAYS return 50.0 (which is the Z coordinate of the camera).

#6 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 18 May 2006 - 04:21 PM

*Bump*

I'd really like to get this working and if anybody has the know how for
this kind of question I'd be greatful for their help!

The problem:
I am unable to trace below the player's location using the collision detection
code given in the first post. The collision detection code either returns
the coordinate where the trace started or a combination of the Z coordinates (e.g. it seems if I trace from player.Z - 50.0 to player.Z + 50.0 it returns 100.0) or it does not return a collision at all.

#7 geon

    Senior Member

  • Members
  • PipPipPipPip
  • 812 posts

Posted 18 May 2006 - 08:10 PM

You coding style gives me headache. Why do you pass some arguments in the function call, while others are members of the class? And returning values by setting members? I don't get it.

I actually tried to follow your code.

#8 Prozac

    New Member

  • Members
  • PipPip
  • 15 posts

Posted 19 May 2006 - 04:25 AM

This was the way a lot of the collision detection code I found worked, I had
planned on cleaning it up to match more my style after I had solved this issue.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users