Get Morphyre Free


This is a page from the Morphyre Pro Online Manual. You can return to the Contents Page, or go to the Morphyre Pro Homepage.

 

Custom Scene Tutorial 1

Drawing a square that rotates with the Music

 

We reccommend that you take a look at the section on Morphyre Modification first as it should give you a bit of an overview...

 

Basics of Development

 

In Morphyre Scenes are small programs that are executed to create your scene. This makes them very powerful, but also a little harder to get started with.

 

Unlike R4, in order to get Morphyre to draw a scene, you must first compile it. Generally to create a scene you'll go through these steps:

  • Create an XML file that will describe your scene in Morphyre
  • Create/edit a .pur3c scene file
  • Run morphyre_compiler from Morphyre Pro to compile the .morpharch file for your compiled scene
  • Run R5 -xml your_file.xml to view the scene you created

 

The Xml

 

Copy and paste the following into a file called MorphyrePro\scenes\tutorial1.xml

 

 

<SceneList>
    <Scene id="tutorial1_default">
        <Filename>bin/tutorial1.pur3s</Filename>
        <Data>
            <Type>scene</Type>
            <SceneId>tutorial1</SceneId>
            <SchemeId>default</SchemeId>
            <Weight>1.0</Weight>
            <Styles>chilled</Styles>
        </Data>
    </Scene>
</SceneList>

 

 

You can have more than one scene in a scenelist, but for now we'll just have one. Note that Scene.id must be unique for each scene, and is generally <SceneId> plus an underscore plus <SchemeId>. The interesting elements are:

  • Type - defines this as a scene. It could be an overlay or fade, but we'll cover those later
  • Weight - how likely this scene is to be picked when Morphyre is randomly choosing scenes
  • Styles - a comma-separated list of Styles, which are used when you click 'Theme' buttons in Morphyre

 

These are documented better on the Colour Scheme XML page.

 

The Code

 

Copy and paste the following into a file called MorphyrePro\scenes\tutorial1.pur3c

The language that the scenes are written in is very similar to C, so comments can be on a line after '//' or between '/*' and '*/'...

 

 

// Here we must include the Pur3h files that we want to use...
// See http://www.morphyre.com/ProManualHeaders to see what is in each file
#include "mathutils.pur3h"
#include "Square.pur3h"

// --------------------------- These are our variables....
// The geometry for a simple square 
CSquare square;
// The shader that we use to draw this on the screen
int shader;
// The texture we'll put on the square
int tex;
// How rotated our square is - we move this by the bass
float movement;

/* This gets called when the scene is initialised,
 it must set up everything we need */
export void init() {
  /* initialise the square. All classes *must* have create called
   explicitly if they have a .create method */
  square.create();
  /* Load the shader. It comes in two parts - a Vertex shader (beginning with v)
    and a fragment shader (beginning with f). Here we simply pass the vertices 
    through to the fragment shader, and draw a texture in it. See the shaders dir
    to find out what shaders Morphyre has by default */
  shader = shaderLoad("vSimple.glsl", "fTexture.glsl");
  /* Load the texture. We explicitly set it here, but we could say something like
    "xml://Texture" and then have a <Texture> tag in the <Data> bit of our Xml file */
  tex = texLoad("star.png");
  // set movement to be 0
  movement = 0;
}

/* This gets called when the scene is deinitialised,
 it must free everything we use */
export void kill() {
 /* Free data used by the square. All classes *must* have destroy called
    explicitly if they have a .destroy method */
  square.destroy();
  // free our shader and texture
  shaderDestroy(shader);
  texDestroy(tex);
}

/* Step gets called when Morphyre wants the scene to move. You should
put any code that deals with movement in here... */
export void step() {
  /* Increase movement by the amount of time that's passed times by
   the sound value for bass. You can use VIS_SOUND_MID or VIS_SOUND_TREBLE
   here too. Note that we generally multiply movements by VIS_TIME_DIFF
   rather than just incrementing them so on faster PCs the scene doesn't
   move faster */
  movement += visGetFloat(VIS_TIME_DIFF) * (0.5 + visGetFloat(VIS_SOUND_BASS)); 
}

