Timing in demos.. how do I do it ?
category: general [glöplog]
Greetings !
I'm currently learning programming with C++ and DirectX9. Now I thought about how to do the timing.. and I didn't figure it out (I'm very new to DirectX or programming generally). If I would like to fade in a cube from the left, let it spin for 10 seconds and then let it then exit to the right side.. how would I do that ?
Sincerly OnTheRun
I'm currently learning programming with C++ and DirectX9. Now I thought about how to do the timing.. and I didn't figure it out (I'm very new to DirectX or programming generally). If I would like to fade in a cube from the left, let it spin for 10 seconds and then let it then exit to the right side.. how would I do that ?
Sincerly OnTheRun
the easiest to learn case (you may evolve from there on). I assume you can count milliseconds already:
This is BY NO MEANS the best way to do it, but it's the easiest method to start on and learn... after that, you might want to learn how to script it and fire events and stuff.
Code:
while(true) {
time = timer->getMs();
cube_rotation.x = time*0.05;
cube_rotation.y = time*0.04;
cube_rotation.z = time*0.03;
if(time<5000) {
// x will be 0 in 5 seconds, starting on -100
cube_position.x = -100 + (time*100/5000)
} else if(time > 15000) { // 10 seconds have passed where position hasn't changed
// When time is 20000 (5 seconds after it started moving), position.x will be +100
cube_position.x = ((time-15000)*100/5000)
}
draw_cube();
}
This is BY NO MEANS the best way to do it, but it's the easiest method to start on and learn... after that, you might want to learn how to script it and fire events and stuff.
A hint is the word 'interpolation', you may use different splinealgorithms to get your best results.. Some nice ones you should consider is hermite and bezier, what Jcl describes is linear interpolation.. Wich we all do somtimes when partycode, but really never should ;)
Then when you have learnt how to do this, you should sit down with paper/pencil and draw some of them, or on the screen.. Whatever :)
The point is that you want nice and smooth transitions, not linear crappy ones..
Then when you have learnt how to do this, you should sit down with paper/pencil and draw some of them, or on the screen.. Whatever :)
The point is that you want nice and smooth transitions, not linear crappy ones..
of course, for -nice- movements, you should use splines, which will allow for greater control over everything that happens, providing you have a nice spline editor or use not-so-many control points (try to make 200 control points on paper, for a 3D movement, and you'll be fucked ;D)... this also allows for easing (like, the cube would slow down when reaching the zero point, and seem to accelerate when leaving to the right side).
-Then again-, you need linear interpolation to browse through the spline, so you better start from there, and keep investigating on your own :-D
To put it simply, linear interpolation is a 'rule of three' forumla, with one incognita... image it as:
<current_value> - <current_time>
<end_value> - <end_time>
You have only one unknown value there (that's typically the current value), so you just cross-multiply the two values you know, and divide by the third.
Imagine you want to have a variable that goes from 0 to 1000 (that's the value), in a time of 15000 milliseconds. You know the current time, the end time, and the end value, need to find the current value for this time, so you go like:
x - current_time
1000 - 15000
The result is: x = (current_time * 1000) / 15000
- current_time * 1000 <-- cross multiplying the two crossed values you already know
- / 15000 <-- divide by the third
Sorry for my shitty english... i could explain this so easily in spanish and with a paper and pen (drawing arrows ;D), hope you got the point
-Then again-, you need linear interpolation to browse through the spline, so you better start from there, and keep investigating on your own :-D
To put it simply, linear interpolation is a 'rule of three' forumla, with one incognita... image it as:
<current_value> - <current_time>
<end_value> - <end_time>
You have only one unknown value there (that's typically the current value), so you just cross-multiply the two values you know, and divide by the third.
Imagine you want to have a variable that goes from 0 to 1000 (that's the value), in a time of 15000 milliseconds. You know the current time, the end time, and the end value, need to find the current value for this time, so you go like:
x - current_time
1000 - 15000
The result is: x = (current_time * 1000) / 15000
- current_time * 1000 <-- cross multiplying the two crossed values you already know
- / 15000 <-- divide by the third
Sorry for my shitty english... i could explain this so easily in spanish and with a paper and pen (drawing arrows ;D), hope you got the point
Also, for rotations, you should be using quaternions and then convert them to matrices for multiplying the vertices of your mesh for them... this allows for spherical interpolation (slerp'ing ;D) of rotations, which works pretty nicely... just one more thing to investigate if you feel like doing so :)
you can use GetTickCount() or timeGetTime() to get a millisec value... but the safest is always retrieving the value from the music player.
I wouldn´t use timeGetTime(). On my system it updates every 30 millisec, and that´s not enough if you have an unstable framerate. I use the high performance counter QueryPerformanceCounter() and QueryPerformanceFrequency(), and it worked fine so far.
Thank you very much for your answers !
With my current standard of knowledge, I only understand the first hint from Jcl, but I'm working on it ;)
Am I right with my adoption, that the timing-code belongs in the messagehandler-loop ?
Mine looks like this at the moment:
// Struktur, in der Informationen zur Nachricht gespeichert werden
MSG msg;
// Diese Schleife läuft bis WM_QUIT gesendet wird
// Wenn eine Nachricht vorliegt, wird sie behandelt
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
lpD3DDevice = Direct3D.GetDevice();
// Beginn der Szene
Direct3D.BeginScene();
Direct3D.DrawText("yeah yeah...",100,400,D3DCOLOR_XRGB(255,255,255));
// die Quelle für das Dreieck setzen
lpD3DDevice->SetStreamSource(0,VB_Triangle,0,sizeof(CustomVertex));
// Das Dreieck ist das erste Element im Puffer
// und es wird genau ein Objekt gezeichnet
lpD3DDevice->DrawPrimitive(D3DPT_LINELIST ,0,3);
// Die Szene ist beendet
Direct3D.EndScene();
}
Sorry for asking such "simple" questions.. but i'm a real hard newbie ;)
Greetings OnTheRun
With my current standard of knowledge, I only understand the first hint from Jcl, but I'm working on it ;)
Am I right with my adoption, that the timing-code belongs in the messagehandler-loop ?
Mine looks like this at the moment:
// Struktur, in der Informationen zur Nachricht gespeichert werden
MSG msg;
// Diese Schleife läuft bis WM_QUIT gesendet wird
// Wenn eine Nachricht vorliegt, wird sie behandelt
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
lpD3DDevice = Direct3D.GetDevice();
// Beginn der Szene
Direct3D.BeginScene();
Direct3D.DrawText("yeah yeah...",100,400,D3DCOLOR_XRGB(255,255,255));
// die Quelle für das Dreieck setzen
lpD3DDevice->SetStreamSource(0,VB_Triangle,0,sizeof(CustomVertex));
// Das Dreieck ist das erste Element im Puffer
// und es wird genau ein Objekt gezeichnet
lpD3DDevice->DrawPrimitive(D3DPT_LINELIST ,0,3);
// Die Szene ist beendet
Direct3D.EndScene();
}
Sorry for asking such "simple" questions.. but i'm a real hard newbie ;)
Greetings OnTheRun
obviously it has to be in your renderloop somewhere. i do (did) sg like:
----------------------------------------
while (!done) {
handle_messages();
unsigned int sync = GetTime();
clearscreen();
if (1000<sync && sync<=3000) thiseffect.render(sync-1000);
if (3000<sync && sync<=5000) thateffect.render(sync-3000);
flip();
}
----------------------------------------
(veeery pseudo :)
----------------------------------------
while (!done) {
handle_messages();
unsigned int sync = GetTime();
clearscreen();
if (1000<sync && sync<=3000) thiseffect.render(sync-1000);
if (3000<sync && sync<=5000) thateffect.render(sync-3000);
flip();
}
----------------------------------------
(veeery pseudo :)
now thats ugly code :)
Who hasn't coded something like this once :P (ugh, party coding sux)
all your timing code are belong to the render loop!
Quote:
// Struktur, in der Informationen zur Nachricht gespeichert werden
MSG msg;
// Diese Schleife läuft bis WM_QUIT gesendet wird
// Wenn eine Nachricht vorliegt, wird sie behandelt
while(msg.message != WM_QUIT)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
lpD3DDevice = Direct3D.GetDevice();
// Beginn der Szene
Direct3D.BeginScene();
Direct3D.DrawText("yeah yeah...",100,400,D3DCOLOR_XRGB(255,255,255));
// die Quelle für das Dreieck setzen
lpD3DDevice->SetStreamSource(0,VB_Triangle,0,sizeof(CustomVertex));
// Das Dreieck ist das erste Element im Puffer
// und es wird genau ein Objekt gezeichnet
lpD3DDevice->DrawPrimitive(D3DPT_LINELIST ,0,3);
// Die Szene ist beendet
Direct3D.EndScene();
}
erhh.. okay, but things will (hopefully) get a tad more structured and abstract than this; the whole idea people are trying to give you is that, as far as you want it to, you need to make the appearance of your visualizers parametrizable; these parameters have to be fed by either a script or perhaps a hardcoded piece of "script" (which can be either shit ugly or quite do-able, depending on both skill and time) which consists of a solid 1-way flow including timeslice codepaths or, which is better, an event system. in the end you'll have to feed interpolation formulas with the parameters such as time or perhaps an accumulative value taken from sound analysis (think about picking a "bass band" from a spectrum, filtering it a little.. etc). there are so many ways around that it's not really easy, at least not for me since i'm utter shit at explaining anything at all, to break this down into a few lines. i think jcl did a nice job at that, and if you'll work from his simple example of 'solving' the issue, you'll hopefully encounter your own needs regarding flexibility, efficiency and results.
in the end it all falls together when your programming skills improve. besides that, it's not considered a "good" practice to place a comment above each line of code, especially not when it makes anyone who can ride a bike go 'duh' ;)
oh well. back to work.
Has any of you any problems with QueryPerformanceFrequency ?
I do :
_LARGE_INTEGER freq;
_LARGE_INTEGER coun;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&coun);
return (double(coun.LowPart)/double(freq.LowPart));
for the DeltaT, and I get fine results on Athlon based machines, but on P4 with hyperthreading, the value is not 100% accurate (I would say about 99 %). Any ideas ?
I do :
_LARGE_INTEGER freq;
_LARGE_INTEGER coun;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&coun);
return (double(coun.LowPart)/double(freq.LowPart));
for the DeltaT, and I get fine results on Athlon based machines, but on P4 with hyperthreading, the value is not 100% accurate (I would say about 99 %). Any ideas ?
besides that, it's not considered a "good" practice to place a comment above each line of code
especially in german... *shivers*...
especially in german... *shivers*...
rm topic.php?which=883/*.c*
lompas: Didn't recognize that so far, but there's another problem with QueryPerformanceFrequency: On notebooks this value can skip around if the CPU switches to another clock rate.
So the safe way would be like
This way it's only one small stutter if the CPU changes its clock freq - instead of a timer jumping around wildly.
So the safe way would be like
Code:
double curtime;
LARGE_INTEGER lastpcv;
void InitTimer()
{
curtime=0;
QueryPerformanceCounter(&lastpcv);
}
double GetTimer()
{
LARGE_INTEGER count, freq;
QueryPerformanceCounter(&count);
QueryPerformanceFrequency(&freq);
curtime += double(count.QuadPart-lastpcv.QuadPart) / double(freq.QuadPart);
lastpcv=count;
return curtime;
}
This way it's only one small stutter if the CPU changes its clock freq - instead of a timer jumping around wildly.
Quote:
lompas: Didn't recognize that so far, but there's another problem with QueryPerformanceFrequency: On notebooks this value can skip around if the CPU switches to another clock rate.
hm, good that you'd mention it, i did not think about it. that's *very* nasty though.. msdn ofcourse blatantly states "result never changes as long as the system runs" - which i took for granted :))
while( 1 )
{
printf( "LOBSTARRS DOMINATION!" );
}
{
printf( "LOBSTARRS DOMINATION!" );
}
for people having problems with multiprocessor (or hyperthreading) machines, and QueryPerformanceCount, use __int64 (if it's Visual C), instead of LARGE_INTEGER and all your problems will be gone
at least, they did for me <g>
kusma: that will not print anything tho until its breaked ;)
Jcl: LARGE_INTEGER is in fact an union and the QuadPart member is __int64, so it's the same. At least it should be.
kb: yes, i also wondered that... but the fact is, that it worked for me :-)
lemme upload my (old, I use a much improved C# class now) C++ timer class
timer.h
timer.cpp
The only change to my old class is the int64 thing (int64 and int32 are typedefs) vs the LARGE_INTEGER (I still cast to LARGE_INTEGER when using the API). It was tested to work on a Multiprocessor machine where the other one failed miserably
I still wonder why, and didn't really got the time to get deeper into it... my motto: if it works, don't touch it :D
I'll post the same timer class, improved, bugfixed (the offset thing is bugged like shit, keep it zero and it'll do), and all in all nicer, when I get to my other machine
lemme upload my (old, I use a much improved C# class now) C++ timer class
timer.h
timer.cpp
The only change to my old class is the int64 thing (int64 and int32 are typedefs) vs the LARGE_INTEGER (I still cast to LARGE_INTEGER when using the API). It was tested to work on a Multiprocessor machine where the other one failed miserably
I still wonder why, and didn't really got the time to get deeper into it... my motto: if it works, don't touch it :D
I'll post the same timer class, improved, bugfixed (the offset thing is bugged like shit, keep it zero and it'll do), and all in all nicer, when I get to my other machine
for the last sentence... in C#, that is :)