# 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                 \ (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|
```