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 at the previous Tutorial first we assume you know some of the things mentioned there...
The XML for this is pretty much exactly as Tutorial 1, with the new scene name. Paste this into a file called scenes/tutorial2.xml:
<SceneList> <Scene id="tutorial2_default"> <Filename>bin/tutorial2.pur3s</Filename> <Data> <Type>scene</Type> <SceneId>tutorial2</SceneId> <SchemeId>default</SchemeId> <Weight>1.0</Weight> <Styles>chilled</Styles> </Data> </Scene> </SceneList>
The Code gets a little more complicated. We still have init, kill, step and render as before, but now we're using CJumpCamera and CKicker... Copy this code into scenes/tutorial2.pur3c:
// 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 "VertexBuffer.pur3h" #include "JumpCamera.pur3h" #include "Kicker.pur3h" // --------------------------- These are our variables.... // The shapes that we'll want to display CVertexBuffer shapea; CVertexBuffer shapeb; // The shader that we use to draw our ovjects int shader; // The texture we'll be reflecting in our objects int tex; // This positions a camera in time with the music CJumpCamera camera; // The 'kicker' moves objects round in time with the music... CKicker kicker; /* This gets called when the scene is initialised, it must set up everything we need */ export void init() { /* Load our shapes - createFromFile just loads a model from the data directory */ shapea.createFromFile("cube_smooth.t2n3v3"); shapeb.createFromFile("thintorus.t2n3v3"); /* Load the shader. It comes in two parts - a Vertex shader (beginning with v) and a fragment shader (beginning with f). Here (unlike tutorial1) we use vEnvMap to create texture coordinates that make the shape look like it's reflecting something... See the shaders dir to find out what shaders Morphyre has by default */ shader = shaderLoad("vEnvMap.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("envmap_sky.jpg"); /* Set up the camera, this takes a few parameters. The first two are the range of positions that the eye can be in (minimum and maximum values) and the next two are the range of positions to look at. Finally we have an 'up' vector which defines the direction that up and down on the screen equates to - usually this is 0,1,0. The camera moves smoothly between random positions in the ranges that are given in time with the music, and deals with making sharp jumps on sudden beats in the music. */ camera.create( Vector3(-4,-4,-5), Vector3(4,4,-3), // the range of positions the eye takes Vector3(-1,-2,-1), Vector3(1,0,1), // the range of points the 'eye' looks at Vector3(0,1,0)); // up /* initialise the 'kicker' no arguments needed here */ kicker.create(); } /* This gets called when the scene is deinitialised, it must free everything we use */ export void kill() { /* All classes *must* have destroy called explicitly if they have a .destroy method */ shapea.destroy(); shapeb.destroy(); camera.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() { // leave the camera to move on its own camera.step(); // the kicker needs 'kicking' with something - we choose bass here kicker.step(visGetFloat(VIS_TIME_DIFF), visGetFloat(VIS_SOUND_BASS)*0.2); } /* 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); // Now set our matrices to render, setPerspective gives us proper 3D drawing... /* 0.1 and 10 define what we want to be the nearest things rendered and what we want to be the furthest. You should always try and keep these as close as possible and the nearest shoul dbe greater than 0 - don't just near to 0 and far to 1000000! */ setPerspective(90/*field of view*/, visGetFloat(VIS_ASPECT), 0.1, 10); // bind the shader we'll use to draw the objects shaderBind(shader); // bind the texture we'll use to draw the objects texBind(0, tex); // set our colour to white glSetInt(GL_COLOR, rgba(1,1,1,1)); // define some matrices we need Matrix mat, mT, mRot; /* First off, we'll draw a 'base' - which is the torus. We want to move with the camera (so start off with camera.matrix), but we need to rotate as our torus is in X and Y and we want it in X and Z, and we also want to move it down a bit... */ matTranslate(&mT, &camera.matrix, 0,-2,0); // move down a bit.. matRotate(&mRot, &mT, PI/2, 1,0,0); // PI/2 = 90 degrees in radians setMatrix(MAT_MODEL, &mRot); // render the torus shapeb.renderDefault(); // actually let's render a few more torii at different sizes... for (int i=0;i<5;i++) { float size = 1 + i*0.25; matScale(&mat, &mRot, size, size, size); setMatrix(MAT_MODEL, &mat); shapeb.renderDefault(); } /* Now we want to render shape rotating around - To do this we need to combine the matrix we have from the kicker and the one from the camera... */ matMul(&mat, &camera.matrix, &kicker.matrix); setMatrix(MAT_MODEL, &mat); // render the shape we'll be kicking around... shapea.renderDefault(); }
The comments should help explain what's being done. We're using Matrices a lot more here, and they allow us to do things like the multiple rings - where we render the same shape multiple times at different positions.
Exactly as before, open a command prompt from Windows, and type the following:
And you should see the new scene! A rotating, smoothed cube on a base of a few torii.
But the black background and reflection is bit boring really... Wouldn't it be good if we could use all Morphyre's existing backgrounds? This isn't actually too hard to do...
However, to get the cool warping effects, Morphyre needs the same foreground to be draw twice - once for the normal visible stuff, and once to make the background warp. Sure, we could just copy and paste the code, but we can do it in a much better way...
Copy and paste the following above "export void render()":
void renderObjects() { }
Now, copy and paste everything below "glSetInt(GL_COLOR, rgba(1,1,1,1));" until the final "}" into renderObjects(), and finally write
renderObjects();
before the final "}"... This has greated a function that you can call multiple times, without having to copy and paste...
You can now compile and run, and amazingly it'll be just like it was before!
Now it turns out that the code to handle backgrounds actually provides shaders and textures for you, so adding backgrounds actually involves removing a lot of code! But first, up the top you need to ask for the backgrounds and shaders by adding:
#include "Background.pur3h" #include "RandomRenderSolid.pur3h"
then, replace:
int shader; int tex;
with:
CBackground background; CRandomRenderSolid solidRender;
In order to initialise the background we must replace the code we used to load our shader and textures:
shader = shaderLoad("vEnvMap.glsl", "fTexture.glsl"); tex = texLoad("envmap_sky.jpg");
with:
// initialise the foreground and background background.create(); solidRender.create();
and again we must do the same for destroy():
shaderDestroy(shader); texDestroy(tex);
with
solidRender.destroy(); background.destroy();
and now we have a background we must step that too by adding the line:
background.step();
to the step() function...
And finally, we have to render our background and everything it needs. Replace the code that was in render:
// bind the shader we'll use to draw the objects shaderBind(shader); // bind the texture we'll use to draw the objects texBind(0, tex); // set our colour to white glSetInt(GL_COLOR, rgba(1,1,1,1)); renderObjects();
with:
// render the mask - which helps warp our background if (background.maskStart()) { renderObjects(); } /* render properly - we must our camera matrix here so that when backgrounds are drawn, Morphyre knows how to position them */ setMatrix(MAT_MODEL, &camera.matrix); background.mainStart(); solidRender.renderStart(background.getBackgroundTexture()); renderObjects(); solidRender.renderEnd(); background.mainEnd();
If you're lazy you'll probably want the completed code, all of which is here:
// 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 "VertexBuffer.pur3h" #include "JumpCamera.pur3h" #include "Kicker.pur3h" #include "Background.pur3h" #include "RandomRenderSolid.pur3h" // --------------------------- These are our variables.... // The shapes that we'll want to display CVertexBuffer shapea; CVertexBuffer shapeb; // These provide the code we need to draw the foreground and background... CBackground background; CRandomRenderSolid solidRender; // This positions a camera in time with the music CJumpCamera camera; // The 'kicker' moves objects round in time with the music... CKicker kicker; /* This gets called when the scene is initialised, it must set up everything we need */ export void init() { /* Load our shapes - createFromFile just loads a model from the data directory */ shapea.createFromFile("cube_smooth.t2n3v3"); shapeb.createFromFile("thintorus.t2n3v3"); // initialise the foreground and background background.create(); solidRender.create(); /* Set up the camera, this takes a few parameters. The first two are the range of positions that the eye can be in (minimum and maximum values) and the next two are the range of positions to look at. Finally we have an 'up' vector which defines the direction that up and down on the screen equates to - usually this is 0,1,0. The camera moves smoothly between random positions in the ranges that are given in time with the music, and deals with making sharp jumps on sudden beats in the music. */ camera.create( Vector3(-4,-4,-5), Vector3(4,4,-3), // the range of positions the eye takes Vector3(-1,-2,-1), Vector3(1,0,1), // the range of points the 'eye' looks at Vector3(0,1,0)); // up /* initialise the 'kicker' no arguments needed here */ kicker.create(); } /* This gets called when the scene is deinitialised, it must free everything we use */ export void kill() { /* All classes *must* have destroy called explicitly if they have a .destroy method */ shapea.destroy(); shapeb.destroy(); camera.destroy(); solidRender.destroy(); background.destroy(); } /* Step gets called when Morphyre wants the scene to move. You should put any code that deals with movement in here... */ export void step() { // leave the camera to move on its own camera.step(); // the kicker needs 'kicking' with something - we choose bass here kicker.step(visGetFloat(VIS_TIME_DIFF), visGetFloat(VIS_SOUND_BASS)*0.2); background.step(); } void renderObjects() { // define some matrices we need Matrix mat, mT, mRot; /* First off, we'll draw a 'base' - which is the torus. We want to move with the camera (so start off with camera.matrix), but we need to rotate as our torus is in X and Y and we want it in X and Z, and we also want to move it down a bit... */ matTranslate(&mT, &camera.matrix, 0,-2,0); // move down a bit.. matRotate(&mRot, &mT, PI/2, 1,0,0); // PI/2 = 90 degrees in radians setMatrix(MAT_MODEL, &mRot); // render the torus shapeb.renderDefault(); // actually let's render a few more torii at different sizes... for (int i=0;i<5;i++) { float size = 1 + i*0.25; matScale(&mat, &mRot, size, size, size); setMatrix(MAT_MODEL, &mat); shapeb.renderDefault(); } /* Now we want to render shape rotating around - To do this we need to combine the matrix we have from the kicker and the one from the camera... */ matMul(&mat, &camera.matrix, &kicker.matrix); setMatrix(MAT_MODEL, &mat); // render the shape we'll be kicking around... shapea.renderDefault(); } /* 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); // Now set our matrices to render, setPerspective gives us proper 3D drawing... /* 0.1 and 10 define what we want to be the nearest things rendered and what we want to be the furthest. You should always try and keep these as close as possible and the nearest shoul dbe greater than 0 - don't just near to 0 and far to 1000000! */ setPerspective(90/*field of view*/, visGetFloat(VIS_ASPECT), 0.1, 10); // render the mask - which helps warp our background if (background.maskStart()) { renderObjects(); background.maskEnd(); } /* render properly - we must our camera matrix here so that when backgrounds are drawn, Morphyre knows how to position them */ setMatrix(MAT_MODEL, &camera.matrix); background.mainStart(); solidRender.renderStart(background.getBackgroundTexture()); renderObjects(); solidRender.renderEnd(); background.mainEnd(); }
And we're done with the code! You can now compile, however because we're using Colour Schemes now you'll have to edit the Xml file to get something useful... Open up tutorial2.xml and instead paste the following in:
<SceneList> <Scene id="tutorial2_default"> <Filename>bin/tutorial2a.pur3s</Filename> <Data> <Type>scene</Type> <SceneId>tutorial2</SceneId> <SchemeId>scheme15</SchemeId> <Weight>1.0</Weight> <Styles>pop,jazzy,flow</Styles> <BgType>BG_FLOW</BgType> <BgTexture>colormap.png</BgTexture> <MidType>MD_WARP</MidType> <MidTexture/> <FgType>FG_ENVMAP_SOLID</FgType> <FgTexture>groovy.png</FgTexture> <BgColour>0xFFFFFFFF</BgColour> <MaskColour>0xFF000000</MaskColour> </Data> </Scene> </SceneList>
And when you run you should see one of Morphyre's rainbow flow backgrounds and a black and white foreground.
Now your scene is just like most of the other Morphyre scenes - by adding more Xml you can quickly and easily have loads of new colour schemes. For a quick example:
For more information on creating and modifying the Xml, see the Xml Reference