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

JSR Multiply8x8        \ Set (A T) = A * U

EOR #&FF               \ Negate A again (so it is now the correct sign for the
CLC                    \ multiplication)

BCS bcar3              \ If the addition just overflowed, then the result is
\ now positive, which means V is already the correct

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)
\
\ 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

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