/* Render gets called when Morphyre actually wants us to draw onto the screen */
export void render() {

 // Clear the screen to black
 glClear(0,0,0,1, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

 // Define two matrix variables
 Matrix mId, mRot;
 // Get the identity (does nothing) matrix
 matIdentity(&mId);
 // Multiply it by a rotation matrix that rotates by 'movement' around z (0,0,1)
 matRotate(&mRot, &mId, movement, 0, 0, 1);
 // Now set our matrices to render, setOrtho gives us a 2D (orthographic)
 // matrix between the 2 coordinates in x and the 2 coordinates in y
 float aspect = visGetFloat(VIS_ASPECT);
 setOrtho(-aspect,aspect,-1,1);
 // set the matrix that we calculated earlier
 setMatrix(MAT_MODEL, &mRot);

 // bind the shader we'll use to draw the square
 shaderBind(shader);
 // bind the texture we'll use to draw the square (on texture unit 0)
 texBind(0, tex);
 // set our colour to white
 glSetInt(GL_COLOR, rgba(1,1,1,1));
 // render out square
 square.render();
}

 

 

 

 

Compiling and Trying it

 

Open a command prompt from Windows, and type the following:

  • cd "Program Files\MorphyrePro"
  • morphyre_compile scenes\tutorial1.pur3c
  • r5 -xml tutorial1.xml

 

And you should see a rotating star! Also, when you start Morphyre UI you should now see a new scene available for you to use!

 

A Bit of Maths :(

 

To start creating Morphyre scenes you need a little understanding of some 3D computer graphics concepts. There are literally hundreds of tutorials on the internet and books so we won't reiterate it all here, but in summary:

 

Vectors


When we draw something to the screen, we have to describe it in a way that the graphics card will understand. This is usually as a series of triangles or quadrangles, defined by the positions of their corners.


We call a position or direction in 3D space a Vector, and it is stored as 3 values - X,Y and Z. In Morphyre we call this Vector3 to distinguish it from ProManualHeaders which is for 2D.

 

You can think of X as left+right, Y as up+down, and Z as in to and out of the screen, so 0,0,0 is generally considered as the middle. You can think of a direction Vector as if you drew a line from 0,0,0 to X,Y,Z... You can add vectors and subtract them and they act like you'd expect numbers to.

 

In the example above 'square' contains 4 Vectors to describe the 4 corners of the square. Calling 'square.render()' sends these to the video card to be drawn.


A good reference is:
http://www.gamedev.net/reference/articles/article1089.asp (Vector tutorial)


There are a whole series of tutorials on this any other aspects of 3D here:
http://www.gamedev.net/reference/list.asp?categoryid=28

 

Matrices


So now we can describe what we want to draw, but if we move something we don't want to have to describe it all over again. So we use Matrices. These are just a list of numbers (in our case 16 of them) which uniquely describe how something should be moved, stretched and rotated in 3D space. To draw something in a certain location you just figure out what matrix (position) you want (which we'll cover later), send it to the video card by calling setMatrix, then send what we actually want to draw to the video card (in our example with square.render() )


Note that setMatrix takes two arguments. The first is usually MAT_MODEL. In computer graphics we usually use two matrices, a special one for the camera (MAT_PROJECT), which determines how we look at what we're drawing, and one which determines how we draw the shapes themselves (MAT_MODEL). When you call setOrtho or setPerspective these change MAT_PROJECT for you, so in reality you should only have to care about MAT_MODEL.

 

There's some more information on Matrices here:
http://en.wikipedia.org/wiki/Matrix_(mathematics)

And also some tutorials:
http://www.gamedev.net/reference/articles/article415.asp (Mathematics of 3D Graphics)
http://www.gamedev.net/reference/articles/article877.asp (Matrix Math )
http://www.gamedev.net/reference/articles/article695.asp (3D Matrix Math Demystified)

You don't need to know how Matrices work, but it helps to know a few things about them:

  • Matrices can store rotation, translation(movement), and scaling
  • When you use one of these to change a position or shape, it's called 'transformation' - sometimes 'multiplication'
  • You can also multiply a Matrix by another Matrix, in which case the result is a Matrix that does the same as transforming by one matrix and then the other
  • The most simple Matrix is called the 'Identity Matrix' - this is like number 1.
  • If you transform a shape by this matrix, you get the same shape back, in the same place
  • If you multiply a Matrix by the Identity matrix, you get the same matrix
  • The order that you multiply matrices matters - A*B is different to B*A. Think of the instructions 'turn right 45 degrees' and 'walk forwards 10 paces'. If you turn then walk, you end up in a different place to if you walked and then turned.


The 'mat'-prefixed functions in Morphyre let you manipulate Matrices. The most useful ones are:

  • matIdentity - return the 'Identity' Matrix
  • matTranslate - return the second argument multiplied by a matrix that translates (moves) by X, Y and Z
  • matRotate - return the second argument multiplied by a matrix that rotates around the vector given by the given angle. Imaging the vector as being an axle on a wheel. Hence to rotate in X and Y like the example, you need the axle sticking out of the screen (which is Z).
  • matScale - return the second argument multiplied by a matrix that scales by X, Y and Z


Note that the functions return the result into the matrix in the first argument, and matTranslate/Scale/Rotate don't just return a matrix that does what you ask, they multiply their second argument by that matrix first. (If you're used to programming you may find it strange that there's an '&' character before each argument - Morphyre's scripting treats arrays slightly differently to C and Java).

