Skip to navigation

Drawing objects: GetObjPointAngles

Name: GetObjPointAngles [Show more] Type: Subroutine Category: Drawing objects Summary: Calculate the view-relative pitch and yaw angles of all the points in an object
Context: See this subroutine in context in the source code References: This subroutine is called as follows: * DrawObject calls GetObjPointAngles

This routine loops through each point in an object definition and calculates the following: 1. A: Calculate the yaw angle of the point, rotated by the rotation of the object itself (i.e. its gaze), so this is the yaw angle of the object point within the object, but with the correct rotation for the direction the object is facing. 2. xDelta(Hi Lo), zDelta(Hi Lo): Convert this yaw angle and the polar distance into x- and z-coordinates for the point in the y = 0 plane (i.e. on the ground) using cos(A) and sin(A) on a triangle with the line along the polar distance to the point as the hypotenuse. So these are the x- and z-coordinates of the object point within the object itself (i.e. relative to the object's origin and rotated to the correct gaze). 3. angle(Hi Lo): Calculate the angle of the right-angled triangle made up of the object point's object-relative x- and z-coordinates, to give us the angle of the vector from the object's origin to the to the point within the object. 4. drawViewYaw(Hi Lo): Rotate the yaw angle of the point within the object definition by the object's view-relative yaw angle to get the view-relative yaw angle of the point. This is the yaw angle of the vector from the viewer to the object point. 5. drawViewPitch(Hi Lo): Construct the vertical triangle that has the vector from the viewer to the point as the hypotenuse, the point altitude as the opposite side and the projection onto the ground of the vector as the adjacent side, and use this to calculate the pitch angle of the vector from the viewer to the object point.
.GetObjPointAngles LDX objTypeToAnalyse \ Set X to the number of the object we are drawing LDA #64 \ Set drawingTableIndex to 64 to use as the index into STA drawingTableIndex \ the drawing tables for the object we are drawing, so \ we store the results in the following drawing tables: \ \ drawViewYaw(Hi Lo) \ \ drawViewPitch(Hi Lo) \ \ Note that these drawing tables live just after the \ tile drawing tables in memory, and the index is set \ accordingly \ \ Specifically, the tile view angles is stored with a \ drawing index of 0 or 32, thus taking up the first 64 \ bytes of each table, while the object point angles \ are stored from drawing index 64 \ \ Each drawing table is 96 bytes in total, so they fit \ together nicely LDA objPointRange+1,X \ Set objectLastPoint to entry X + 1 from the table at STA objectLastPoint \ objPointRange, so this now contains the number of the \ first point for the next object, object #X + 1, which \ is one greater than the last point number for object \ #X, as the lists of object points are sequential (so \ object #0's points are first in the object data \ tables, then object #1's points, and so on) LDY objPointRange,X \ Set Y and pointNumber to entry X from the table at STY pointNumber \ objPointRange, so this now contains the number of the \ first point for object #X \ We now work through the points in the object, using Y \ as the point number and working through the points \ from first to last \ \ Object points are stored as polar coordinates plus a \ height, to define where they appear within the object \ \ The polar coordinate consists of a polar yaw angle and \ a polar distance (where these are relative to the gaze \ and origin of the object in the object's definition), \ which together define the position of the point on the \ ground plane (y = 0), as if we were looking at the \ object from above \ \ The height defines the y-coordinate of the point \ relative to the object's origin, so that's the height \ of the point within the object \ \ You can think of the polar coordinate as defining the \ positions of the points on a piece of paper, and the \ height then extruding those points up and down, away \ from the paper, to create a 3D model .obpt1 LDA objectGazeYawLo \ Set the following STA T \ LDA objectGazeYawHi \ (A T) = objectGazeYaw(Hi Lo) + yaw angle for point Y CLC \ ADC objPointYaw,Y \ So this is the yaw angle of point Y in the object, \ rotated by the rotation of the object itself (i.e. its \ gaze), so this is the yaw angle of the object point \ within the object, but with the correct rotation for \ the direction the object is facing JSR GetSineAndCosine \ Calculate the following: \ \ sinA = |sin(A T)| \ \ cosA = |cos(A T)| \ \ where (A T) is the yaw angle of the object point \ \ This also returns the sign of the result in H, as \ follows: \ \ * Bit 7 of H is the sign of sin(A) \ \ * Bit 6 of H is the sign of cos(A) \ \ where 0 = positive and 1 = negative LDY pointNumber \ Set U to the polar distance of the object point LDA objPointDistance,Y STA U LDA cosA \ Set (A T) = A * U JSR Multiply8x8 \ = distance * cos(A T) STA T \ Set (A T) = distance * cos(A T) / 256 LDA #0 \ \ This discards the fractional part of the result BIT H \ If bit 6 of H is clear then cos(A T) is positive and BVC obpt2 \ the result already has the correct sign, so jump to \ obpt2 to skip the following JSR Negate16Bit \ Otherwise cos(A T) needs to be negative, so set: \ \ (A T) = -(A T) \ \ So the result now has the correct sign .obpt2 STA U \ Set (U T) = (A T) \ = distance * cos(A T) \ \ So this is the length of the adjacent side in the \ triangle with the polar distance as the hypotenuse LDA objectAdjacentLo \ Set zDelta(Hi Lo) = objectAdjacent(Hi Lo) + (U T) CLC \ ADC T \ So this adds the adjacent side of the vector from the STA zDeltaLo \ viewer to the object, to the adjacent side of the LDA objectAdjacentHi \ polar coordinate of the object point ADC U \ STA zDeltaHi \ This gives the distance along the axis of the object \ point, i.e. the point's coordinate BPL obpt3 \ If the high byte of the result is positive, jump to \ obpt3 as the result in A is already correct for the \ absolute value (i.e. A = zDeltaHi = |zDeltaHi|) LDA #0 \ Negate zDelta(Hi Lo) so it contains the absolute value SEC \ of the coordinate SBC zDeltaLo STA zDeltaLo LDA #0 SBC zDeltaHi .obpt3 \ As the angle of the triangle is at the viewer end of \ the hypotenuse, the length of the adjacent side is \ effectively the length of the side along the z-axis, \ so we can use the distance as a z-coordinate STA zDeltaAbsoluteHi \ Set zDeltaAbsoluteHi = |zDeltaHi| \ \ So we now have the absolute z-axis length in: \ \ (zDeltaAbsoluteHi zDeltaLo) \ \ and the original high byte of the signed z-axis length \ is still in zDeltaHi \ We now do a similar calculation for the x-axis, but \ using sin(A) instead of cos(A) to get the length of \ the opposite side of the triangle instead of the \ adjacent LDA objPointDistance,Y \ Set U to the polar distance of the object point STA U LDA sinA \ Set (A T) = A * U JSR Multiply8x8 \ = distance * sin(A) STA xDeltaLo \ Set (A xDeltaLo) = distance * sin(A) / 256 LDA #0 STA xDeltaAbsoluteHi \ Set xDeltaAbsoluteHi = 0 so it is correctly set to the \ high byte of (A xDeltaLo), as A is 0 LDA H \ Set xDeltaHi to the sign of sin(A T) from above, so STA xDeltaHi \ the calculation in GetHypotenuseAngle uses the correct \ sign for opposite side (only the sign bit from \ xDeltaHi is used in GetHypotenuseAngle, so this will \ work as bits 0 to 6 of xDeltaHi will be ignored) \ So we now have the x- and z-coordinates of the object \ point within the object itself (i.e. relative to the \ object's origin and rotated to the correct gaze), so \ we can calculate the angle in that triangle, to give \ us the yaw angle of the point relative to the object's \ origin JSR GetHypotenuseAngle \ Calculate the angle of the hypotenuse in the triangle \ with the following non-hypotenuse sides: \ \ * xDelta(Hi Lo) \ \ * zDelta(Hi Lo) \ \ and return the angle in angle(Hi Lo), the tangent in \ angleTangent, the length of the longer side in \ a(Hi Lo) and the length of the shorter side in \ b(Hi Lo) \ \ So angle(Hi Lo) is the angle of the right-angled \ triangle made up of the object point's object-relative \ x- and z-coordinates, i.e. the yaw angle of the point \ within the object, or the angle of the vector from the \ object's origin to the to the point within the object \ We now calculate the yaw and pitch angles of the point \ in terms of the 3D world, storing the results in the \ following tables: \ \ drawViewYaw(Hi Lo) \ \ drawViewPitch(Hi Lo) \ \ We do this by constructing two right-angled triangles, \ one flat on the ground (to calculate the yaw angle) \ and the other standing vertically (to calculate the \ pitch angle) \ \ Let's start with the triangle on the ground \ \ The object has a view-relative yaw angle of: \ \ objectViewYaw(Hi Lo) \ \ which was set up by the call to the GetObjectAngles \ routine before we called this routine \ \ This contains the yaw angle of the object relative to \ the view \ \ We just calculated the object-relative yaw angle of \ the object point in: \ \ angle(Hi Lo) \ \ If we add these two yaw angles together to get: \ \ angle(Hi Lo) + objectViewYaw(Hi Lo) \ \ then this gives us the view-relative yaw angle of the \ object point, which is what we are trying to calculate \ in this routine LDY drawingTableIndex \ Set Y to the drawing table index for this object point \ so we store the results in the last 32 bytes of the \ tile view drawing tables (i.e. in the object point \ drawing tables) LDA angleLo \ Set drawViewYaw(Hi Lo) for this point to: CLC \ ADC objectViewYawLo \ angle(Hi Lo) + objectViewYaw(Hi Lo) STA drawViewYawLo,Y \ LDA angleHi \ So this is the angle in our first triangle, which is ADC objectViewYawHi \ the view-relative yaw angle of the object point, so STA drawViewYawHi,Y \ store this in the drawing table at drawViewYaw(Hi Lo) \ We now move on to calculating the view-relative pitch \ angle of the object point by looking at the second \ triangle \ \ In the first triangle, we calculated the length and \ angle of the hypotenuse along the ground, which \ represents the projection from above of the vector \ from the viewer to the object point \ \ We're now interested in the y-axis element, so let's \ construct a triangle that stands vertically, and whose \ hypotenuse is the 3D vector from the viewer to the \ object point \ \ The hypotenuse that we used to calculate the yaw angle \ is now along the ground, acting as the bottom side \ of the new triangle, i.e. the adjacent side where the \ triangle's angle is the pitch angle \ \ The opposite side of the triangle is the height of the \ point above the ground (if we're looking up) or below \ ground (if we're looking down), which we can calculate \ using the objPointHeight table, which contains the \ height of the point within the object, and adding the \ view-relative y-coordinate of the object \ \ Given these two sides, we can then calculate the pitch \ angle of our new triangle to give the view-relative \ pitch angle of the object point, which is what we are \ trying to calculate in this routine JSR GetHypotenuse \ Calculate the length of the hypotenuse in the triangle \ with side lengths of a(Hi Lo) and b(Hi Lo) and angle \ angleTangent, which are still set from the call to \ GetHypotenuseAngle above to the values for the vector \ from the object's origin to the to the point within \ the object \ \ So this is the adjacent side of our new triangle, \ along the ground \ \ The hypotenuse length is returned in hypotenuse(Hi Lo) LDY pointNumber \ Set A to the objPointHeight entry for the object point LDA objPointHeight,Y \ \ The height is stored in objPointHeight as follows: \ \ * Bit 7 contains the sign bit \ \ * Bits 0 to 6 contain half the magnitude of the \ height \ \ So the height stored as a scaled, sign-magnitude value \ that we now need to extract into a signed number ASL A \ Shift the sign bit into the C flag and double the \ value in bits 0 to 6 to get the magnitude of the \ height in A, so: \ \ A = |height| STA T \ Set (A T) = |height| LDA #0 BCC obpt4 \ If the sign bit is clear then the height is positive \ and (A T) already has the correct sign, so jump to \ obpt4 to skip the following \ Otherwise the sign bit is set and the height is \ negative, so we need to negate the absolute value to \ get the signed height JSR Negate16Bit \ Set (A T) = -(A T) \ \ So the result now has the correct sign .obpt4 STA U \ Set (U T) = (A T) \ \ So (U T) contains the height of the object point \ relative to the object's origin, i.e. within the \ object \ We already calculated the object's view-relative \ y-coordinate in objectOpposite(Hi Lo), so now we add \ the two together to get the height of the object \ point, relative to the view LDA T \ Set (A xDeltaLo) = (U T) + objectOpposite(Hi Lo) CLC \ ADC objectOppositeLo \ So (A xDeltaLo) now contains the vertical delta, ready STA xDeltaLo \ to pass to GetPitchAngleDelta LDA U ADC objectOppositeHi LDX viewingObject \ Set X to the object number of the viewer so the call \ to GetPitchAngleDelta calculates the pitch angle \ relative to the viewing object's gaze JSR GetPitchAngleDelta \ Set pitchDelta(Hi Lo) to the pitch angle of the vector \ along the hypotenuse of our new triangle, given the \ length of the vertical side in (A xDeltaLo) and the \ length of the adjacent side in hypotenuse(Hi Lo), \ relative to the viewing object's gaze \ \ So we now have the view-relative pitch angle of the \ object point, which is what we are trying to calculate \ in this routine LDY drawingTableIndex \ Set Y to the drawing table index for this object point \ so we store the results in the last 32 bytes of the \ tile view drawing tables (i.e. in the object point \ drawing tables) LDA pitchDeltaHi \ Store pitchDelta(Hi Lo) in the drawing table as STA drawViewPitchHi,Y \ drawViewPitch(Hi Lo) LDA pitchDeltaLo STA drawViewPitchLo,Y INC pointNumber \ Increment the point number to move on to the next \ point in the object INC drawingTableIndex \ Increment the index into the drawing table so we store \ the results for the next point in the next byte in the \ drawing tables LDY pointNumber \ If we have reached the first point of the next object CPY objectLastPoint \ then we have processed all the points in this object, BEQ obpt5 \ so jump to obpt5 to return from the subroutine JMP obpt1 \ Loop back to process the next point in the object .obpt5 RTS \ Return from the subroutine