Mystify 2.0

Mystify is the name of that screensaver on Windows. No, not the one with the pipes or the 3D text one – the other one. The one with the bouncing lines. You’ve got it.

Since I created some simplified OpenGL/GLFW 2D basecode yesterday and introduced it to my diploma multimedia class today, I thought I’d set them a task: to replicate this astonishing technological feat of graphical programming, only better! Like the billion dollar man version of it! In an hour!

And while they were doing their version I did mine, which looks like this…

Yup, that’ll be the mystify screensaver then.. =P

Full source code after the break for those who fancy a peak under the hood…

BounceShape.h

#ifndef BOUNCESHAPE_H
#define BOUNCESHAPE_H
 
#include <iostream>
#include <GL/gl.h>
using namespace std;
 
class BounceShape
{
public:
    GLfloat x[4], y[4];
    GLfloat xSpeed[4], ySpeed[4];
 
    GLfloat red, green, blue;
    GLfloat targetRed, targetGreen, targetBlue;
 
    static GLfloat colourChangeSpeed;
 
    static GLfloat xSpeedMultiplier, ySpeedMultiplier;
    static GLfloat xSpeedAdder, ySpeedAdder;
 
    //static GLboolean seededRand;
 
    void drawShape();
    void moveShape(GLint, GLint);
    void pickColour(GLfloat&, GLfloat&, GLfloat&);
    void shiftShapeColour(GLint);
 
    BounceShape(); // Constructor declaration
};
 
#endif

BounceShape.cpp

#include "BounceShape.h"
#include <cstdlib>
 
// Define the static factors to multiply our speeds by (so they move at a decent clip!)
GLfloat BounceShape::xSpeedMultiplier = 6.0f;
GLfloat BounceShape::ySpeedMultiplier = 6.0f;
GLfloat BounceShape::xSpeedAdder = 1.0f;
GLfloat BounceShape::ySpeedAdder = 1.0f;
 
GLfloat BounceShape::colourChangeSpeed = 0.001;
 
BounceShape::BounceShape()
{
    // Initialise the x/y and xSpeeds/ySpeeds
    for (int loop = 0; loop < 4; loop++)
    {
        x[loop] = (int)rand() % 800;
        y[loop] = (int)rand() % 600;
 
        xSpeed[loop] = ((float)rand() / (float)RAND_MAX) * xSpeedMultiplier;
        ySpeed[loop] = ((float)rand() / (float)RAND_MAX) * ySpeedMultiplier;
    }
 
    // Pick a random colour for the current colour and target colour
    pickColour(red, green, blue);
    pickColour(targetRed, targetGreen, targetBlue);
 
}
 
 
void BounceShape::drawShape()
{
    // Set the colour for the shape
    glColor3f(red, green, blue);
 
    // Draw the shape
    glBegin(GL_LINE_LOOP);
 
        for (int pointLoop = 0; pointLoop < 4; pointLoop++)
        {
            glVertex2f(x[pointLoop], y[pointLoop]);
        }
 
    glEnd();
}
 
 
void BounceShape::moveShape(int theScreenWidth, int theScreenHeight)
{
    for (int loop = 0; loop < 4; loop++)
    {
        // Move the particles
        x[loop] += xSpeed[loop];
        y[loop] += ySpeed[loop];
 
        // Bounce the particles when they hit the edge of the window
        if (x[loop] > theScreenWidth)
        {
            x[loop] = theScreenWidth;
            xSpeed[loop] = -xSpeedAdder + ((float)rand() / (float)RAND_MAX) * (xSpeedMultiplier * -1);
        }
        if (x[loop] < 0.0f)
        {
            x[loop] = 0.0f;
            xSpeed[loop] =  xSpeedAdder + ((float)rand() / (float)RAND_MAX) * xSpeedMultiplier;
        }
 
        if (y[loop] > theScreenHeight)
        {
            y[loop] = theScreenHeight;
            ySpeed[loop] = -ySpeedAdder + ((float)rand() / (float)RAND_MAX) * (ySpeedMultiplier * -1);
        }
        if (y[loop] < 0.0f)
        {
            y[loop] = 0.0f;
            ySpeed[loop] = ySpeedAdder + ((float)rand() / (float)RAND_MAX) * ySpeedMultiplier;
        }
 
    } // End of for loop
 
}
 
 
// Function to pick a new random colour that takes parameters as REFERENCES to variables, so changes are made to the original variables!
void BounceShape::pickColour(GLfloat &redVariable, GLfloat &greenVariable, GLfloat &blueVariable)
{
    redVariable   = (float)rand() / (float)RAND_MAX;
    greenVariable = (float)rand() / (float)RAND_MAX;
    blueVariable  = (float)rand() / (float)RAND_MAX;
}
 
 
void BounceShape::shiftShapeColour(int currentFrameCount)
{
    // Change the target colour every 500 frames
    if (currentFrameCount % 500 == 0)
    {
        pickColour(targetRed, targetGreen, targetBlue);
    }
 
    // Shift red component close to red target
    if (red < targetRed)
    {
        red += colourChangeSpeed;
    }
    if (red > targetRed)
    {
        red -= colourChangeSpeed;
    }
 
    // Shift green component closer to green target
    if (green < targetGreen)
    {
        green += colourChangeSpeed;
    }
    if (green > targetGreen)
    {
        green -= colourChangeSpeed;
    }
 
    // Shift blue component closer to blue target
    if (blue < targetBlue)
    {
        blue += colourChangeSpeed;
    }
    if (blue > targetBlue)
    {
        blue -= colourChangeSpeed;
    }
}

