Skip to navigation

Revs on the BBC Micro

3D objects: BuildCarObjects (Part 1 of 3)

Name: BuildCarObjects (Part 1 of 3) [Show more] Type: Subroutine Category: 3D objects Summary: Calculate the 3D coordinate of the specified car Deep dive: Drawing a 3D car from 2D parts
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * BuildPlayerCar calls BuildCarObjects * BuildVisibleCar calls BuildCarObjects

This routine calculates the 3D coordinate of the specified car, given its progress through the current segment and the racing line, as follows: [ xCoord2 ] [ xSegmentCoordI ] [ xTrackSegmentI ] [ yCoord2 ] = [ ySegmentCoordI mod 32 ] + [ yTrackSegmentI ] * carProgress [ zCoord2 ] [ zSegmentCoordI ] [ zTrackSegmentI ] [ xTrackSegmentO ] + [ 0 ] * carRacingLine * 4 [ zTrackSegmentO ] [ 0 ] + [ 144 ] [ 0 ] In the above: * xTrackSegmentI is the inner segment vector for the car's position * xTrackSegmentO is the outer segment vector for the car's position * xSegmentCoordI is the coordinate for the start of the car's track segment The routine then calculates the yaw and pitch angles for the car object (or objects). This part calculates the 3D coordinate of the car along the inside edge of the track, i.e. the first part of the above: [ xCoord2 ] [ xSegmentCoordI ] [ xTrackSegmentI ] [ yCoord2 ] = [ ySegmentCoordI mod 32 ] + [ yTrackSegmentI ] * carProgress [ zCoord2 ] [ zSegmentCoordI ] [ zTrackSegmentI ] The mod 32 part caps the y-coordinate to a maximum of 8192.
Arguments: X The driver number of the car object to build thisDriver Same as X Y The index * 3 of the track segment to use for the calculation
Returns: X X is set to the driver number in thisDriver xCoord2 Contains the object's 3D coordinates (for the one-object car) or the coordinates of the rear tyres (for the four-object car objYawAngle The object's yaw angle
.BuildCarObjects LDA segmentVector,Y \ Fetch the segment vector number for track segment Y, \ which gives us the segment vector number of the car \ object we want to build STA vectorNumber \ Store the segment vector number in vectorNumber so we \ can retrieve it in parts 2 and 3 STY T \ Store the index * 3 of the track segment in T TAY \ Set Y to the segment vector number of the car object LDA carProgress,X \ Set TT to the lowest byte of the car's progress STA TT \ through the current segment LDA carRacingLine,X \ Set UU to the car's current racing line STA UU LDA xTrackSegmentI,Y \ Set VV to the 3D x-coordinate of the inner segment STA VV \ vector for the car object LDA yTrackSegmentI,Y \ Set VV+1 to the 3D y-coordinate of the inner segment STA VV+1 \ vector for the car object LDA zTrackSegmentI,Y \ Set VV+2 to the 3D z-coordinate of the inner segment STA VV+2 \ vector for the car object \ We now calculate the following: \ \ xCoord2 = xSegmentCoordI \ + xTrackSegmentI * carProgress LDX #0 \ We are about to work our way through the three axes, \ so set X = 0 to use as an axis counter, working \ through the three axes x, y, z using X = 0, 1, 2 \ \ The comments below are for the x-axis LDA TT \ Set U = TT STA U \ = the lowest byte of the car's progress through \ the current segment LDY T \ Set Y to the index * 3 of the track segment that we \ stored above .bcar1 LDA #0 \ Set (V A) = 0 STA V LDA VV,X \ Set A to the x-coordinate of the inner segment vector \ We now calculate (V A T) = A * U, making sure we get \ the signs right BPL bcar2 \ If A is positive, jump to bcar2 to multiply A and U as \ they are \ If we get here then A is negative, so we need to apply \ the correct sign to the multiplication EOR #&FF \ Negate A (so it is now positive) CLC ADC #1 JSR Multiply8x8 \ Set (A T) = A * U EOR #&FF \ Negate A again (so it is now the correct sign for the CLC \ multiplication) ADC #1 BCS bcar3 \ If the addition just overflowed, then the result is \ now positive, which means V is already the correct \ high byte for (V A T), so jump to bcar3 DEC V \ Otherwise, decrement V to &FF so it's the correct \ high byte for (V A T) BCC bcar3 \ Jump to bcar3 (this BCC is effectively a JMP as we \ just passed through a BCS) .bcar2 JSR Multiply8x8 \ Set (A T) = A * U .bcar3 \ By this point, we have the following, signed result: \ \ (V A T) = A * U \ = xTrackSegmentI * carProgress \ \ We now add (V A) to the Y-th entry in xSegmentCoordI, \ which is the xSegmentCoordI entry for the track \ segment passed to the routine (Y contains the \ index * 3 of the track segment), and store the result \ in xCoord2 \ \ For the y-axis of the coordinate, i.e. for the \ multiplication: \ \ yCoord2 = ySegmentCoordI + (V A) \ \ then we add ySegmentCoordIHi mod 32 instead of \ ySegmentCoordIHi CLC \ Set (xCoord2Hi xCoord2Lo) ADC xSegmentCoordILo,Y \ = (xSegmentCoordIHi xSegmentCoordILo) + (V A) STA xCoord2Lo,X \ \ starting with the low bytes LDA xSegmentCoordIHi,Y \ And then the high bytes (though with a short interlude \ for when X = 1, when we add ySegmentCoordIHi mod 32 \ instead) PHP \ Store the C flag on the stack so we can retrieve it \ when adding the high bytes below CPX #1 \ If X = 1, set A = A mod 32 BNE bcar4 \ = ySegmentCoordIHi mod 32 AND #31 \ \ This caps the y-coordinate to a maximum of 8192 .bcar4 PLP \ Now we can finally add the high bytes ADC V STA xCoord2Hi,X INY \ Increment the axis pointer for xSegmentCoordI INX \ Increment the axis pointer for xCoord2 CPX #3 \ Loop back until X has looped through all three axes BNE bcar1