.gaze13 \ We jump here with A set to the number of the tile edge \ that we can test against the gaze vector (where the \ edge goes from corner A to corner A + 1) TAX \ Copy A into X so we can use it as an index into the \ corner height variables we set up above \ By this point the value of X (and, currently, of A) \ is in the range 0 to 3, and it represents the first \ corner of the edge within the tile we are analysing \ \ Here are the tile's corner heights that we set in the \ S, T, U and V variables above: \ \ ^ [T] [U] \ | \ | [S] [V] \ z-axis \ into \ screen x-axis from left to right ---> \ \ The variables S, T, U and V are consecutive in memory, \ so LDA S,X can be used to select corner altitudes for \ values of X, like this: \ \ [T] [U] [1] [2] \ = \ [S] [V] [0] [3] \ \ Similarly, LDA T,X will select the altitude for tile \ corner X + 1 \ \ Note that back in part 2, we set W to the same value \ as S, so this approach will still work if X = 3, as \ LDA T,X will fetch the value of W, which is the tile \ altitude of corner 0, just like S \ \ So X (and A) represent the starting corner, and we now \ calculate the coordinates and gradient of the tile \ edge from corner X to corner X + 1, so we can work out \ whether the gaze passes above or below the edge \ \ We start by calculating the distance along the tile \ edge that corresponds to the current position of the \ gaze vector \ \ To see how this works, consider the axes of the tile \ corners: \ \ ^ [1] [2] \ | \ | [0] [3] \ z-axis \ into \ screen x-axis from left to right ---> \ \ The low bytes of the current gaze vector in xCoordLo \ and zCoordLo give us the fractional part of the \ coordinate of the current position along the gaze, and \ the fractional part gives us the position of the \ coordinate within the tile (as the tile corners are on \ the integer coordinates) \ \ Zooming in on the tile, we have the following, where \ [x] is the current position along the gaze vector when \ looking at the tile from above: \ \ [1] [2] \ xCoordLo \ <----------> [x] \ ^ \ | \ | zCoordLo \ | \ [0] v [3] \ \ We now set edgeGazeDistance to the corner-relative \ coordinate of the gaze vector along the edge that we \ are considering (so if we are examining the edge from \ corner X to corner X + 1, we're looking for the \ distance between corner X and the [x] of the gaze \ vector) \ \ For example, if we are considering the left edge from \ 0 to 1, the distance of the [x] relative to corner 0 \ and along the edge is zCoordLo, while on the top edge \ from 1 to 2, the distance of the [x] relative to \ corner 1 and along the edge is xCoordLo \ \ The other two edges are similar but the distances are \ in the opposite direction to the axes \ \ So if we are considering the bottom edge from 3 to 0, \ the distance is ~xCoordLo, because: \ \ xCoordLo + ~xCoordLo = 1 \ \ So adding xCoordLo and ~xCoordLo together gives us the \ distance between the tile corners (which is 1), so it \ follows that ~xCoordLo is the distance from corner 3 \ to the gaze point, as xCoordLo is the distance from \ corner 0 to the gaze point \ \ We now set edgeGazeDistance to the correct value LSR A \ Set Y as follows: LDY xCoordLo \ BCS gaze14 \ * xCoordLo if A = 1 or 3 (i.e. bit 0 of A is 1) LDY zCoordLo \ \ * zCoordLo if A = 0 or 2 (i.e. bit 0 of A is 1) .gaze14 LSR A \ Set A as follows: TYA \ BCC gaze15 \ * A = Y if A = 0 or 1 (i.e. bit 1 of A is 0) EOR #%11111111 \ \ * A = ~Y if A = 2 or 3 (i.e. bit 1 of A is 1) .gaze15 STA edgeGazeDistance \ Set edgeGazeDistance to the result in A, so we set it \ as follows: \ \ * edgeGazeDistance = zCoordLo if A = 0 \ \ * edgeGazeDistance = xCoordLo if A = 1 \ \ * edgeGazeDistance = ~zCoordLo if A = 2 \ \ * edgeGazeDistance = ~xCoordLo if A = 3 \ \ We use edgeGazeDistance below to calculate where the \ gaze vector crosses the edge \ We now calculate the gradient of the tile edge LDA S,X \ Set G to the altitude of corner X, which we use in the STA G \ calculation at the end of the routine LDA T,X \ Set A to the altitude of corner X + 1 minus the SEC \ altitude of corner X SBC S,X \ \ So this is the gradient of the edge between corner \ X and corner X + 1 PHP \ Store the flags of the result on the stack, so we can \ retrieve the sign below BPL gaze16 \ If the result is negative then negate it to make it EOR #%11111111 \ positive, so A now contains the absolute value of the CLC \ edge gradient ADC #1 .gaze16 STA U \ Set U to the absolute gradient of the edge LDA edgeGazeDistance \ Set A to the fractional distance along the edge that \ corresponds to the current position along the gaze \ vector JSR Multiply8x8 \ Set (A T) = A * U \ = fractional distance * |gradient| PLP \ Restore the sign of the gradient which we stored on \ the stack above, so the N flag reflects the sign of \ the gradient JSR Absolute16Bit \ Set the sign of (A T) to match the gradient, so A is \ now the correct sign for the calculation and we have \ the following: \ \ (A T) = fractional distance * gradient \ \ The fractional distance is a fractional value that \ represents how far along the edge we would need to go \ in order to be at the corresponding coordinate as the \ current position along the gaze vector \ \ The gradient is the change in altitude as we move from \ one end of the edge to the other \ \ Multiplying these two therefore gives us the height of \ the point along the edge that corresponds to the \ current position along the gaze vector \ \ This height is relative to the tile corner at the \ start of the edge (i.e. corner X), so to get the \ altitude of the point in the 3D world, we need to add \ the result in (A T) to the altitude of corner X \ \ We set G above to the altitude of corner X, which came \ from the call to GetTileAltitude, so G contains the \ high byte of the altitude and is therefore an integer \ value, so we add a low byte of 0 in the addition CLC \ Set (U T) = (G 0) + (A T) ADC G \ STA U \ So (U T) contains the altitude of the point on the \ edge that corresponds to the current position of the \ gaze vector LDA yCoordLo \ Set (A *) = yCoord(Hi Lo) - (U T) SEC \ SBC T \ So A contains the high byte of the difference in LDA yCoordHi \ altitude between the current position along the gaze SBC U \ vector and the point on the edge that corresponds to \ the vector BPL gaze17 \ If the current position along the gaze vector is \ higher than the corresponding point on the edge, then \ the viewer's gaze is not being obstructed by the edge, \ so jump to gaze1 via gaze17 to move along the gaze \ vector and restart the checks JMP gaze4 \ Otherwise the current position along the gaze vector \ is below the corresponding point on the edge, and the \ viewer's gaze is being obstructed by the edge, so jump \ to gaze4 to return from the subroutine with the C flag \ set to indicate that the viewer is not looking at a \ tile .gaze17 JMP gaze1 \ Jump to gaze1 to move along the gaze vector and \ restart the checks (this jump point is for use by \ branching instructions)Name: FollowGazeVector (Part 5 of 5) [Show more] Type: Subroutine Category: Maths (Geometry) Summary: For non-flat tiles with two horizontal edges, work out whether the tile edge obstructs the gaze vectorContext: See this subroutine in context in the source code References: No direct references to this subroutine in this source file
[X]
Subroutine Absolute16Bit (category: Maths (Arithmetic))
Calculate the absolute value (modulus) of a 16-bit number
[X]
Subroutine Multiply8x8 (category: Maths (Arithmetic))
Calculate (A T) = T * U
[X]
Variable edgeGazeDistance in workspace Zero page
The fractional distance along a tile edge that matches the current position along the gaze vector
[X]
Label gaze1 in subroutine FollowGazeVector (Part 1 of 5)
[X]
Label gaze14 is local to this routine
[X]
Label gaze15 is local to this routine
[X]
Label gaze16 is local to this routine
[X]
Label gaze17 is local to this routine
[X]
Label gaze4 in subroutine FollowGazeVector (Part 1 of 5)