#include "precomp.h" #define DOTS 50000 // target: 50000 @ 30fps. float rx = 0, ry = 0, rz = 0; float elapsed1, elapsed2, elapsed3, elapsed4; timer t; //----------------------------------------------------------- // Game::Sort // Sort the dots according to depth (QuickSort) //----------------------------------------------------------- void Swap( vec3& a, vec3& b ) { vec3 t = a; a = b; b = t; } void Game::Sort( vec3 a[], int first, int last ) { if (first >= last) return; int p = first; vec3 e = a[first]; for (int i = first + 1; i <= last; i++) if (a[i].y < e.y) Swap( a[i], a[++p] ); Swap( a[p], a[first] ); Sort( a, first, p - 1 ); Sort( a, p + 1, last ); } //----------------------------------------------------------- // Game::Init // Initialize the application //----------------------------------------------------------- void Game::Init() { m_Dot = new Sprite( new Surface( "assets/dot.png" ), 4 ); m_Dot->SetFlags( Sprite::FLARE ); m_Points = new vec3[DOTS]; m_Rotated = new vec3[DOTS]; for (int i = 0; i < DOTS; i++) { float a = (floor( Rand( 36 ) ) + powf( Rand( 1 ), 8.0f )) * 10.0f, // arm d = powf( Rand( 1 ), 4.0f ), // distance along arm u = d * sinf( a * PI / 180 ), // point on arm v = d * cosf( a * PI / 180 ), x = u * cosf( d * -2.0f ) + v * sinf( d * -2.0f ), // swirl z = u * sinf( d * -2.0f ) - v * cosf( d * -2.0f ), y = (Rand( 2 ) - 1.0f) * 0.1f * (1 - d); // thickness m_Points[i] = vec3( x, y, z ); } } //----------------------------------------------------------- // Game::Transform // Rotate the dots around the origin using matrix m //----------------------------------------------------------- void Game::Transform() { mat4 m; m = mat4::rotatey( -ry ) * mat4::rotatex( 0.3f ); // ry += 0.00035f, rz += 0.001f; if (rx > 360) rx -= 360; if (ry > 360) ry -= 360; if (rz > 360) rz -= 360; for (int i = 0; i < DOTS; i++) { m_Rotated[i] = (vec4( m_Points[i], 1 ) * m).xyz; // HACK: encode dot index in rotated z coordinate uint* hack = (uint*)&m_Rotated[i].z; *hack = (*hack & -4) + i; } } //----------------------------------------------------------- // Game::Render // Draw the dots to the window surface //----------------------------------------------------------- void Game::Render() { t.reset(); m_Surface->Clear( 0 ); elapsed4 = t.elapsed(); int i = 0; for (; i < DOTS / 10; i++) { // extract dot index from sorted z-coordinate uint dotIdx = *(uint*)&m_Rotated[i].z & 3; // draw scaled sprite float sx = (m_Rotated[i].x * 3000.0f / (m_Rotated[i].z + 5.0f)) + (SCRWIDTH / 2); float sy = (m_Rotated[i].y * 3000.0f / (m_Rotated[i].z + 5.0f)) + (SCRHEIGHT / 2); float size = 170.0f / (m_Rotated[i].z + 5.0f); m_Dot->SetFrame( dotIdx ); m_Dot->DrawScaledAdditiveSubpixel( sx - size / 2, sy - size / 2, size, size, m_Surface ); } for (; i < DOTS; i++) { // draw subpixel particle float sx = (m_Rotated[i].x * 3000.0f / (m_Rotated[i].z + 5.0f)) + (SCRWIDTH / 2); float sy = (m_Rotated[i].y * 3000.0f / (m_Rotated[i].z + 5.0f)) + (SCRHEIGHT / 2); m_Surface->PlotSubpixel( sx, sy, 0xffffff ); } } //----------------------------------------------------------- // Game::Tick // Main function of application //----------------------------------------------------------- static float ms = 250, fps = 1, peak = 0, low = 50000; timer tt; void Game::Tick( float a_DT ) { t.reset(); Transform(); elapsed1 = t.elapsed(); t.reset(); Sort( m_Rotated, 0, DOTS / 10 - 1 ); Sort( m_Rotated, DOTS / 10, DOTS - 1 ); elapsed2 = t.elapsed(); t.reset(); Render(); elapsed3 = t.elapsed(); char t[200]; sprintf( t, "transform: %7.4f", elapsed1 ); m_Surface->Print( t, 2, 2, 0x11ffffff ); sprintf( t, "sort: %7.4f", elapsed2 ); m_Surface->Print( t, 2, 12, 0x11ffffff ); sprintf( t, "render: %7.4f - smooth: %4.1f, low: %5.1f fps: %4.1f peak: %5.2f", elapsed3, ms, low, fps, peak ); m_Surface->Print( t, 2, 22, 0x11ffffff ); sprintf( t, "clear: %7.4f", elapsed4 ); m_Surface->Print( t, 2, 32, 0x11ffffff ); ms = 0.95f * ms + 0.05f * tt.elapsed(); float curfps = 1000.0f / tt.elapsed(); fps = 0.98f * fps + 0.02f * curfps; low = min( ms, low ); peak = max( peak, fps ); tt.reset(); }