Skip to navigation

BBC Micro Elite

Drawing ships: LL9 (Part 5 of 11)

Name: LL9 (Part 5 of 11) [View in context] Type: Subroutine Category: Drawing ships Summary: Draw ship: Calculate the visibility of each of the ship's faces Deep dive: Back-face culling
.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 BEQ LL91 \ If z_hi = 0 then jump to LL91 \ 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 ADC 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 ADC #4 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 \ to do, so jump to LL94 to exit the loop 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