neilobremski
Experienced Member
I've been working with 8-bit fixed point for pre-calculated SINE and COSINE values. One compromise of this is that the maximum positive value is only 127 which means I can only represent 127/128 (0.9921875) and not a whole ONE.
Recently I encountered an odd bug where viewing my tile map from a 270° facing caused the Z coordinates of all objects to be reversed. My test map and player position looked like so:
And thus I expected to see the following:
But instead it was blank. I decided the view (position) rotation algorithm must be bogus and reworked it from scratch. I tested it with the following JScript on my Windows computer:
NOTE that Y is negated for use as Z since positive Z is "in front" and negative is "in back" of the camera. Redoing the algorithm only caused me more alarm because it looked suspiciously like what I had had before:
So why then wasn't it working? I stepped through the code and found the answer in the 8-bit fixed point. See if you can spot it ...
The SINE value of 270 degrees is -1 which in 8-bit fixed point is 0x80 or -128. This cannot be negated because there is no +128! And since the two's complement does nothing the value remains the same. Remember I am truncating positive values to +127 or 0x7F and doing that logically would incur a lot of extra instructions ... or I could simply negate the 16-bit value resulting from IMUL ...
That works but there are two NEG's surrounding an ADD which is redundant. I finally reduced this code to the following which works beautifully ...
Voila! Whenever dealing with fixed point, size does matter!
Recently I encountered an odd bug where viewing my tile map from a 270° facing caused the Z coordinates of all objects to be reversed. My test map and player position looked like so:
Code:
P
P <
And thus I expected to see the following:
But instead it was blank. I decided the view (position) rotation algorithm must be bogus and reworked it from scratch. I tested it with the following JScript on my Windows computer:
Code:
function cos(degrees)
{
return Math.round(Math.cos(Math.PI * degrees / 180.0) * 100) / 100;
}
function sin(degrees)
{
return Math.round(Math.sin(Math.PI * degrees / 180.0) * 100) / 100;
}
function turn(x,y, degrees)
{
var xr = (x * cos(degrees)) - (y * sin(degrees));
var yr = (y * cos(degrees)) + (x * sin(degrees));
WScript.Echo("TURN: " + xr + " , " + yr);
}
function view(x,y, degrees)
{
var xr = (x * cos(degrees)) - (y * -sin(degrees));
var yr = -((y * cos(degrees)) + (x * -sin(degrees)));
WScript.Echo("VIEW: " + xr + " , " + yr);
}
var x = parseFloat(WScript.Arguments.Item(0).toString());
var y = parseFloat(WScript.Arguments.Item(1).toString());
var degrees = parseFloat(WScript.Arguments.Item(2).toString());
turn(x,y,degrees);
view(x,y,degrees);
NOTE that Y is negated for use as Z since positive Z is "in front" and negative is "in back" of the camera. Redoing the algorithm only caused me more alarm because it looked suspiciously like what I had had before:
Code:
; Zt = ((Yr * cos(dir)) + (Xr * -sin(dir))) * -128
; Xt = ((Xr * cos(dir)) - (Yr * -sin(dir))) * 128
So why then wasn't it working? I stepped through the code and found the answer in the 8-bit fixed point. See if you can spot it ...
Code:
MOV AX, BP ; 5EA
MOV AL, AH ; 5EC AL = cos(dir)
IMUL BL ; 5EE Yr * cos(dir)
MOV DX, AX ; 5F0
MOV AX, BP ; 5F2
NEG AL ; 5F4 AL = -sin(dir)
IMUL BH ; 5F6 Xr * -sin(dir)
ADD AX, DX ; 5F8
NEG AX ; 5FA * -128
STOSW ; 5FC write [ Zt ]
The SINE value of 270 degrees is -1 which in 8-bit fixed point is 0x80 or -128. This cannot be negated because there is no +128! And since the two's complement does nothing the value remains the same. Remember I am truncating positive values to +127 or 0x7F and doing that logically would incur a lot of extra instructions ... or I could simply negate the 16-bit value resulting from IMUL ...
Code:
MOV AX, BP ; 5EA
MOV AL, AH ; 5EC
IMUL BL ; 5EE Yr * cos(dir)
MOV DX, AX ; 5F0
MOV AX, BP ; 5F2
IMUL BH ; 5F4 Xr * -sin(dir)
NEG AX ; 5F6
ADD AX, DX ; 5F8
NEG AX ; 5FA * -128
STOSW ; 5FC write [ Zt ]
That works but there are two NEG's surrounding an ADD which is redundant. I finally reduced this code to the following which works beautifully ...
Code:
MOV AX, BP ; 5EA
MOV AL, AH ; 5EC
IMUL BL ; 5EE Yr * cos(dir)
MOV DX, AX ; 5F0
MOV AX, BP ; 5F2
IMUL BH ; 5F4 Xr * -sin(dir)
NEG DX ; 5F6
ADD AX, DX ; 5F8
STOSW ; 5FA write [ Zt ]
Voila! Whenever dealing with fixed point, size does matter!