Main.cpp

#include <iostream>
#include <ctime>
#include <cstdlib>
 
#include <GL/glew.h>		// Include OpenGL headers
#include <GL/glfw.h>		// Include OpenGL Framework headers
 
#include "BounceShape.h"
 
using namespace std;
 
//bool seededRand = false;
 
void initGL(int width, int height)
{
    // ----- Window and Projection Settings -----
 
    // Set the window title
    glfwSetWindowTitle("Mystify 2.0 | r3dux.org");
 
    // Setup our viewport to be the entire size of the window
    glViewport(0, 0, (GLsizei)width, (GLsizei)height);
 
    // Change to the projection matrix, reset the matrix and set up orthagonal projection (i.e. 2D)
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, width, height, 0, 0, 1); // Paramters: left, right, bottom, top, near, far
 
    // ----- OpenGL settings -----
 
    glfwSwapInterval(1); 		// Lock to vertical sync of monitor (normally 60Hz, so 60fps)
 
    glEnable(GL_SMOOTH);		// Enable (gouraud) shading
 
    glDisable(GL_DEPTH); 		// Disable the depth buffer
 
    glEnable(GL_BLEND);			// Enable blending & specify function (used for alpha)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
    glClearAccum(0.0f, 0.0f, 0.0f, 0.0f); // Set the accumulation buffer clear colour
 
    glLineWidth(5.0f);			// Set a 'chunky' line width
 
    glEnable(GL_LINE_SMOOTH);	// Enable anti-aliasing on lines
}
 
 
void drawScene(int currentFrame, int width, int height, const int NUM_SHAPES, BounceShape* shape)
{
    // Clear the screen
    glClear(GL_COLOR_BUFFER_BIT);
 
    glAccum(GL_RETURN, 0.95); // Copy the accumulation buffer to the screen
 
    glClear(GL_ACCUM_BUFFER_BIT); // Clear the accumulation buffer
 
    // Reset the matrix
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
 
    // ----- Shape related stuff -----
 
    for (int loop = 0; loop < NUM_SHAPES; loop++)
    {
        // Draw the shape
        shape[loop].drawShape();
 
        // Move the shape on the screen
        shape[loop].moveShape(width, height);
 
        // Shift the current shape colour closer to the target colour, and reset if neccessary
        shape[loop].shiftShapeColour(currentFrame);
 
    }
 
    // ----- End of shape related stuff ------
 
    glfwSwapBuffers(); // Swap the buffers to display the scene (so we don't have to watch it being drawn!)
 
    glAccum(GL_ACCUM, 0.95f); // Copy the contents of the screen to the accumulation buffer
}
 
 
int main()
{
    // Seed the random number generator
    srand((unsigned)time(NULL));
 
    // Frame counter and window settings variables
    int frame      = 0, width     = 800, height      = 600;
    int	redBits    = 8, greenBits = 8,   blueBits    = 8;
    int alphaBits  = 8, depthBits = 0,   stencilBits = 0;
 
    // Flag to keep our main loop running
    bool running = true;
 
    // Initialise glfw
    glfwInit();
 
    // Create a window
    if(!glfwOpenWindow(width, height, redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, GLFW_WINDOW))
    {
        cout << "Failed to open window!" << endl;
        glfwTerminate();
        return 0;
    }
 
    // Call our initGL function to set up our OpenGL options
    initGL(width, height);
 
    // Create our array of shapes
    const GLint NUM_SHAPES = 2;
    BounceShape shape[NUM_SHAPES];
 
    // Main loop...
    while (running == true)
    {
        // Increase our frame counter
        frame++;
 
        // Draw our scene
        drawScene(frame, width, height, NUM_SHAPES, shape);
 
        // Exit if ESC was pressed or the window was closed
        running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED);
    }
 
    glfwTerminate();
 
    return 0;
}

