Friday, February 10, 2012

Capsule-Capsule Collision in Games

I have had to solve the problem of capsule on capsule collision solution and have found little if any good code or solutions on the 'net.  So here is my solution with code samples.  Also most game physics books discuss the math but not the overall view of how to sequence collision and physics in a game.  I highly recommend the book, Real-Time Collision Detection, by Christer Ericson.  A lot of the code here is derived and adapted from this book.

But first, lots of background about game collision.

Collision
In computer games (like TDL from Sandswept Studios, which I am writing) you need to have the Players and Non-Player Characters (Personas) move around the scene. As they move they should never be allowed to interpenetrate other objects like walls, the ground, and other personas.

You achieve this in two phases:

  • First by making a list of all physics objects in the whole world that might interact.  This is the coarse phase, and results in each object having a list of all the other objects to check.
  • Second is by moving each physics object with non-zero velocity while holding everything else still, and seeing if it collides with any of the objects it was paired with in the Coarse Phase. If it collides you move that distance, then adjust the velocity by the collision normal vector. This is called Movement Clipping.
Movement should not be confused with hit detection such as punches, baseball bat swings, and rifle shots.  These are solved with a very different strategy that involves triangle meshes and such.

There are several 3D shapes that are useful for movement clipping. A sphere is very simple to solve and is good for roundish objects like rocks, grenades, and such. Boxes can be good for walls and come in two flavors, Axis Aligned Bounding Boxes (AABB), and Object Bounding Boxes (OBB).  An AABB is a box aligned with the X,Y,Z axis and is very fast to solve.  OBB is a box but aligned along the natural axis of the object and moves and rotates with the object. OBB is pretty fast to solve too and is good for walls, and about any boxy objects.

Personas are best represented as Capsules.  A capsule is two spheres with a cylinder between them.  I have heard them called pills or suppositories too. The upper and lower ends are actually half-spheres.
A Capsule.
The reason capsules are so good to use is that they give a very realistic feel to collisions with walls and other Personas.  Also they are reasonably easy to program and compute. One very nice feature is they will slide up stair step due to the lower half sphere without any special programming for stairs.
Capsule represents a player. Lower curve navigates stairs.
Coarse Pass 
So, how do we solve the Coarse Pass?  What you do is every physics object has a AABB.  For each physics object you compare it against every other physics object to see if their AABBs overlap.  There are several complications.  Firstly the objects can be moving so you want to expand the AABB in the direction of motion so it fits the start and end locations of the object.  Not to hard to implement.  The second problem is a bit more complex.  In a large scene (TDL is infinite) there can be a huge number of physics objects.  Comparing all of them to each other is an Order Squared or O2 problem.  For example, 5,000 parts will require almost 25,000,000 comparisons!

So what you have to do is break the problem down so you are only comparing against parts that are reasonably close.  This can be done by grouping parts in some natural way, like all the parts in a given room, and put an AABB around them all and if your physics object does not intersect the big AABB, then ignore all the parts inside.  Often this ends up being a hierarchy of AABBs.

Nested AABBs


In TDL we do an OctTree.  We divided space up into cubes, then each cube is divided in to 8 sub-cubes for about 8 levels deep.  We only check a given physics objects others in it's parent cube, its cube, and all the sub cubes below it.
SubCube division.
From http://software.intel.com/en-us/articles/extending-stl-for-games/
On other major speed up for the Coarse Pass is to only have moving objects do checking against all other objects moving or not. (This particular strategy sped our Coarse Pass up by 32x!)

The result of the Coarse pass is that each Physics Object now has a list of all other potential other objects that might collide with it when it does is move.

Some improvements are to keep two lists, moving and not moving parts. Or keep three lists, moving, not moving but movable, and permanently immovable parts. (like walls)

The Fine Pass
So now we want to take all the parts that are moving and move them, but clip their movement by anything they collide with.  A object will have a certain movement it does in the current game time interval. For example a player moving at 3 meters per second will move 0.18 meters in 1/60th of a second.

What we want to do is check if any of the potential other objects get hit in that 0.18 meters and if so which are the closest. We then take those closest objects and subtract any component of our velocity that is perpendicular to the impact surface normal.

There are two approaches for finding the collision distance.  The first and best is to do a direct linear math solution as to how far you can move before hitting the other object.  This is best because is arrives at an exact distance in one pass of equations.

