Skip to navigation

Aviator on the BBC Micro

3D geometry: ProjectPoint (Part 1 of 3)

Name: ProjectPoint (Part 1 of 3) [Show more] Type: Subroutine Category: 3D geometry Summary: Project a point onto the screen (i.e. convert from 3D coordinates to screen coordinates)
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * ProcessLinesToShow calls ProjectPoint

This routine projects a point in 3D space onto the screen. If the point in 3D space is (xPoint, yPoint, zPoint), then the resulting projection is at pixel coordinate (x, y) on-screen, where: x = 80 + 256 * (xPoint / zPoint) y = 96 + 256 * (2 * yPoint / zPoint) The result is stored in (xPoint, yPoint), while zPoint is left alone so we can check its sign. The coordinates are all signed 16-bit values. Also, various bits are set in the point's status byte. These describe the relationship between the x- and y-coordinates of the point, which we can use to quickly determine whether the line containing this point is on-screen. We also set a bit that records that we have projected this point, so we don't repeat the process during this iteration of the main loop. The routine breaks down as follows: * Part 1 sets the various bits in the point's status byte * Part 2 calculates the divisions by zPoint * Part 3 calculates the addition of (80, 96) Arguments: GG The point ID to process xPoint+GG The point's x-coordinate in 3D space yPoint+GG The point's y-coordinate in 3D space zPoint+GG The point's z-coordinate in 3D space Returns: pointStatus+GG The following bits are set as follows, so they can be used in ProcessLinesToShow to determine the visibility of the line containing this point: * Bit 0: * 0 = we have not projected this point before * 1 = we have already projected this point * Bit 2: * 0 = yPoint is positive * 1 = yPoint is negative * Bit 3: * 0 = xPoint is positive * 1 = xPoint is negative * Bit 4: * 0 = |yPoint| * 2 < |zPoint| * 1 = |yPoint| * 2 >= |zPoint| * Bit 5: * 0 = |xPoint| < |zPoint| * 1 = |xPoint| >= |zPoint| xPoint+GG The pixel x-coordinate of the point, projected onto the screen yPoint+GG The pixel y-coordinate of the point, projected onto the screen
.ProjectPoint LDX GG \ Set X to the point ID to process LDA #0 \ Set N = 0, which we use for collecting set bits to STA N \ apply to the point's status byte LDA #%00010000 \ Set bit 4 of R, for use in ScaleUp STA R LDA pointStatus,X \ Set Y = A = the status byte for the point to process TAY AND #1 \ If bit 0 of the status byte is clear, then we have not BEQ proj1 \ yet projected this point onto the screen, so jump to \ proj1 to skip the following instruction and move on \ to the projection process RTS \ Bit 0 of the point's status byte is set, which means \ we have already projected this point, so return from \ the subroutine .proj1 TYA \ Set bit 0 of the point's status byte in pointStatus ORA #1 \ to indicate that we have now called ProjectPoint for STA pointStatus,X \ this point, i.e. the point has been projected (or soon \ will be, anyway) \ The first step is to set the four bits in the point's \ status byte, which we will return from the subroutine \ to be used in ProcessLinesToShow for determining the \ visibility of the line containing this point \ We first set (Q P) = |zPoint| \ \ bumping (Q P) up to 1 if zPoint = 0, so (Q P) has a \ minimum value of 1 LDA zPointHi,X \ Set A = the high byte of the point's z-coordinate BMI proj4 \ If the z-coordinate is negative, jump to proj4 to \ change its sign STA Q \ Set Q = the high byte of the point's z-coordinate BEQ proj2 \ If the high byte of the z-coordinate is 0, jump to \ proj2 LDA zPointLo,X \ Set P = the low byte of the point's z-coordinate, so STA P \ (Q P) now contains the point's z-coordinate, as \ required JMP proj5 \ Jump to proj5 to move on to the point's x-coordinate .proj2 \ If we get here then the high byte of the z-coordinate \ is 0 LDA zPointLo,X \ Set A = the low byte of the point's z-coordinate BNE proj3 \ If the low byte of the point's z-coordinate is \ non-zero, skip the following instructions \ If we get here then both the high and low bytes of the \ z-coordinate are 0, so zPoint = 0 LDA #1 \ Set A = 1 to use as the low byte of (Q P), so we will \ end up with (Q P) = 1, as required .proj3 STA P \ Set P = the low byte of the point's z-coordinate, or \ 1 if the low byte was zero, so (Q P) now contains \ |zPoint|, with a minimum value of 1, as required JMP proj5 \ Jump to proj5 to move on to the point's x-coordinate .proj4 \ If we get here then the high byte of the z-coordinate \ is negative, so now we negate it to make it positive LDA #0 \ Negate zPoint and store it in (Q P), starting with the SEC \ low bytes SBC zPointLo,X STA P LDA #0 \ And then the high bytes, so (Q P) now contains the SBC zPointHi,X \ point's z-coordinate made positive, i.e. |zPoint|, STA Q \ as required .proj5 \ We now set (QQ PP) = |xPoint| LDA xPointHi,X \ Set A = the high byte of the point's x-coordinate BMI proj6 \ If the x-coordinate is negative, jump to proj6 to \ change its sign STA QQ \ Set QQ = the high byte of the point's x-coordinate LDA xPointLo,X \ Set PP = the low byte of the point's x-coordinate, so STA PP \ so (QQ PP) now contains |xPoint|, as required JMP proj7 \ Jump to proj7 to move on to the point's y-coordinate .proj6 \ If we get here then the high byte of the x-coordinate \ is negative, so now we negate it to make it positive LDA #0 \ Negate xPoint and store it in (QQ PP), starting with SEC \ the low bytes SBC xPointLo,X STA PP LDA #0 \ And then the high bytes, so (QQ PP) now contains the SBC xPointHi,X \ point's x-coordinate made positive, i.e. |xPoint|, STA QQ \ as required LDA N \ Set bit 3 of N (so we set bit 3 of the point's status ORA #%00001000 \ byte when we're done) to indicate that xPoint is STA N \ negative .proj7 \ We now set (SS RR) = |yPoint| * 2 LDA yPointHi,X \ Set A = the high byte of the point's y-coordinate BMI proj8 \ If the y-coordinate is negative, jump to proj8 to \ change its sign STA SS \ Set SS = the high byte of the point's y-coordinate LDA yPointLo,X \ Set A = the high byte of the point's y-coordinate, so \ we now have: \ \ (SS A) = |yPoint| ASL A \ Set (SS RR) = (SS A) * 2 ROL SS \ = |yPoint| * 2 STA RR JMP proj9 \ Jump to proj9 to move on to the next stage .proj8 \ If we get here then the high byte of the y-coordinate \ is negative, so now we negate it to make it positive \ before multiplying by 2 LDA #0 \ Negate yPoint and store it in (A RR), starting with SEC \ the low bytes SBC yPointLo,X STA RR LDA #0 \ And then the high bytes, so (A RR) now contains the SBC yPointHi,X \ point's y-coordinate made positive, so \ we now have: \ \ (A RR) = |yPoint| ASL RR \ Set (SS RR) = (A RR) * 2 ROL A \ = |yPoint| * 2 STA SS LDA N \ Set bit 2 of N (so we set bit 2 of the point's status ORA #%00000100 \ byte when we're done) to indicate that yPoint is STA N \ negative .proj9 \ We now set bit 5 of the status byte in N by comparing \ the |xPoint| and |zPoint| values we just calculated LDA QQ \ If QQ < Q, jump to proj11 to leave bit 5 clear CMP Q BCC proj11 BNE proj10 \ If QQ > Q, jump to proj10 to set bit 5 \ If we get here then QQ = Q LDA PP \ If PP < P, jump to proj11 to leave bit 5 clear CMP P BCC proj11 .proj10 \ If we get here then QQ = Q and PP >= P, which means \ (QQ PP) >= (Q P), i.e. |xPoint| >= |zPoint| LDA #%00100000 \ Set bit 5 of N (so we set bit 5 of the point's status ORA N \ byte when we're done) to indicate that STA N \ |xPoint| >= |zPoint| .proj11 \ We now set bit 4 of the status byte in N by comparing \ the |yPoint| and |zPoint| values we just calculated LDA SS \ If SS < Q, jump to proj13 to leave bit 4 clear CMP Q BCC proj13 BNE proj12 \ If SS > Q, jump to proj12 to set bit 4 \ If we get here then SS = Q LDA RR \ If RR < P, jump to proj13 to leave bit 4 clear CMP P BCC proj13 .proj12 \ If we get here then SS = Q and RR >= P, which means \ (SS RR) >= (Q P), i.e. |yPoint| * 2 >= |zPoint| LDA N \ Set bit 4 of N (so we set bit 4 of the point's status ORA #%00010000 \ byte when we're done) to indicate that STA N \ |yPoint| * 2 >= |zPoint|