Note: If you want to compile this on Windows, you’ll need to include windows.h before gl.h – just so ya know…

10 thoughts on “Mystify 2.0”

  1. Very nice! Any chance of a compiled version for download? Or even what software you recommend in order to compile this at home? That’d be cool too.

    1. I’d be happy to give you a compiled version if that’s what you’d really like, but it’s far more fun to have the source code and be able to play with it…

      So in that spirit, head on over and get yourself a copy of the MinGW version of Code::Blocks from here: http://www.codeblocks.org/downloads/26 – you want the codeblocks-12.11mingw-setup.exe version.

      Then you can grab an updated version of the Mystify code I put together as a Code::Blocks project for you from here: Mystify-2.0.zip

      The above project has the GLFW library built into it (libs and headers) so it’s completely standalone – once Code::Blocks is installed you should have no problem opening the .cbp project and then building and running the program.

      There’s lots of static constants to do with how many points are in a shape, how fast the shapes move, how often and how quickly they change colour etc. – hope you have some fun playing around with it!

  2. Why on earth didn’t I specify the number of points in the GL_LINE_LOOP to be a constant, instead of hard-coding ‘4’ everywhere?

    In fact, I’d do a ton of things differently if I wrote this again, but no, I’m just navel-gazing at an old post because somebody asked me about it, and I hadn’t looked at the post in years…

  3. I’m a composer that would very much like to utilize this technology with my music.
    Trying desperately to understand your posts made me realize (after a giant brain fart) there’s no way I could even start to understand code.
    My question is… is there a software available that I would be able to manipulate (ie via a mouse) the movement of the graphics?
    Thanks very much,
    Randy

    1. Hi Randy,

      Mmm – tough one. What you’re after is called a ‘visualiser’ (or ‘visualizer’ I suppose, depending on where you’re from). There are stacks of these available, the best one(s) I can think of come with Winamp music player (Advanced Visualisation Studio (AVS) and MilkDrop) – however, although you can switch and change between presets which react to the music dynamically (i.e. stab keys at various points during the music and the current preset changes to whatever), they don’t typically allow for a great deal of user interaction.

      One exception to this rule I can think of is the Jeff Minter written visualiser for the Xbox 360, which is called Neon – this allows you to plug in up to 4 controllers and do various things on them to control the visualisation in real-time. Here’s an example of it in action: https://www.youtube.com/watch?v=wpmSA6pxCL4.

      You might also like to look into DJ/VJ tools – as this sounds like something many people would be interested in to make good looking interactive visuals in night-clubs or such – perhaps google “music visualisation tools” or “interactive music visualisation” and you should get something that’ll be of use.

      Hope this helps!

      1. I can’t tell you how much I appreciate this!
        For years I’ve been wanting to manipulate these types of visuals but couldn’t find an app to do so.
        Although I’m sure I’ll be facing quite a learning curve (and need to buy an iPad) I think it will be worth it in the long run.
        Thanks again so much,
        Randy

          1. After your suggestions I had googled “interactive music visualisation” and found Futura Epsis- http://www.futura-epsis1.com/project/GRID
            (check out the first vid).
            I wound up downloading a trial of a program called “Magic” which is cool (as expected will have a learning curve) but I may come back to the Futura Epsis since it has so much more control ( being able to draw in instrument parts…ie…an ascending violin part moving with a sweeping, upward movement).
            At some point I’d like to draw in different parts with different colors and different widths (ie thin white strokes for violin and thicker blue strokes for cello) …Hopefully that makes sense.
            Thanks again for your post, I didn’t even know what keywords to search for!

  4. Hot diggety, a couple years ago I started a small effort to make a highly-configurable version that can be shipped with the XScreensaver project, because Mystify is the screensaver I miss most from the 95/XP era. I forgot about it until recently and have decided I would like to finally finish what little I started back when I was in school.

    With your permission I would like to take heavy inspiration (with citation!) from your color-shifting code in particular. :)

    https://github.com/EgonSpengler/mystify

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.