This is a page from the Morphyre Pro Online Manual. You can return to the Contents Page, or go to the Morphyre Pro Homepage.
We reccommend that you take a look at the section on Morphyre Modification first as it should give you a bit of an overview...
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:
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:
These are documented better on the Colour Scheme XML page.
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(); }
Open a command prompt from Windows, and type the following:
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!
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:
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
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:
The 'mat'-prefixed functions in Morphyre let you manipulate Matrices. The most useful ones are:
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).
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:
And that's it for this tutorial! Next time we move into 3D!