Aviator on the BBC Micro

# 3D geometry: SetObjPointCoords (Part 1 of 2)

```       Name: SetObjPointCoords (Part 1 of 2)                         [Show more]
Type: Subroutine
Category: 3D geometry
Summary: Calculate the coordinate for a point within an object
Deep dive: 3D objects
Rotating and translating points in 3D space
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* FireGuns calls SetObjPointCoords
* ProcessLine (Part 6 of 7) calls SetObjPointCoords
* ProcessRunwayLine (Part 2 of 5) calls SetObjPointCoords

Arguments:

GG                   The ID of the point to process and update

matrixNumber         The matrix to use in the calculation:

* 0 = matrix 1

* 9 = matrix 2

* 18 = matrix 3

* 27 = matrix 4

objectAnchorPoint    Point ID of the anchor point to which we add the final
result to

Returns:

xPointHi  etc.       Point GG is set to the point's coordinate in 3D space

showLine             If the calculation overflows, then the coordinate does
not fit within the boundaries of Aviator's 3D world, so
bit 6 is set to indicate that the line containing this
point should not be shown

xTemp1Hi  etc.       The rotated vector from the anchor point to the object
point

.SetObjPointCoords

LDX GG                 \ Set X to the point ID whose coordinates we want to
\ calculate

LDY xObjectPoint,X     \ Set PP to the point's anchor-relative x-coordinate
STY PP                 \ from xObjectPoint

LDY yObjectPoint,X     \ Set QQ to the point's anchor-relative y-coordinate
STY QQ                 \ from yObjectPoint

LDY zObjectPoint,X     \ Set RR (and Y) to the point's anchor-relative
STY RR                 \ z-coordinate from zObjectPoint, which also contains
\ the scale factor in bits 4 to 7

LDA shift4Right,Y      \ Set UU (and A) to Y >> 4, to extract the scale factor
STA UU                 \ from bits 4 to 7 of the zObjectPoint entry

CMP #9                 \ If the scale factor in A >= 9, set bit 7 of K so the
ROR K                  \ result of the call to Multiply4x16 below is doubled,
\ i.e. (G W) is doubled. This gives us an extra factor
\ of 2 on top of the maximum 2^8 factor we would get by
\ left-shifting the result (see part 2 for the scaling
\ code)

LDX #5                 \ We now zero (xTemp1, yTemp1, zTemp1), which is stored
\ in six bytes to give us three 16-bit coordinate values
\ (from xTemp1Lo to zTemp1Hi), so set a counter in X to
\ count the bytes

LDA #0                 \ Set A = 0 to use as our zero

.objp1

STA xTemp1Lo,X         \ Zero the X-th byte of the six-byte coordinate block
\ between xTemp1Lo and zTemp1Hi

DEX                    \ Decrement the loop counter

BPL objp1              \ Loop back until we have zeroed all six bytes

\ We now do the matrix multiplication:
\
\   [ xTemp1 ]     [ m0 m1 m2 ]   [ xObjectPoint ]
\   [ yTemp1 ]  =  [ m3 m4 m5 ] x [ yObjectPoint ]
\   [ zTemp1 ]     [ m6 m7 m8 ]   [ zObjectPoint ]
\
\ We do this in three loops of three, using an outer and
\ inner loop. We set two loop counters, VV and X, for
\ the outer and inner loops respectively. They both
\ iterate through 2, 1 and 0, with VV iterating through
\ zTemp1, yTemp1 and xTemp1, and X iterating through
\ zObjectPoint, yObjectPoint and xObjectPoint
\
\ We also iterate a counter in P for the matrix entries,
\ which counts down from m8 to m0, decreasing by 1 on
\ each iteration
\
\ All the iterators count backwards, so the calculations
\ in order are:
\
\   * zTemp1 += zObjectPoint * m8
\   * zTemp1 += yObjectPoint * m7
\   * zTemp1 += xObjectPoint * m6
\
\   * yTemp1 += zObjectPoint * m5
\   * yTemp1 += yObjectPoint * m4
\   * yTemp1 += xObjectPoint * m3
\
\   * xTemp1 += zObjectPoint * m2
\   * xTemp1 += yObjectPoint * m1
\   * xTemp1 += xObjectPoint * m0
\
\ Or, to switch it around the othee way and plug in the
\ initial value of (xTemp1, yTemp1, zTemp1) = (0, 0, 0),
\ we get:
\
\   * xTemp1 = m0 * xObjectPoint + m1 * yObjectPoint
\                               + m2 * zObjectPoint
\
\   * yTemp1 = m3 * xObjectPoint + m4 * yObjectPoint
\                               + m5 * zObjectPoint
\
\   * zTemp1 = m6 * xObjectPoint + m7 * yObjectPoint
\                               + m8 * zObjectPoint
\
\ which gives us the matrix multiplication that we want
\ to calculate

LDA matrixNumber       \ Set P = matrixNumber + 8
CLC                    \
ADC #8                 \ In the following loop, P counts down from m8 to m0,
STA P                  \ which we implement as an index that counts down from
\ matrixNumber + 8 to matrixNumber, so that it iterates
\ through the correct matrix table
\
\ This works because matrixNumber contains 0 for matrix
\ 1, 9 for matrix 2 and so on, and each matrix table,
\ such as matrix1Lo, contains 9 entries

LDA #2                 \ Set VV = 2, to act as our outer loop counter that
STA VV                 \ iterates through zTemp1, yTemp1 and xTemp1

.objp2

LDX #2                 \ Set X = 2, to act as our inner loop counter that
\ iterates through zObjectPoint, yObjectPoint and
\ xObjectPoint (i.e. RR, QQ and PP)

.objp3

LDY P                  \ Set Y = P, which is the number of the matrix element
\ to multiply next

LDA PP,X               \ Set A = PP, QQ or RR (when X = 0, 1 or 2), which is
\ the correct zObjectPoint, yObjectPoint or xObjectPoint
\ to multiply next

STA I                  \ Store A in I (this doesn't appear to be used)

AND #%00001111         \ Set V = bits 0-3 of A, which removes the scale factor
STA V                  \ in the case of zObjectPoint (the other points always
\ fit into bits 0 to 3)

BEQ objp5              \ If V = 0, jump to objp5 to move on to the next loop,
\ as we already know the result of V * (S R) will be
\ zero

LDA matrix1Hi,Y        \ Set S = P-th entry of matrix1Hi
STA S

LDA matrix1Lo,Y        \ Set R = P-th entry of matrix1Lo
STA R                  \
\ so now (S R) is the 16-bit matrix element that we want
\ to multiply

AND #1                 \ If bit 0 of R is clear, then the matrix entry is
BEQ objp4              \ positive, skip the following three instructions

LDA V                  \ Bit 0 of R is set, which means this matrix entry is
EOR #%10000000         \ negative, so set bit 7 of V to make it negative, to
STA V                  \ give the result of the multiplication below the
\ correct sign

.objp4

STX Q                  \ Store the loop counter in X, so we can retrieve it
\ after the call to Multiply4x16

JSR Multiply4x16       \ Call Multiply4x16 to calculate:
\
\   (G W) = V * (S R) >> 8       if bit 7 of K = 0
\
\   (G W) = V * (S R) >> 7       if bit 7 of K = 1
\
\ K is only set to 1 if the scale factor is 9, in which
\ case we effectively do one of the factors of 2 at this
\ point (and the other 8 in part 2)

LDX Q                  \ Restore the value of X

LDY VV                 \ Fetch the outer loop counter from VV, which points to
\ the relevant xTemp1, yTemp1 or zTemp1 coordinate

LDA W                  \ Add (G W) to the xTemp1 coordinate, starting with the
CLC                    \ low bytes
STA xTemp1Lo,Y

LDA G                  \ And then the high bytes, so we have the following (if
ADC xTemp1Hi,Y         \ we are working with xTemp1 and m0, for example):
STA xTemp1Hi,Y         \
\   xTemp1 += (G W)
\          += V * (S R)
\          += xObjectPoint * m0
\
\ which is the result we want for this element of the
\ matrix multiplication

.objp5

LDA P                  \ If P = matrixNumber then we have done all nine
CMP matrixNumber       \ calculations, so jump down to objp6 to apply the
BEQ objp6              \ correct scale factor

DEC P                  \ Otherwise we have more calculations to do, so
\ decrement P to point to the next matrix entry

DEX                    \ Decrement the inner loop counter to work our way
\ through zObjectPoint, yObjectPoint and xObjectPoint

BPL objp3              \ Loop back until we have worked through all three
\ anchor-relative points

DEC VV                 \ Decrement the outer loop counter to work our way
\ through zTemp1, yTemp1 and xTemp1

JMP objp2              \ Jump back to objp2 to do the next calculation
```