# 3D objects: GetObjPitchAngle

```       Name: GetObjPitchAngle                                        [Show more]
Type: Subroutine
Category: 3D objects
Summary: Calculate an object's pitch angle
Deep dive: Pitch and yaw angles
Context: See this subroutine in context in the source code
References: This subroutine is called as follows:
* GetObjectAngles calls via GetObjPitchAngle-2
* GetSectionAngles (Part 3 of 3) calls via GetObjPitchAngle-2
* GetSegmentAngles (Part 1 of 3) calls via GetObjPitchAngle-2
* GetSegmentAngles (Part 2 of 3) calls via GetObjPitchAngle-2

Arguments:

X                    The offset of the variable to use for the object's 3D
coordinates

* &F4 = yHelmetCoord

* &FA = yCoord1

* &FD = yCoord2

Y                    The offset of the second variable to use:

* 0 = yPlayerCoord

(L K)                The result from GetObjectDistance, which is called
between GetObjYawAngle and GetObjPitchAngle

Returns:

LL                   The pitch angle of the object

A                    The pitch angle of the object (same as LL)

scaleUp              The scale up factor for the object

scaleDown            The scale down factor for the object

C flag               Is the object visible on-screen:

* Clear if the object is on-screen

* Set if it isn't on-screen

N flag               Set according to the y-coordinate, so a BPL following
the call will branch if the y-coordinate is positive

Other entry points:

GetObjPitchAngle-2   Use yPlayerCoord (Y = 0)

LDY #0                 \ Use xPlayerCoord for the second variable when calling
\ the routine via GetObjPitchAngle-2

.GetObjPitchAngle

\ The vectors used in this routine are configured by the
\ values of X and Y, but for the purposes of simplicity,
\ the comments will assume the following:
\
\   * X = &FD, yCoord2
\
\   * Y = 0, yPlayerCoord

LDA ySegmentCoordILo,X \ Set (WW QQ) = yCoord2 - yPlayerCoord
SEC                    \
SBC yPlayerCoordHi,Y   \ starting with the low bytes
STA QQ

LDA ySegmentCoordIHi,X \ And then the high bytes
SBC yPlayerCoordTop,Y
STA WW

\ Let's call this difference in y-coordinates y-delta,
\ so:
\
\   (WW QQ) = (A QQ) = y-delta

BPL pang1              \ If (A QQ) is positive, jump to pang1 to skip the
\ following

LDA #0                 \ Set (A QQ) = 0 - (WW QQ)
SEC                    \
SBC QQ                 \ starting with the low bytes
STA QQ

LDA #0                 \ And then the high bytes
SBC WW

\ So (A QQ) is now positive, in other words:
\
\   (A QQ) = |y-delta|

.pang1

LSR A                  \ Set (A QQ) = (A QQ) >> 3
ROR QQ                 \            = |y-delta| / 8
LSR A
ROR QQ
LSR A
ROR QQ

STA TT                 \ Set (TT QQ) = (A QQ)
\             = |y-delta| / 8

\ We now compare the two 16-bit values in (A QQ) and
\ (L K)

CMP L                  \ If A < L, then (A QQ) < (L K), so jump to pang3
BCC pang3

BNE pang2              \ If A <> L, i.e. A > L, then (A QQ) > (L K), so jump
\ to pang2 to return from the subroutine with the C flag
\ set

\ The high bytes are equal, so now we compare the low
\ bytes

LDA QQ                 \ If QQ < K, then (A QQ) < (L K), so jump to pang3
CMP K
BCC pang3

.pang2

\ If we get here then (A QQ) >= (L K), so:
\
\   |y-delta| / 8 >= (L K)

SEC                    \ Set the C flag

RTS                    \ Return from the subroutine

.pang3

LDY #0                 \ Set Y = 0, which we use to count the number of shifts
\ in the following calculation

LDA L                  \ Set (A K) = (L K)

.pang4

\ This part is called from below, if we want to scale
\ the division

ASL QQ                 \ Set (TT QQ) = (TT QQ) << 1
ROL TT

INY                    \ Increment Y

.pang5

\ If we get here, then:
\
\   * (TT QQ) = |y-delta| / 8
\
\   * WW is the high byte of y-delta
\
\   * (A K) = |x-delta|
\
\   * |x-delta| > |y-delta| / 8
\
\   * Y = 0
\
\ We now do the following division so we can use
\ trigonometry to calculate the pitch angle:
\
\   (|y-delta| / 8) / |x-delta|
\
\ To get started, we shift both 16-bit values to the
\ left as far as possible, which we can do without
\ affecting the result as we are going to divide the two
\ values, so any mutual shifts will cancel each other
\ out in the division
\
\ We count the number of shifts we do in Y
\
\ Once that's done, we can drop the low bytes and just
\ divide the high bytes, which retains as much accuracy
\ as possible while avoiding the need for full 16-bit
\ division
\
\ So we keep shifting left until we get a 1 in bit 7 of
\ (A K), as that's the larger of the two values

ASL K                  \ Set (A K) = (A K) << 1
ROL A

BCC pang4              \ If we just shifted a 0 out of the high byte of (A K),
\ then we can keep shifting, so loop back to rotn6 to
\ keep shifting both values

ROR A                  \ We just shifted a 1 out of bit 7 of A, so reverse the
\ shift so A contains the correct high byte (we don't
\ care about the low byte any more)

\ So by this point, (A K) and (TT QQ) have both been
\ scaled by the same number of shifts

STA V                  \ Set V = A, the high byte of the scaled |x-delta|,
\ which we know is at least 128 (as bit 7 is set)

STY scaleDown          \ Set scaleDown to the number of shifts in Y

TAY                    \ Set scaleUp = 256 / (1 + (A - 128) / 128)
LDA divideX-128,Y      \             = 256 / (1 + (|x-delta| - 128) / 128)
STA scaleUp            \
\ We know that A contains the scaled-up |x-delta|, which
\ ranges from 128 (when x-delta is small) to 256 (when
\ x-delta is large), so scaleUp contains the reciprocal
\ of this, i.e. 1/|x-delta|, scaled into the range 256
\ to 128

LDA QQ                 \ Set T = QQ, the low byte of the scaled |y-delta|, to
STA T                  \ use for rounding the result in Divide8x8

LDA TT                 \ Set A = TT, the high byte of the scaled |y-delta|

JSR Divide8x8          \ Set T = 256 * A / V
\       = 256 * (|y-delta| / 8) / |x-delta|
\
\ using the lower byte of the |y-delta| numerator for
\ rounding

LDA T                  \ If T >= 128, jump to pang8 to return from the
CMP #128               \ subroutine with the C flag set
BCS pang8

BIT WW                 \ If y-delta is positive, jump to pang6 to skip the
BPL pang6              \ following and add 60 to T

LDA #60                \ Set A = 60 - T
SEC
SBC T

.pang6

CLC                    \ Set A = T + 60