Doing something!

 

Before all this maths, you put in the tutorial code that did the following...

 

Matrix mId, mRot;
matIdentity(&mId);
matRotate(&mRot, &mId, movement, 0, 0, 1);
setMatrix(MAT_MODEL, &mRot);

 

Hopefully it makes a bit more sense now. We define two Matrices (mId, and mRot), then set mId to be the Identity matrix, and multiply this by a matrix that rotates - finally we send it to the Video Card so that when we draw the square it is rotated...

 

But what if we didn't want to do anything, and just render a square. Sure, we could set movement to 0, or we could remove the rotation all together...

 
Matrix mId;
matIdentity(&mId);
setMatrix(MAT_MODEL, &mId);

 

Note: setIdentity(MAT_MODEL) does exactly the same thing as the above 3 lines.

Or we could replace matRotate with matTranslate. Using 1,0,0 will just move the square to the right by a bit.

 

Matrix mId, mT;
matIdentity(&mId);
matTranslate(&mT, &mId, 1, 0, 0);
setMatrix(MAT_MODEL, &mT);

 

Or we could move it upwards with the Bass sound instead of moving it sideways by a fixed amount...

 

Matrix mId, mT;
matIdentity(&mId);
matTranslate(&mT, &mId, 0, visGetFloat(VIS_SOUND_BASS), 0);
setMatrix(MAT_MODEL, &mT);

 

But if we still wanted to rotate? We just add matRotate, and be sure that it uses the matrix (mT) we got from matTranslate. Now the square bounces up and down but rotates too...

 

Matrix mId, mT, mRot;
matIdentity(&mId);
matTranslate(&mT, &mId, 0, visGetFloat(VIS_SOUND_BASS), 0);
matRotate(&mRot, &mT, movement, 0, 0, 1);
setMatrix(MAT_MODEL, &mRot);

 

Remember what we said before about the order that you do things? If you swap matRotate and matTranslate, the square rotates, but now instead of bouncing up and down, which direction it bounces depends on how it was rotated?

 

Matrix mId, mT, mRot;
matIdentity(&mId);
matRotate(&mRot, &mId, movement, 0, 0, 1);
matTranslate(&mT, &mRot, 0, visGetFloat(VIS_SOUND_BASS), 0);
setMatrix(MAT_MODEL, &mT);


This is very flexible, but it's all a bit long-winded - so there are a few 'utility' functions that do a lot of this for you...

 

setMatrixTranslateScale moves and scales, either you supply the first argument as a matrix, or you leave it out to use the 'identity' matrix. For example instead of the above, you can just do:

 

float size = 1+visGetFloat(VIS_SOUND_TREBLE);
setMatrixTranslateScale(
  visGetFloat(VIS_SOUND_BASS),0,0,  size,size,1);

 

This will move the share right by the bass value, and it will also make its size dependent on treble. There are no convenience functions for rotation in 3D too, as they quickly get ugly - although there is a function to deal with translation, rotation and scaling in 2D: setMatrix2DTransform

 

Matrix mId;
matIdentity(&mId);
setMatrix2DTransform(&mId, visGetFloat(VIS_SOUND_BASS),0,
  1+visGetFloat(VIS_SOUND_TREBLE), visGetFloat(VIS_SOUND_MID)*10);

 

The above code moves the square:

  • Left and Right by bass
  • Sized by treble
  • Rotated by mid-range sound

 

And that's it for this tutorial! Next time we move into 3D!