// Metaballs by Gynvael Coldwind of Vexillium // http://gynvael.coldwind.pl // http://vexillium.org // mailto: gynvael@vexillium.org // // Note: // This code was created for educational purposes, so it was supposed // to be as simple as possible, hence no real optimisation was used, // which leads to the fact that this code is just slow. // This version (multithread) was made for Windows platform only. // // License (BSD): // Copyright (c) 2008, Gynvael Coldwind of Vexillium // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Gynvael Coldwind nor Vexillium nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY Gynvael Coldwind ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL Gynvael Coldwind BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #ifdef WIN32 # include #else # error This version of metaballs is for Windows platform only. #endif using namespace std; // Constants #define VERSION "0.0.1" #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 #define THREAD_COUNT 4 // No SDLmain plz #ifdef main # undef main #endif // Macro stuff #define CHECK_TRUE(a) if(!(a)) { puts("Failed: " #a ); return 1; } #define CHECK_ZERO(a) if((a) != 0) { puts("Failed: " #a ); return 1; } #define STATUSFMT "%-40s" #define BUFFSIZE (FREQ * SAMPLE_SIZE * CHANNEL_COUNT) // Color structs struct BGRAColor { unsigned char B,G,R,A; }; struct BGRColor { unsigned char B,G,R; }; struct RGB { float r, g, b; }; // Canvas, threshold BGRAColor *Canvas; float threshold = 0.001f; // Thread struct struct MyThread { int from_y; int to_y; bool active; bool finished; bool eot; HANDLE th; }; MyThread Threads[THREAD_COUNT]; // Metaball struct struct Metaball { float x, y; float vx, vy; float r,g,b; }; Metaball balls[10]; // Thread worker function void MyThreadFunc(struct MyThread *Self) { while(!Self->eot) { // Wait for activation while(!Self->active) Sleep(1); // Calculate metaball stuff for each pixel // For each row... int i,j,k; for(j = Self->from_y; j < Self->to_y; j++) { // For each pixel in the row... for(i = 0; i < WINDOW_WIDTH; i++) { // Setup some vars float reached_thershold = 0.0f; float r = 0.0f, g = 0.0f, b = 0.0f; // For each metaball... for(k = 0; k < (int)(sizeof(balls) / sizeof(Metaball)); k++) { // Calculate the inverted squared distance float dx = i - balls[k].x; float dy = j - balls[k].y; float curr = 1.0f / (dx*dx + dy*dy); // Calculate reached threshold and colors reached_thershold += curr; r += balls[k].r * curr; g += balls[k].g * curr; b += balls[k].b * curr; } // Normalize the RGB vector float len = 1.0f / sqrtf(r*r + g*g + b*b); r *= len; g *= len; b *= len; // Set the colors if(reached_thershold >= threshold) { Canvas[i + j * WINDOW_WIDTH].R = (unsigned char)(255.0f * r); Canvas[i + j * WINDOW_WIDTH].G = (unsigned char)(255.0f * g); Canvas[i + j * WINDOW_WIDTH].B = (unsigned char)(255.0f * b); } else { Canvas[i + j * WINDOW_WIDTH].R = (unsigned char)(128.0f * r); Canvas[i + j * WINDOW_WIDTH].G = (unsigned char)(128.0f * g); Canvas[i + j * WINDOW_WIDTH].B = (unsigned char)(128.0f * b); } } // For each pixel in a row... } // For each row... // Done Self->finished = true; Self->active = false; } } // Main function int main() { // Banner printf("Metaballs MultiThread version - v." VERSION " by gynvael.coldwind//vx\n"); // Open the SDL window printf(STATUSFMT, "Initializing SDL..."); CHECK_ZERO(SDL_Init(SDL_INIT_VIDEO)); // Setup Video mode SDL_Surface *Screen; Screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 32, SDL_HWSURFACE); CHECK_TRUE(Screen); puts("OK"); // Display some cool message puts("Entering main loop..."); // Get canvas Canvas = (BGRAColor*)Screen->pixels; // Init metaball color array RGB colors[] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f, 1.0f }, { 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 1.0f }, { 1.0f, 0.0f, 1.0f }, }; // For each metaball... int m; for(m = 0; m < (int)(sizeof(balls) / sizeof(Metaball)); m++) { // Randomize metaball position balls[m].x = rand() % WINDOW_WIDTH; balls[m].y = rand() % WINDOW_HEIGHT; // Randomize metaball movement float ang = (float)(rand() % 10001) / 10000.0f; balls[m].vx = sinf(ang) * 6; balls[m].vy = cosf(ang) * 6; // Get metaball color from the array above balls[m].r = colors[m].r; balls[m].g = colors[m].g; balls[m].b = colors[m].b; } // For each thread... for(m = 0; m < THREAD_COUNT; m++) { // Setup the thread struct Threads[m].active = false; Threads[m].finished = false; Threads[m].eot = false; Threads[m].from_y = m * (WINDOW_HEIGHT / THREAD_COUNT); Threads[m].to_y = (m + 1) * (WINDOW_HEIGHT / THREAD_COUNT); Threads[m].th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThreadFunc, &Threads[m], 0, NULL); } // Initialize FPS counter float fps = 0.0f; unsigned int last_time = SDL_GetTicks(); // Main loop SDL_Event Ev; bool Done = false; for(;!Done;) { // Detach CPU (uncomment this to get lower CPU usage in this thread) //Sleep(1); // For each thread... int k; for(k = 0; k < THREAD_COUNT; k++) { // Activate the thread! Threads[k].finished = false; Threads[k].active = true; } // Wait until the threads are done while(1) { // Calculate the number of done threads int finished = 0; for(k = 0; k WINDOW_WIDTH) { balls[k].x = WINDOW_WIDTH; balls[k].vx = -balls[k].vx; } if(balls[k].y < 0.0) { balls[k].y = 0.0f; balls[k].vy = -balls[k].vy; } if(balls[k].y > WINDOW_HEIGHT) { balls[k].y = WINDOW_HEIGHT; balls[k].vy = -balls[k].vy; } } // Flip the buffer SDL_Flip(Screen); // Check fps fps += 1.0f; if(last_time + 1000 < SDL_GetTicks()) { // Reset the FPS counter last_time = SDL_GetTicks(); printf("%f fps\n",fps); fps = 0; } // Clear event struct memset(&Ev, 0, sizeof(Ev)); // Events while(SDL_PollEvent(&Ev)) { switch(Ev.type) { // Esc down ? case SDL_KEYDOWN: if(Ev.key.keysym.sym == SDLK_ESCAPE) Done = true; break; // Done ? case SDL_QUIT: Done = true; break; } } } // Done puts("Done"); // Quit SDL SDL_Quit(); // Bye return 0; }