The other not-so-good approach is to do successive approximations.  This is where you first test the farthest move distance (in our example 0.18 meters) and see if it overlaps the other object.  If it does you cut the distance in half and try again.  On success you add half again, and on fail subtract half again.  The more times you do this the closer you get to the correct distance. 8 iterations get you withing 2 to the -8th power or 1/256 of the total distance within the correct answer.  One approach is to make longer absolute distance do more iterations, and short distances do less.  This can be done with a single tolerance number.  Why would you ever use successive approximations?  Because there are some geometric solutions that get into very complex and ugly math, yet the 'does it overlap' math is very simple. A good example of this is unaligned capsules.

Rounding Errors
One other important issue with solving move distance to hit objects.  Once you have hit the object and adjusted you velocity to be parallel to the hit surface, you bay be just a very tiny bit inside the surface, and you velocity may not be exactly parallel.  On computer the actual math is in binary and there are rounding errors. For example 1 divided by 3 is not 1/3, it is 0.333333333 and the number is not infinite.  So you need to have a small number that is the limit of how close 'equal' is.  In TDL is is called PHYSICS_EPSILON and is 0.0001 meters.

You then have macros (inline functions) such as isEqual, isZero, that are like this...

    inline bool isEqual(float a, float b) { return abs(a - b) <= PHYSICS_EPSILON; }

You also need the same methods for your 3D vector classes.

Now when you check for the collision distance you check whether the velocity is away or parallel (nearly parallel!) to the surface.  If it is it is not a hit.

One other note: Testing for dot products to be small requires a test against PHYSICS_EPSILON squared since a dot product is in essence a multiply.  This can bite you if you are comparing normals for parallel and also comparing velocity values for near zero.  Your dot products can be very small and not appear to be not near zero when they are.

Back to Fine Pass
For the fine pass we now find how far we can move, move there, adjust our velocity by any impacts, subtract how far we moved from the total (the 0.18 meters) and keep doing that until we have moved the full distance, or our velocity hits zero.

Oh, one question you might have, where did our velocity come from in the first place?  Every tick of the game you add gravity to the velocity, and add some velocity according to the controls such as in TDL, the W key makes you move forward.

Capsules
Ok, enough back story. The real intent of this posting is to discuss solutions for capsules and give code examples.

A capsule is defined one of two ways, either points A and B which are the centers of the two spheres, and a radius, or a single point A and a direction and distance given by a vector, and a radius.

There are several solutions you need and methods.  They are surfaceNormal, intersects, and collisionDistance.

But to implement those you need some utility methods.  One is segmentSegmentDistance which is given segment AB and segment CD find the least distance between the segments and the two points on the segments that are closest.  In the case of a capsule if this distance is less than the sum of the two radii then they are intersecting.  If the distance is more than the sum of the radii plus the speed of motion, then they wont collide at all.

A very similar method as ptSegmentNearest which finds the distance between a point P and a line segment AB and wht the point on the segment is.

Surface Normal
The surface normal is fairly simple.Given a point P presumed to be on the surface use ptSegmentNearest to find the point Q on the capusles AB segment and subtract Q from P to get a vector and then normalize the vector.  It will be the surface normal.  Watch out for the degenerate case where P is on the segment AB!

Intersects
If you are solving the distance to impact by successive approximations you need to have a fast way to tell if two Capsules intersect.  The best is to use segmentSegmentDistance.  If it is less than the sum of the radii then they intersect.  Watch out for the PHYSCIS_EPSILON issues here.  It is really

  if(isZero(dist - R1 - R2)) ...
Collision Distance
Now for the hard part!
If we have a moving capsule and it might collide with another capsule, well, do they and how far?

Turns out there are two cases for this.

