// 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 ultra slow. // This version should work under both Windows and Linux platforms, // however it was only tested on Windows. // // 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 # include # define Sleep(a) usleep(a * 100) #endif using namespace std; // Constants #define VERSION "0.0.1" #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 // 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) // Main function int main() { // Banner printf("Metaballs SingleThread 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..."); // Setup color strucures and Canvas struct BGRAColor { unsigned char B,G,R,A; }; struct BGRColor { unsigned char B,G,R; }; struct RGB { float r, g, b; }; BGRAColor *Canvas = (BGRAColor*)Screen->pixels; // Setup threshold float threshold = 0.001f; // Setup metaball struct struct Metaball { float x, y; float vx, vy; float r,g,b; }; // Setup metaball colors RGB colors[] = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 0, 1, 1 }, { 1, 0, 0 }, { 0, 1, 0 }, { 0, 1, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, }; // Setup metaballs Metaball balls[10]; // 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; } // 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 row... int i,j,k; for(j = 0; j < WINDOW_HEIGHT; 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... // For each metaball... for(k = 0; k < (int)(sizeof(balls) / sizeof(Metaball)); k++) { // Move the ball balls[k].x += balls[k].vx; balls[k].y += balls[k].vy; // Screen edge reached ? if(balls[k].x < 0.0) { balls[k].x = 0.0f; balls[k].vx = -balls[k].vx; } if(balls[k].x > 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; } } // 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; } // Update buffer SDL_Flip(Screen); // Clear Ev 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; }