Pinpointing the positions of cars on the track by progress and racing line
As you would expect from a simulation of cars chasing each other round a track, Revs needs to maintain an accurate record of each car's position. In theory we could just use a set of 3D coordinates to store each car's position on the 3D track, and this would appear to make sense, given that the track sections and segments are stored in this way (see the deep dive on building a 3D track from sections and segments).
However, calculations for things like collisions and overtaking are fairly difficult and time-consuming when you're dealing with a fully three-dimensional coordinate system, not least when the directions of "forwards" and "backwards" change completely as cars move around the track. So instead, car positions are stored in terms of segment numbers and progress. The advantage here is that it's a lot easier to manage a car's progress and then use that calculate its 3D coordinates, than it is to keep updating a car's 3D coordinates with its position round a track that twists and turns through 3D space.
Let's see what's involved
Progress and segment numbers
Each car's progress around the track, including that of the current player's car, is stored in the following tables, with one entry for each car:
- objTrackSection contains the section number containing the car (from 0 to 23 in Silverstone, as given in trackSectionCount)
- objSectionSegmt contains the segment number containing the car within the current section (starting from 0 at the start of each new section, and going up to the number of segments in the section, as given in the section's trackSectionSize)
- (objectSegmentHi objectSegmentLo) contains the segment number containing the car, as the total number of segments from the starting line (from 0 to 1023 in Silverstone, as given in trackLength)
- carProgress is effectively a fractional part of the car's progress through the segment, with 0 being the start of the segment and 255 the end of the segment
These values are all related. As the car drives around the track, the value of carProgress is updated, increasing if the car is moving forwards along the track, and decreasing is it's moving backwards. Each iteration around the main driving loop, carProgress is updated by adding carSpeedHi each time, where carSpeedHi is the high byte of the car's current speed in (carSpeedHi carSpeedLo). As carProgress is effectively the progress fraction through one segment, carSpeedHi is therefore the speed of the car in terms of 1/256-ths of a segment.
When carProgress rolls over from 255 to 0, both objSectionSegmt and (objectSegmentHi objectSegmentLo) are incremented by one segment. If objSectionSegmt reaches the number of segments in this section, which is given in trackSectionSize, then it wraps round to zero to start counting from the next section, and objTrackSection is incremented to move on the next section (and if the section number reaches the total number of sections in trackSectionCount, it too wraps round to zero). Meanwhile, (objectSegmentHi objectSegmentLo) keeps counting segments until it reaches trackLength (1024 for Silverstone), at which point it also wraps around to zero.
See the MovePlayerSegment routine for details of how the car is moved between segments, and part 2 of the MoveCars routine for the code that applies the forward speed. The CompareCarSegments routine works out the order of cars by working with progress and segment numbers, and demonstrates just how simple it is to work out car orders using this approach - it's much easier than it would be if we were having to work it out in 3D geometry.
On top of the forward progress, each car has a value in the carRacingLine table, which determines the left-right position of the car on the track - in essence, this is the value of the left-right x-coordinate when seen from the point of view of the driver. As it determines how far each car is to the left or right on the track, it's effectively the car's racing line. It has a value between 0 and 255, as follows:
- 0 is full right
- 128 is the centre line
- 255 is full left
Bit 7 of carRacingLine is therefore set if the car is in the left half the track, and clear if it's in the right half.
Between the car's progress and the car's racing line, we can place cars on the track with pinpoint accuracy, to within 1/256 of the segment size. In the deep dive on building a 3D track from sections and segments we estimated that the average segment is around 4.6m long, so the cars are placed with an accuracy of 4.6m / 256 = 0.018m, or just under 2cm. This fits nicely within the 3.8cm grid of 16-bit coordinates, so technically speaking, the progress system places cars within the segment to twice the accuracy of the underlying coordinate system.
In other words, if you keep bumping into other cars in Revs, you can't blame inaccuracies in the collision code. Revs knows exactly where its cars are, even if you don't...