#157383 - Polaris - Fri May 23, 2008 6:11 am
So I'm trying to change an objects position along the X and Y axis respectively using the sine and cosine functions. In any other place I wouldn't mind using sin() and cos() to do this, but since those are slow as molasses in the DS I have turned to libnds LUTs.
Code: |
int positionX;
int positionY;
int _stepX = SIN[_angle & SPRITE_ANGLE_MASK] >> 4;
int _stepY = COS[_angle & SPRITE_ANGLE_MASK] >> 4;
_positionX += _stepX;
_positionY += _stepY; |
That's roughly what i'm doing right now, and ofcourse it doesn't work. What I can't wrap my head around is why. I'm obviously missing a very important detail here.
I'm guessing it has something to do with fixed point values because this method works if I use sin() and cos() and the [_angle & SPRITE_ANGLE_MASK] >> 4 bit is pretty much littered all over the place as "the thing to do" with the LUTs.
#157385 - simonjhall - Fri May 23, 2008 7:43 am
Just in case you missed it, you know that 'angle' goes from 0-511, right? So 0 = 0 degrees, 511 = 359 degrees?
_________________
Big thanks to everyone who donated for Quake2
#157387 - kusma - Fri May 23, 2008 8:02 am
Just for clarity: 511 = ~359 degrees (or 359.2969...), not 359 exactly
#157389 - nanou - Fri May 23, 2008 10:19 am
OT: Each "nintendo degree" is almost exactly 2/3 of regular degrees (512x2 = 1024 ~~ 360x3 = 1080.) Like "nintendo seconds" which also tend to be shorter (remember SMB? That timer doesn't show real seconds.)
_________________
- nanou
#157396 - Polaris - Fri May 23, 2008 3:17 pm
I understand that what goes inside the [] should be a number in the 0-511. But isn't exactly that what the AND'ing with the bit mask doing? Taking what ever _angle has at the moment and triming it so that it is left as something between 0-511.
By the way SPRITE_ANGLE_MASK is 0x01FF.
What fixed point format do the trig luts use? I'm pretty sure that will be of great help.
#157398 - eKid - Fri May 23, 2008 3:50 pm
Polaris wrote: |
What fixed point format do the trig luts use? I'm pretty sure that will be of great help. |
They are in 4.12 format.
#157404 - Polaris - Fri May 23, 2008 6:17 pm
OK I think i'm getting this.
When I do >> 4 on the value returned by the LUTs I am actually turning it into 8.8, right? So if I wanted to add that number to my position, it in turn should also be 8.8, to keep everything in the correct place, right?
The part that's giving me trouble is what comes after that, which is a bit of topic :( I got my crud pixel plotting function that takes 2 ints as parameters(x and y screen positions) and inside it manages them as regular ints(with no fixed point).
I tried triming all the stuff to the left of the fixed point by doing >> 8, hoping it would turn the number into a regular int that my function could use properly, but that didn't worked quite right.
Is changing the pixel plotting function so it can handle some kind of fixed point the only thing I can do now?
#157408 - strager - Fri May 23, 2008 6:49 pm
The following are effectively equivalent:
Code: |
double s = (double)SIN[(int)(SPRITE_ANGLE_MASK * angle / M_PI) & SPRITE_ANGLE_MASK] / (1 << 12);
double s = sin(angle); |
In your original post, _stepX is effectively in the range from -256 (-1 * (1 << 12) >> 4) to 256, which may be why you don't see it properly.
#157410 - Cearn - Fri May 23, 2008 9:33 pm
Polaris wrote: |
OK I think i'm getting this.
When I do >> 4 on the value returned by the LUTs I am actually turning it into 8.8, right? So if I wanted to add that number to my position, it in turn should also be 8.8, to keep everything in the correct place, right?
The part that's giving me trouble is what comes after that, which is a bit of topic :( I got my crud pixel plotting function that takes 2 ints as parameters(x and y screen positions) and inside it manages them as regular ints(with no fixed point).
I tried trimming all the stuff to the left of the fixed point by doing >> 8, hoping it would turn the number into a regular int that my function could use properly, but that didn't worked quite right.
|
Doing >>12 should cut the numbers down to regular ints, yes. However, this won't get you very far by itself because that'll just give either -1, 0 or +1. Usually -1 or 0. These numbers won't be of much use when plotting.
It might help if you explained what you are trying to do with these numbers; what behaviour you were expecting and what you actually got.
#157419 - Polaris - Sat May 24, 2008 1:31 am
Cearn you are correct. It didn't took me much time to realise I was only getting 0 and -1 out of those results when my object was moving only up, to the left and occasionally in a perfect diagonal upwards, not to mention staying in place some of the time.
Maybe you can recall the other thread I made, in which I was asking for some help to plot individual pixels in Mode 0. I'm looking into this so I can make a particle system that doesn't use sprites, the first particle effect I was looking to implement was a radial explosion, with each particle having different speed and a random angle when created.
So to achieve that effect I don't think there is any way around it but to use sine and cosine, so I can make the particle move along a given angle. I already figured out that I don't really need to calculate the sine and cosine on a loop, it would probably work if I calculated the cordinates of a point around the center of the "explosion" when the particle is created, and then with those numbers construct the vector the particle would use to move.
Using that technique I might be able to get away with using sin() and cos() and a bunch of floats, and with some simple casting to int everthing should work fine. Infact it works fine, already tried it. But I wouldn't be learning anything if I did that, would I?
Basically what I'm looking for, is the way to achieve the same effect that the math.h functions and floats get me, but using the LUTs. Since the LUTs return fixed point, I was hoping for a way in which I could convert them into regular ints, with a meaningful value for this purpose of course, so I wouldn't need to change the plotting functions I already have.
I hope this clarified something, because it turned out to be quite long.
#157430 - tepples - Sat May 24, 2008 2:40 am
In order to move at other than specific angles, you'll need to be able to represent positions and velocities that are fractions of a pixel. Programs on the PS1, GBA, and DS typically use fixed-point arithmetic for this. So try storing all your vectors, including positions and velocities, in some fixed-point format.
Point of terminology: Technically, a position is a point, not a vector, but computer geometry commonly represents a point as as a vector from some origin.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#157434 - silent_code - Sat May 24, 2008 11:22 am
i believe, that what distinguishes a point from a vector in computer graphics is the operations you can do on them and that they are always relative to a certain origin, that when moved, will also move the whole vector. think of sub meshes etc. the difference between a cg and a math vector is, that in cg, the origin is implicit. ;^)
_________________
July 5th 08: "Volumetric Shadow Demo" 1.6.0 (final) source released
June 5th 08: "Zombie NDS" WIP released!
It's all on my page, just click WWW below.
#157435 - Cearn - Sat May 24, 2008 1:10 pm
Polaris: yes, that helps.
You're probably removing the fixed-point bits too soon. The calculations should all be done in fixed point arithmetic; only when you finally call the plotter should you shift down to .0 fixed point (i.e., regular ints). For the velocity, you need a sine/cosine value and a speed for that particle to scale the velocity. In floating point, that'd be something like:
Code: |
// --- Init ---
x= 100;
y= 100;
v0= drand_minmax(0.125, 2); // Between 1/8 and 2 px/frame
angle= drand_minmax(0, 2*pi);
vx = v0*cos(angle);
vy = v0*sin(angle);
// --- Per frame ---
x = vx;
y += vy;
plot(x, y, color);
|
In fixed point, you still have something like this, but you have to remember where the fixed-point information goes and how it changes with the calculations.
Code: |
// Some useful stuff
#define ANGLE_COUNT 512
static inline f32 sinf32(int angle) { return SIN[angle&(ANGLE_COUNT-1)]; }
static inline f32 cosf32(int angle) { return COS[angle&(ANGLE_COUNT-1)]; }
int rand_minmax(int min, int max)
{
return (rand()*(max-min)>>15)+min;
}
// --- Init ---
x= 100<<8;
y= 100<<8;
v0= rand_minmax(0x100/8, 0x100*2); // Between 1/8 and 2 px/frame (Q8)
angle= rand_minmax(0, ANGLE_COUNT);
vx = v0*cosf32(angle)>>12; // Q8*Q12>>12 -> Q8
vy = v0*sin32(angle)>>12;
// --- Per frame ---
x += vx; // Q8+Q8 -> Q8
y += vy; // Q8+Q8 -> Q8
plot(x>>8, y>>8, color)
|
'Points' are real, physical locations in a space. 'Base-vectors' are connections between an arbitrary set of points, forming a coordinate system. 'Coordinates' of a point are the number of steps along the base-vectors needed to reach a given point. 'Vectors' describe distances between points, expressed as differences between the coordinates. You can express base-vectors (u, v, w) of one system (S') in those of another (S). Given the coordinates of point P in S' (x'=(x', y',z')), you can find the coordinates in S (x=(x,y,z)) by scaling the base-vectors of S by the coordinates and adding the results: x=x'?u + y'?v + z'?w. This corresponds to the matrix-vector multiplication x=Mx', where M is [ u v w ]. Hence, matrix multiplications can be used to describe coordinate transformations.
:)
#157460 - Polaris - Sun May 25, 2008 1:02 am
This looks really neat. Just a couple of questions, what exactly is happening here? Code: |
(rand()*(max-min)>>15)+min; |
And why do I need this range?
#157480 - Cearn - Sun May 25, 2008 10:14 am
Polaris wrote: |
This looks really neat. Just a couple of questions, what exactly is happening here? Code: | (rand()*(max-min)>>15)+min; |
|
rand() returns a number between 0 and 0x8000 (well technically between 0 and RAND_MAX). Interpreting this as a Q15 (15bit fixed point) number, this represents a range between 0 and 1. Multiply that by N and you get a range between 0 and N in 15-bit fixed point. Shifting away the fixed-point bits gives a proper [0, N) range.
At least, this is how it worked at some time. Apparently RAND_MAX has changed to 0x7FFFFFFF at some point, so this wouldn't quite work an extra shift for the rand(). The reason I'm doing this at all is because the 'standard' way of getting a ranged number (rand()%N) requires a very slow modulo. If you require lots of randoms, this can be significant.
Do a search for random on the forum. The matter of getting a random number has come up quite often.
Polaris wrote: |
And why do I need this range? |
This was just an example range. You don't need to use it, but it is seemed to give decent explosion rates in my test.
#157482 - kusma - Sun May 25, 2008 12:53 pm
Cearn wrote: |
The reason I'm doing this at all is because the 'standard' way of getting a ranged number (rand()%N) requires a very slow modulo. If you require lots of randoms, this can be significant. |
If you compile the code as ARM (and not THUMB), a long multiply can be used for the modulo (gcc optimizes modulos by constant values like that). If you need many random values, you might want to change the approach anyway. My method of choice is to fill a table of random values with a proper RNG, and use a cheap (and poor) pseudo-rand to pick a random index into that table.