If the capsules are aligned, in other words their AB lines are parallel or anti-parallel then the solution is much easier. (Anti-parallel means line AB is exactly the opposite direction from the other capsule's AB).

To tell if they are parallel just take the dot product of the two ABs.  If it 1.0 or -1.0 then they are parallel so you do the easy direct linear math solution.  Careful, once again don't just do abs(AB.dot(other->AB)) == 1.0.  You must use isZero(abs(AB.dot(other->AB)) - 1.0) to account for PHYSICS_EPSILON.
Parallel Capsules

* note: in some graphics code PHYSICS_EPSILON is just called EPSILON.

If they are not parallel then you have to solve by successive approximations.

Direct Linear Solution
Parallel capsules can be solved by adding the two AB segment together into one longer segment (watch out for the anti-parallel case!) and adding the radii resulting in a bigger capsule.  Then solve a ray from the A point of this to hit the combined capsule.  The distance will be correct.

-- Picture here ---

To solve the ray, first solve for an infinite cylinder and if it misses then there is no collision.  If it hits, find the point along the cylinder center line nearest the hit point.  If it is between A and B then you hit the body of the capsule.  If not then solve each of the spheres. and see which is closest.  There are some odd cases where your ray origin is inside the capsule, oops, the capsules were already intersecting.

You also have to see if the surface normal at the hit point is at right angles to your velocity.  This means it is a grazing hit (a miss) or you are just on the surface and are moving parallel to the surface, also a miss.  In fact any solution with the ray origin inside the capsule is a miss. These near misses are critical to good collision behavior.
Combine two capsules and then collide as a vector to get distance to collision.

Successive Approximation
If the capsules are not parallel you have to do a successive approximation.  Above I explained that you can tell whether two capsules are intersecting by simply finding the distance between the two AB segments. So you call the intersects method to decide how to iterate.  One great optimization is that much of the math for distance doe not depend on the resulting distance, only on the dot or cross product between the two AB segments.  You can take advantage of this to speed the iterations up.
Successive Approximation. Shown with parallel capsules but in our case they would not be parallel. Shows just 3 iteration, you would do more like 10.

Terrain Collision
One exception in all this collision is terrain.
Since TLD is an infinite world, and based on a OctTree, the terrain is not a normal object in the scene.  It changes altitude as a single sheet of triangles, penetrates between OctTree cubes and is generally misbehaved So I treat terrain as a special case in the collision code as a check before checking object collision in the Fine Pass.  I also do not collide physics objects with the terrain as such, I instead take a lowest point in the physics object and just collide that with the start and end points on the ground for the move interval. This makes the assumption that you are moving less than a terrain square tile interval.  Since my terrain is 1 meter squares this assumes you are doing somewhat less than 60 meters per second, which is very fast. So rather than check each triangle along the path of the move, I just check the start and end points.  In practice this works well.  Then I use the surface normal of the ground to adjust velocity.  Inelegant, but efficient.
Movement vectors with convex and concave terrain.  Vectors are exaggerated long. 

Code
Some words about collision code.  Each game physics engine has its own vector type, some float or double scalar type, and its own convention for variable names.  When making a game it is a good idea to use an existing physics engine.  We tried Bullet Physics.  In the end I ended up doing a custom physics engine for TDL for several reasons.

  • We have an infinite world and physics engines are usually geared toward a 'level' in a game which is limited in space.
  • We have all the data structures in place such as an OctTree, terrain definitions, all the objects in the scene, all because we are generating the whole world on-the-fly as you move through it. (Go play Minecraft)  Using and existing engine tends to result in duplicate data for everything.  We have to use memory very efficiently.
  • We are very efficient with CPU use.  Every cycle counts.
There are only two reasons you should write your own physics engine...

  • The above three reasons of you have a very unique game world.
  • Because it is downright fun and you like algorithms, math, and lots of debugging.
So, the following code examples are built on the bullet physics base types such as btVector3 and btScalar (set to double precision) so I don't have to rewrite all the vector math and Quaternions, and Matrices.

---- Code examples coming soon. -----

Oh, BTW, X is to the right of the screen, Y is Up, Z is out of the screen toward the viewer, but collision is in world coordinates, not screen coordinate, so you can think of it as X is East, Y is Up, Z is South.

Profiling
I can't emphasize enough how important it is to do code profiling. I use cRunWatch from raven.  It is very simple.  I was doing lots of work on getting the Fine Pass collision to be fast, and as soon as I profiled TDL, I saw that my Coarse Pass was taking 80% of the total execution time.  I easily then made it much faster and it now gets 32 FPS in debug mode, and > 200 FPS in release mode.

Debugging
Debugging physics is very difficult because everything is happening in a fraction of a second and if you set a break point the timing is all off and the keyboard is no longer game movement input.  The best thing to do is put if statements specifically as breakpoints.  In TDL it looks like this...

volatile int foo = 0;

if(!IsServer && m_isPlayer && m_velocity.getX() > 0.0 and other->m_isAABB)
{
     foo = 1;
}Then you can get to the right place in the game, like running into a wall and set the breakpoint on foo = 1 and then continue and when you start to move forward the breakpoint is triggered.  So get creative on breakpoints in this style.

Another great class I designed is a PeriodicLog.  What it does is every n milliseconds it enables logging for one frame.  Then you can have a log entry for example that prints the solution distance every frame but instead of getting swamped with log lines, you just get one or two per second.

Also at a higher level it helps to be able to turn on wireframe representations of all collision objects and they change color when impacting or moving etc.  This does not help much with low level collision code debugging, but is great for in game whole scene setup issues.

TF

Wednesday, January 18, 2012

The Real Problem with SOPA

So Congress has the SOPA bill before it. There is a huge online protest to stop it, which is being very effective.

But next week? Next month?

The SOPA and PIPA bills are just the latest in a fairly long line of bills that are designed to control the internet and will not be the last. Already there is talk about the next bill and how it will be worded better.

The real problem is not the particular bills. It is that our elected politicians even feel a need for any bill at all.

If I remember my Democracy 101, the politicians represent The People. And I sure don't hear The People clamoring for more internet regulation.

So what is really up? We have old congressmen that do not have a clue about the internet. Some lobbyist comes to them with money and supposed expertise. The congressmen don't understand the hidden (or not so hidden) agenda of the lobbyists to preserve outdated business models. And they sure don't understand the internet at all.

The only real hope to come from today's internet protest is that the pile of phone calls from constituents will give the congressmen such a bad taste in their mouth that in the future they will shy away from any bill with the word 'internet' in it.

And the world will then be a better place.

TF

P.S. A recent article here has the opinion that the latest session of congress has been the least productive in history. Meaning they passed the fewest laws. Since when is passing more laws better? How about we measure congressional success by how many laws they repeal.

Monday, January 9, 2012

A New Kind of Science - Stephen Wolfram

I saw a book called A New Kind of Science online and ordered a used copy.

Meanwhile, I read it on line while waiting for the hard copy to show up. When it did the tome was huge. Yup, I like to buy books by the pound.

Any ways, The web site for the book is here. A New Kind of Science

This is not just any old book. There have been several books of science over the centuries that would be called landmark books. For example Principia Mathematica, The Origin of the Species or Godel, Escher, Bach. This book is right up there with them.

(I realize the hyperbole there and am quite serious.)

Back in Galileo and Newton's time the earthshaking idea they proposed was not just about astronomy, but was the idea that Mathematics could be used to understand and describe the natural world. Before that Mathematics was considered purely abstract, of the mind. Newton came up with calculus and linear equations to describe motion.

Because science has used solvable mathematics, proofs, and equations to discover how Things Work, that has been the class of problems solved. Some problems in biological growth, physics, and such have been impossible to solve, and so have been ignored or swept under the rug.

What Wolfram proposes in A New Kind of Science is that there are many structures and processes in the universe that can not be described by linear math. Instead there are automatons and computer programs that can easily describe and model some processes. Not only does this technique model many processes that were previously 'complex' but great complexity can often flow from very simple processes. A familiar example of this is fractals, where a simple set of rules generate a very detailed and complex drawing.

The other side of this coin is that some problems and models can never be proven no matter how long you simulate or compute them. Science hates to say 'I don't know', but Wolfram clearly states that there are some things that can not be known.

Much of the book centers around the idea of generating complexity by way of ultra simple programs, and finding out just how simple you can get and still get huge complexity.

The book touches on biological organisms, stellar systems, fluid flow, the mind, free will, evolution, religion, society, and of course, mathematics.

So, when reading the book one thing stood out as odd. One is the general use of I, My, This Science, where Wolfram's ideas and science is 'All New and Improved!' and will revolutionize science. This gives it about the same tone as crackpots on the internet with some hair-brained theory that will change the world and has never been know before (tm). The difference with this book is that Wolfram is correct! Books such as The Origin of the Species have none of that tone.

Another thing that will help with this book is to not get stuck on the huge amount of ideas and processes presented. You don't have to understand every concept and program process presented. If I read this and learned to understand every mathematical concept presented in depth it would take years to read.

In the end the core idea is of The Principle of Computational Equivalence. The idea is that it does not matter whether a program is run on a computer, in a biological system, or a flow of atoms in a fluid, it is all the same. This is much like how the equation of gravity, F = MmG/r^2 is equivalent to what actual gravity does. But the equation and computing it is not what gravity is doing in the real world. It is a model that matches what we see, so gives understanding.

The other core idea is that the only way of discovering the outcome of some simple systems is to run the system to completion. For example, it is impossible to determine whether pi ever repeats, except to keep computing digits of pi until it does. (And so far it has not after billions of digits.)

The final paragraph is a gem...

And indeed in the end the Principle of Computational Equivalence encapsulates both the ultimate power and the ultimate weakness of science. For it implies that all the wonders of the universe can in effect be captured by simple rules, yet it shows that there can be no way to know all the consequences of these rules, except in effect just to watch and see how they unfold.

Wow.

TF

Sunday, January 1, 2012

Honda Civic Hybrid 2005 Headlamp Replacement

Ok,

A headlight blew. So I had to replace it.

The high beams are toward the middle of the car and are fairly easy to replace.

In general you rotate the lamp to the left so it unclips. Then push in the tab on the wire harness end and pull the lamp loose.

Don't touch the bulbs you are putting in ever. The oil from your fingers will shatter the bulb once it is on for a while.

The low beams are the next outward from the center of the car. They are difficult.
On the driver side the battery is in the way. On the passenger side the fuel pump (?) is in the way.

The instructions say on the driver side you have to remove the wiper fluid reservoir, but I think the Hybrid is different.

What I did on the passenger side is go in through the wheel well. You use a screwdriver to pry out the two black plastic clips and then bend the wheel well plastic cover down so you can get your hand in. (Major yoga here.) I assume you do the same on the driver side.

The left and right blinker are accessed through the wheel well too.

Oh, you have to turn the steering wheel all the way to one side so the wheel well is exposed and you can get in there.

TF

Monday, December 19, 2011

BMP085 Code Revisited Floating Point Version

Previously I posted a correction to the Bosch BMP085 calculation.
Today I found this paper, BMP085-Calcs.pdf but was unable to find C code to go with the paper.

This code results in more refined altitude increments and uses the whole accuracy.  The old integer code rounds of some bits and makes altitude increments more chunky.

(Very important in some altitude based feedback systems and others)

So I dug in and implemented it...

My variables, then the constant calcs, then the calc, then the test code.


int Baro_ac1, Baro_ac2, Baro_ac3, Baro_b1, Baro_b2, Baro_mb, Baro_mc, Baro_md;
unsigned int Baro_ac4, Baro_ac5, Baro_ac6;

float Baro_fc3;
float Baro_fc4;
float Baro_fb1;
float Baro_fc5;
float Baro_fc6;
float Baro_fmc;
float Baro_fmd;
float Baro_fx0;
float Baro_fx1;
float Baro_fx2;
float Baro_fy0;
float Baro_fy1;
float Baro_fy2;
float Baro_fp0;
float Baro_fp1;
float Baro_fp2;

...

// And calculate derrived constants used by BaroCalcFloat()
// from http://wmrx00.sourceforge.net/Arduino/BMP085-Calcs.pdf
Baro_fc3 = 160.0f * powf(2.0f, -15.0) * Baro_ac3;
Baro_fc4 = 0.001f * powf(2.0f, -15.0) * Baro_ac4;
Baro_fb1 = (160.0f * 160.0f) * powf(2.0f, -30.0) * Baro_b1;

Baro_fc5 = Baro_ac5 * powf(2.0f, -15.0) / 160.0f;
Baro_fc6 = Baro_ac6;
Baro_fmc = Baro_mc * powf(2.0f, 11.0) / (160.0f * 160.0f);
Baro_fmd = Baro_md / 160.0f;

Baro_fx0 = Baro_ac1;
Baro_fx1 = 160.0f * powf(2.0f, -13.0) * Baro_ac2;
Baro_fx2 = (160.0f * 160.0f) * powf(2.0f, -25.0) * Baro_b2;

Baro_fy0 = Baro_fc4 * powf(2.0f, 15.0);
Baro_fy1 = Baro_fc4 * Baro_fc3;
Baro_fy2 = Baro_fc4 * Baro_fb1;

Baro_fp0 = (3791.0f - 8.0f)/1600.0f;
Baro_fp1 = 1.0f - 7357.0f * powf(2.0f, -20.0);
Baro_fp2 = 3038.0f * 100.0f * powf(2.0f, -36.0);

...

float tu = BaroState.rawTemperature;
// Assumes Baro_oss highest precision BARO_OSS_MODE_ULTRA_HIGH_RES.
float pu = BaroState.rawPressure / 256.0f;

float alpha = Baro_fc5 * (tu - Baro_fc6);
float Tc = alpha + Baro_fmc / (alpha + Baro_fmd);
BaroState.temperatureC = Tc;

float s = Tc - 25.0f;
float x = Baro_fx2 * (s * s) + Baro_fx1 * s + Baro_fx0;
float y = Baro_fy2 * (s * s) + Baro_fy1 * s + Baro_fy0;
float z = (pu - x) / y;
BaroState.pressurePa = Baro_fp2 * (z * z) + Baro_fp1 * z + Baro_fp0;

...

void BaroTestMathFloat()
{
mainMessagePrint(ROUTE_DEBUG, "Baro Test Math (float)\r\n");

Baro_ac1 =7911;
Baro_ac2 = -934;
Baro_ac3 = -14306;
Baro_ac4 = 31567;
Baro_ac5 = 25671;
Baro_ac6 = 18974;
Baro_b1 = 5498;
Baro_b2 = 46;
Baro_mb = -32768;
Baro_mc = -11075;
Baro_md = 2432;

Baro_fc3 = 160.0f * powf(2.0f, -15.0) * Baro_ac3;
Baro_fc4 = 0.001f * powf(2.0f, -15.0) * Baro_ac4;
Baro_fb1 = (160.0f * 160.0f) * powf(2.0f, -30.0) * Baro_b1;
mainMessagePrint(ROUTE_DEBUG, "fc3 %f fc4 %f fb1 %f", Baro_fc3, Baro_fc4, Baro_fb1);

Baro_fc5 = Baro_ac5 * powf(2.0f, -15.0) / 160.0f;
Baro_fc6 = Baro_ac6;
Baro_fmc = Baro_mc * powf(2.0f, 11.0) / (160.0f * 160.0f);
Baro_fmd = Baro_md / 160.0f;
mainMessagePrint(ROUTE_DEBUG, "fc5 %f fc6 %f fmc %f fmd %f",
Baro_fc5, Baro_fc6, Baro_fmc, Baro_fmd);

Baro_fx0 = Baro_ac1;
Baro_fx1 = 160.0f * powf(2.0f, -13.0) * Baro_ac2;
Baro_fx2 = (160.0f * 160.0f) * powf(2.0f, -25.0) * Baro_b2;
mainMessagePrint(ROUTE_DEBUG, "x %f %f %f",
Baro_fx0, Baro_fx1, Baro_fx2);

Baro_fy0 = Baro_fc4 * powf(2.0f, 15.0);
Baro_fy1 = Baro_fc4 * Baro_fc3;
Baro_fy2 = Baro_fc4 * Baro_fb1;
mainMessagePrint(ROUTE_DEBUG, "y %f %f %f",
Baro_fy0, Baro_fy1, Baro_fy2);

Baro_fp0 = (3791.0f - 8.0f)/1600.0f;
Baro_fp1 = 1.0f - 7357.0f * powf(2.0f, -20.0);
Baro_fp2 = 3038.0f * 100.0f * powf(2.0f, -36.0);
mainMessagePrint(ROUTE_DEBUG, "p %f %f %f",
Baro_fp0, Baro_fp1, Baro_fp2);

BaroState.rawTemperature = 0x69EC;
BaroState.rawPressure = 0x982FC0;
Baro_oss = BARO_OSS_MODE_ULTRA_HIGH_RES;

float tu = BaroState.rawTemperature;
// Assumes Baro_oss highest precision BARO_OSS_MODE_ULTRA_HIGH_RES.
float pu = BaroState.rawPressure / 256.0f;

mainMessagePrint(ROUTE_DEBUG, "rt %f rp %f", tu, pu);

float alpha = Baro_fc5 * (tu - Baro_fc6);
float Tc = alpha + Baro_fmc / (alpha + Baro_fmd);
BaroState.temperatureC = Tc;
mainMessagePrint(ROUTE_DEBUG, "a %f Tc %f", alpha, Tc);

float s = Tc - 25.0f;
float x = Baro_fx2 * (s * s) + Baro_fx1 * s + Baro_fx0;
float y = Baro_fy2 * (s * s) + Baro_fy1 * s + Baro_fy0;
float z = (pu - x) / y;
BaroState.pressurePa = Baro_fp2 * (z * z) + Baro_fp1 * z + Baro_fp0;
mainMessagePrint(ROUTE_DEBUG, "s %f x %f y %f z %f p %f", s, x, y, z, BaroState.pressurePa);
}



TF

Saturday, November 26, 2011

On "Book Code"

Ok,
So you buy that nifty new book on Game Programming. One would assume the people writing the chapters would know what they are doing, or at least have used the code in an actual game.  Nope.

Always understand what the code does, and be ready for problems arising from the difference between your goal, to write a working commercial game, and the writer's goal, to write a chapter in a book.

I have run into this in two places recently.  One is the network library MTUDP in

Advanced 3D game programming with DirectX 10.0 and the other has been bullet physics. 

MTUDP is a good idea but not done and tested well. (If you want my rewrite, email me) and bullet physics is a university project to try new ideas in physics.  In my case I want to create a open ended infinite zombie game with on-the-fly AI generation of all game content.

There is an echo here of operating system issues.  Was the OS written to make money for a huge corporation, or by hobbyists to make a system they want to work in and use, or as a fashion statement for elite computer users? (Ok, you guess which three OSs to which I am referring.0

Oh, well, end of rant. And end of three weeks of wasted time trying to debug and make a fundamentally broken library work.

TF

Monday, November 21, 2011

MTUDP.cpp More Fixes

WARNING - I should remove this post, but instead will give a warning.  Do not use the MTUDP.cpp library as is.  It has many bugs and makes a fatal mistake.  The user ID is the IP address.  This breaks if there is a fire wall in the way, and there is always a firewall.  I have nearly rewritten the library with very little of the original code left over.

--------------------------------------------------------------------

Ok, turns out the previous post was not a complete fix for the Ack code.
As usual the blog mangles the C code a bit, but you should be able to copy this, else email me for a complete copy.



 unsigned short NetLibHost::ProcessIncomingACKs( char *pBuffer, unsigned short len, DWORD receiveTime )
 {
  UNREFERENCED_PARAMETER(len);
  // Get the number of ACKs in this message, not counting the base ACK.
  unsigned char numAcks, mask, *ptr;
  DWORD         basePacketID, ackID;

  ptr = (unsigned char *)pBuffer;

  // The story: We want to ack each received packet. 
  // But if we have received a series of packets we only
  // have to ack the highest numbered one and we assume all the lesser packets are ack'd too.
  // But then there can be some higher number packets we have received with some gaps.
  // So we send the highest received packet so far with no unreceived packets less than it, then
  // have bytes with the bits representing yes/no acks for higher numbered packets.'
  // So say we have received  3,4,5,6,7,9,10,11,13
  // The base would be 7 since we have all pacjets up to that,
  // Then the next byte is the bits 0x00 | 0x40 | 0x20 | 0x10 | 0x00 | 0x40
  // for packets 8,9,10,11,12,13 respectively.

  // Get the base packet ID, which indicates all the ordered packets received so far.
  memcpy( &basePacketID, ptr, sizeof( DWORD ) );
  ptr += sizeof( DWORD );
  // Get the number of additional ACKs.
  // TODO - Keene - Runs off the end if Ack record was truncated to fit!
  // Solution: don't ever make packets messages that are too large, e.g. 3k
  numAcks = *ptr;
  ptr++;
  // Zero the byte so if there is a one off error in bits, it is not a false ack.
  *ptr = 0x00;
  ackID = d_outQueue.GetLowestID();

#if defined( _DEBUG_VERBOSE )
  OUTPUTREPORT3( "<   Ack low=%04d base=%04d end=%04d\n", ackID, basePacketID, basePacketID + numAcks );
#endif

  // Can get stuck in loop here if corrupt data.
  int debugCount = 0;
  while( ackID <= basePacketID )
  {
   debugCount++;
   // The packet has been ack's so update average ping time.
   ACKPacket( ackID, receiveTime );
   ackID++;
  }

  mask = 0x80;

  // TODO - Keene - Runs off the end if ack record was truncated to fit!
  // Solution: don't ever make packets messages that are too large, e.g. 3k
  while( ackID < basePacketID + numAcks )
  {
   if( mask == 0x00 )
   {
    mask = 0x80;
    ptr++;
    // Zero the byte so if there is a one off error in bits, it is not a false ack.
    *ptr = 0x00;
   }

   if( ( *ptr & mask ) != 0 )
   {
    ACKPacket( ackID, receiveTime );
   }

   mask >>= 1;
   ackID++;
  }

  return (unsigned short)(ptr - (unsigned char *)pBuffer);
 }

 unsigned short NetLibHost::ProcessIncomingReliable( char *pBuffer, unsigned short maxLen, DWORD receiveTime )
 {
  maxLen;
  // Process any messages in the packet.
  DWORD           packetID;
  char            *readPtr;
  unsigned short  length;

  readPtr = pBuffer;
  memcpy( &packetID, readPtr, sizeof( DWORD ) );
  readPtr += sizeof( DWORD );
  memcpy( &length, readPtr, sizeof( unsigned short ) );
  readPtr += sizeof( unsigned short );
#if defined( _DEBUG_VERBOSE )
  OUTPUTREPORT2( "<   %04d (%d) R\n", packetID, length );
#endif
  // If this message is a packet, queue the data
  // to be dealt with by the application later.
  d_inQueue.AddPacket( packetID, (char *)readPtr, length, receiveTime );
  readPtr += length;

  // Should we build an ACK message?
  if( d_inQueue.GetCount() == 0 )
  {
   return (unsigned short)( readPtr - pBuffer );
  }

  // Build the new ACK message.
  DWORD         lowest, highest, ackID;
  unsigned char mask, *ptr;

  lowest = d_inQueue.GetCurrentID();
  highest = d_inQueue.GetHighestID();

  // Cap the highest so as not to overflow the ACK buffer
  // (or spend too much time building ACK messages).
  // (Was bug here because ACK_MAXPERMSG was 256 which does not fit in a byte.)
  if( highest > lowest + ACK_MAXPERMSG )
  {
   highest = lowest + ACK_MAXPERMSG;
  }

#if defined( _DEBUG_VERBOSE )
  OUTPUTREPORT2( " >  %04d ack to %04d ", lowest, highest );
#endif

  // The story: We want to ack each received packet. 
  // But if we have received a series of packets we only
  // have to ack the highest numbered one and we assume all the lesser packets are ack'd too.
  // But then there can be some higher number packets we have received with some gaps.
  // So we send the highest received packet so far with no unreceived packets less than it, then
  // have bytes with the bits representing yes/no acks for higher numbered packets.'
  // So say we have received  3,4,5,6,7,9,10,11,13
  // The base would be 7 since we have all pacjets up to that,
  // Then the next byte is the bits 0x00 | 0x40 | 0x20 | 0x10 | 0x00 | 0x40
  // for packets 8,9,10,11,12,13 respectively.

  ptr = (unsigned char *)d_ackBuffer;
  // Send the base packet ID, which is the ID of the last ordered packet received.
  memcpy( ptr, &lowest, sizeof( DWORD ) );
  ptr += sizeof( DWORD );
  // Add the number of additional ACKs.
  *ptr = (unsigned char)(highest - lowest);
  ptr++;
  // Zero the byte so if there is a one off error in bits, it is not a false ack.
  *ptr = 0x00;

  ackID = lowest + 1;
  mask = 0x80;

  while( ackID <= highest )
  {
   if( mask == 0x00 )
   {
    mask = 0x80;
    ptr++;
    // Zero the byte so if there is a one off error in bits, it is not a false ack.
    *ptr = 0x00;
   }

   // Is there a packet with id 'ackID' ?
   if( d_inQueue.UnorderedPacketIsQueued( ackID ) == true )
   {
    *ptr |= mask;  // There is
   }
   else
   {
    *ptr &= ~mask;  // There isn't
   }

   mask >>= 1;
   ackID++;
  }

#if defined( _DEBUG_VERBOSE )
  OUTPUTREPORT0( "\n" );
#endif

  // Record the ammount of the ackBuffer used.
  d_ackLength = (unsigned short)(ptr - (unsigned char *)d_ackBuffer);
  assert(d_ackLength <= ACK_BUFFERLENGTH);

  // return the number of bytes read from buffer
  return (unsigned short)( readPtr - pBuffer );
 }