# Drawing ships: LL9 (Part 5 of 12)

## [BBC Micro disc version, Flight]

```       Name: LL9 (Part 5 of 12)                                      [Show more]
Type: Subroutine
Category: Drawing ships
Summary: Draw ship: Calculate the visibility of each of the ship's faces
Deep dive: Drawing ships
Back-face culling
Context: See this subroutine in context in the source code
References: No direct references to this subroutine in this source file

.EE29

LDA (XX0),Y            \ We set Y to 12 above before jumping down to EE29, so
\ this fetches byte #12 of the ship's blueprint, which
\ contains the number of faces * 4

BEQ LL41               \ If there are no faces in this ship, jump to LL42 (via
\ LL41) to skip the face visibility calculations

STA XX20               \ Set A = the number of faces * 4

LDY #18                \ Fetch byte #18 of the ship's blueprint, which contains
LDA (XX0),Y            \ the factor by which we scale the face normals, into X
TAX

LDA XX18+7             \ Set A = z_hi

.LL90

TAY                    \ Set Y = z_hi

\ The following is a loop that jumps back to LL90+3,
\ i.e. here. LL90 is only used for this loop, so it's a
\ bit of a strange use of the label here

INX                    \ Increment the scale factor in X

LSR XX18+4             \ Divide (y_hi y_lo) by 2
ROR XX18+3

LSR XX18+1             \ Divide (x_hi x_lo) by 2
ROR XX18

LSR A                  \ Divide (z_hi z_lo) by 2 (as A contains z_hi)
ROR XX18+6

TAY                    \ Set Y = z_hi

BNE LL90+3             \ If Y is non-zero, loop back to LL90+3 to divide the
\ three coordinates until z_hi is 0

.LL91

\ By this point z_hi is 0 and X contains the number of
\ right shifts we had to do, plus the scale factor from
\ the blueprint

STX XX17               \ Store the updated scale factor in XX17

LDA XX18+8             \ Set XX15+5 = z_sign
STA XX15+5

LDA XX18               \ Set XX15(1 0) = (x_sign x_lo)
STA XX15
LDA XX18+2
STA XX15+1

LDA XX18+3             \ Set XX15(3 2) = (y_sign y_lo)
STA XX15+2
LDA XX18+5
STA XX15+3

LDA XX18+6             \ Set XX15+4 = z_lo, so now XX15(5 4) = (z_sign z_lo)
STA XX15+4

JSR LL51               \ Call LL51 to set XX12 to the dot products of XX15 and
\ XX16, which we'll call dot_sidev, dot_roofv and
\ dot_nosev:
\
\   XX12(1 0) = [x y z] . sidev
\             = (dot_sidev_sign dot_sidev_lo)
\             = dot_sidev
\
\   XX12(3 2) = [x y z] . roofv
\             = (dot_roofv_sign dot_roofv_lo)
\             = dot_roofv
\
\   XX12(5 4) = [x y z] . nosev
\             = (dot_nosev_sign dot_nosev_lo)
\             = dot_nosev

LDA XX12               \ Set XX18(2 0) = dot_sidev
STA XX18
LDA XX12+1
STA XX18+2

LDA XX12+2             \ Set XX18(5 3) = dot_roofv
STA XX18+3
LDA XX12+3
STA XX18+5

LDA XX12+4             \ Set XX18(8 6) = dot_nosev
STA XX18+6
LDA XX12+5
STA XX18+8

LDY #4                 \ Fetch byte #4 of the ship's blueprint, which contains
LDA (XX0),Y            \ the low byte of the offset to the faces data

CLC                    \ Set V = low byte faces offset + XX0
STA V

LDY #17                \ Fetch byte #17 of the ship's blueprint, which contains
LDA (XX0),Y            \ the high byte of the offset to the faces data

ADC XX0+1              \ Set V+1 = high byte faces offset + XX0+1
STA V+1                \
\ So V(1 0) now points to the start of the faces data
\ for this ship

LDY #0                 \ We're now going to loop through all the faces for this
\ ship, so set a counter in Y, starting from 0, which we
\ will increment by 4 each loop to step through the
\ four bytes of data for each face

.LL86

LDA (V),Y              \ Fetch byte #0 for this face into A, so:
\
\   A = %xyz vvvvv, where:
\
\     * Bits 0-4 = visibility distance, beyond which the
\       face is always shown
\
\     * Bits 7-5 = the sign bits of normal_x, normal_y
\       and normal_z

STA XX12+1             \ Store byte #0 in XX12+1, so XX12+1 now has the sign of
\ normal_x

AND #%00011111         \ Extract bits 0-4 to give the visibility distance

CMP XX4                \ If XX4 <= the visibility distance, where XX4 contains
BCS LL87               \ the ship's z-distance reduced to 0-31 (which we set in
\ part 2), skip to LL87 as this face is close enough
\ that we have to test its visibility using the face
\ normals

\ Otherwise this face is within range and is therefore
\ always shown

TYA                    \ Set X = Y / 4
LSR A                  \       = the number of this face * 4 /4
LSR A                  \       = the number of this face
TAX

LDA #255               \ Set the X-th byte of XX2 to 255 to denote that this
STA XX2,X              \ face is visible

TYA                    \ Set Y = Y + 4 to point to the next face
TAY

JMP LL88               \ Jump down to LL88 to skip the following, as we don't
\ need to test the face normals

.LL87

LDA XX12+1             \ Fetch byte #0 for this face into A

ASL A                  \ Shift A left and store it, so XX12+3 now has the sign
STA XX12+3             \ of normal_y

ASL A                  \ Shift A left and store it, so XX12+5 now has the sign
STA XX12+5             \ of normal_z

INY                    \ Increment Y to point to byte #1

LDA (V),Y              \ Fetch byte #1 for this face and store in XX12, so
STA XX12               \ XX12 = normal_x

INY                    \ Increment Y to point to byte #2

LDA (V),Y              \ Fetch byte #2 for this face and store in XX12+2, so
STA XX12+2             \ XX12+2 = normal_y

INY                    \ Increment Y to point to byte #3

LDA (V),Y              \ Fetch byte #3 for this face and store in XX12+4, so
STA XX12+4             \ XX12+4 = normal_z

\ So we now have:
\
\   XX12(1 0) = (normal_x_sign normal_x)
\
\   XX12(3 2) = (normal_y_sign normal_y)
\
\   XX12(5 4) = (normal_z_sign normal_z)

LDX XX17               \ If XX17 < 4 then jump to LL92, otherwise we stored a
CPX #4                 \ larger scale factor above
BCC LL92

.LL143

LDA XX18               \ Set XX15(1 0) = XX18(2 0)
STA XX15               \               = dot_sidev
LDA XX18+2
STA XX15+1

LDA XX18+3             \ Set XX15(3 2) = XX18(5 3)
STA XX15+2             \               = dot_roofv
LDA XX18+5
STA XX15+3

LDA XX18+6             \ Set XX15(5 4) = XX18(8 6)
STA XX15+4             \               = dot_nosev
LDA XX18+8
STA XX15+5

JMP LL89               \ Jump down to LL89

.ovflw

\ If we get here then the addition below overflowed, so
\ we halve the dot products and normal vector

LSR XX18               \ Divide dot_sidev_lo by 2, so dot_sidev = dot_sidev / 2

LSR XX18+6             \ Divide dot_nosev_lo by 2, so dot_nosev = dot_nosev / 2

LSR XX18+3             \ Divide dot_roofv_lo by 2, so dot_roofv = dot_roofv / 2

LDX #1                 \ Set X = 1 so when we fall through into LL92, we divide
\ the normal vector by 2 as well

.LL92

\ We jump here from above with the scale factor in X,
\ and now we apply it by scaling the normal vector down
\ by a factor of 2^X (i.e. divide by 2^X)

LDA XX12               \ Set XX15 = normal_x
STA XX15

LDA XX12+2             \ Set XX15+2 = normal_y
STA XX15+2

LDA XX12+4             \ Set A = normal_z

.LL93

DEX                    \ Decrement the scale factor in X

BMI LL94               \ If X was 0 before the decrement, there is no scaling

LSR XX15               \ Set XX15 = XX15 / 2
\          = normal_x / 2

LSR XX15+2             \ Set XX15+2 = XX15+2 / 2
\            = normal_y / 2

LSR A                  \ Set A = A / 2
\       = normal_z / 2

DEX                    \ Decrement the scale factor in X

BPL LL93+3             \ If we have more scaling to do, loop back up to the
\ first LSR above until the normal vector is scaled down

.LL94

STA R                  \ Set R = normal_z

LDA XX12+5             \ Set S = normal_z_sign
STA S

LDA XX18+6             \ Set Q = dot_nosev_lo
STA Q

LDA XX18+8             \ Set A = dot_nosev_sign

JSR LL38               \ Set (S A) = (S R) + (A Q)
\           = normal_z + dot_nosev
\
\ setting the sign of the result in S

BCS ovflw              \ If the addition overflowed, jump up to ovflw to divide
\ both the normal vector and dot products by 2 and try
\ again

STA XX15+4             \ Set XX15(5 4) = (S A)
LDA S                  \               = normal_z + dot_nosev
STA XX15+5

LDA XX15               \ Set R = normal_x
STA R

LDA XX12+1             \ Set S = normal_x_sign
STA S

LDA XX18               \ Set Q = dot_sidev_lo
STA Q

LDA XX18+2             \ Set A = dot_sidev_sign

JSR LL38               \ Set (S A) = (S R) + (A Q)
\           = normal_x + dot_sidev
\
\ setting the sign of the result in S

BCS ovflw              \ If the addition overflowed, jump up to ovflw to divide
\ both the normal vector and dot products by 2 and try
\ again

STA XX15               \ Set XX15(1 0) = (S A)
LDA S                  \               = normal_x + dot_sidev
STA XX15+1

LDA XX15+2             \ Set R = normal_y
STA R

LDA XX12+3             \ Set S = normal_y_sign
STA S

LDA XX18+3             \ Set Q = dot_roofv_lo
STA Q

LDA XX18+5             \ Set A = dot_roofv_sign

JSR LL38               \ Set (S A) = (S R) + (A Q)
\           = normal_y + dot_roofv

BCS ovflw              \ If the addition overflowed, jump up to ovflw to divide
\ both the normal vector and dot products by 2 and try
\ again

STA XX15+2             \ Set XX15(3 2) = (S A)
LDA S                  \               = normal_y + dot_roofv
STA XX15+3

.LL89

\ When we get here, we have set up the following:
\
\   XX15(1 0) = normal_x + dot_sidev
\             = normal_x + [x y z] . sidev
\
\   XX15(3 2) = normal_y + dot_roofv
\             = normal_y + [x y z] . roofv
\
\   XX15(5 4) = normal_z + dot_nosev
\             = normal_z + [x y z] . nosev
\
\ and:
\
\   XX12(1 0) = (normal_x_sign normal_x)
\
\   XX12(3 2) = (normal_y_sign normal_y)
\
\   XX12(5 4) = (normal_z_sign normal_z)
\
\ We now calculate the dot product XX12 . XX15 to tell
\ us whether or not this face is visible

LDA XX12               \ Set Q = XX12
STA Q

LDA XX15               \ Set A = XX15

JSR FMLTU              \ Set T = A * Q / 256
STA T                  \       = XX15 * XX12 / 256

LDA XX12+1             \ Set S = sign of XX15(1 0) * XX12(1 0), so:
EOR XX15+1             \
STA S                  \   (S T) = XX15(1 0) * XX12(1 0) / 256

LDA XX12+2             \ Set Q = XX12+2
STA Q

LDA XX15+2             \ Set A = XX15+2

JSR FMLTU              \ Set Q = A * Q
STA Q                  \       = XX15+2 * XX12+2 / 256

LDA T                  \ Set T = R, so now:
STA R                  \
\   (S R) = XX15(1 0) * XX12(1 0) / 256

LDA XX12+3             \ Set A = sign of XX15+3 * XX12+3, so:
EOR XX15+3             \
\   (A Q) = XX15(3 2) * XX12(3 2) / 256

JSR LL38               \ Set (S T) = (S R) + (A Q)
STA T                  \           =   XX15(1 0) * XX12(1 0) / 256
\             + XX15(3 2) * XX12(3 2) / 256

LDA XX12+4             \ Set Q = XX12+4
STA Q

LDA XX15+4             \ Set A = XX15+4

JSR FMLTU              \ Set Q = A * Q
STA Q                  \       = XX15+4 * XX12+4 / 256

LDA T                  \ Set T = R, so now:
STA R                  \
\   (S R) =   XX15(1 0) * XX12(1 0) / 256
\           + XX15(3 2) * XX12(3 2) / 256

LDA XX15+5             \ Set A = sign of XX15+5 * XX12+5, so:
EOR XX12+5             \
\   (A Q) = XX15(5 4) * XX12(5 4) / 256

JSR LL38               \ Set (S A) = (S R) + (A Q)
\           =   XX15(1 0) * XX12(1 0) / 256
\             + XX15(3 2) * XX12(3 2) / 256
\             + XX15(5 4) * XX12(5 4) / 256

PHA                    \ Push the result A onto the stack, so the stack now
\ contains the dot product XX12 . XX15

TYA                    \ Set X = Y / 4
LSR A                  \       = the number of this face * 4 /4
LSR A                  \       = the number of this face
TAX

PLA                    \ Pull the dot product off the stack into A

BIT S                  \ If bit 7 of S is set, i.e. the dot product is
BMI P%+4               \ negative, then this face is visible as its normal is
\ pointing towards us, so skip the following instruction

LDA #0                 \ Otherwise the face is not visible, so set A = 0 so we
\ can store this to mean "not visible"

STA XX2,X              \ Store the face's visibility in the X-th byte of XX2

INY                    \ Above we incremented Y to point to byte #3, so this
\ increments Y to point to byte #4, i.e. byte #0 of the
\ next face

.LL88

CPY XX20               \ If Y >= XX20, the number of faces * 4, jump down to
BCS LL42               \ LL42 to move on to the

JMP LL86               \ Otherwise loop back to LL86 to work out the visibility
\ of the next face
```