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 2

3D Objects and Backgrounds

 

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

 

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

 

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.

 

 

Compiling and Trying it

 

Exactly as before, open a command prompt from Windows, and type the following:

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

 

And you should see the new scene! A rotating, smoothed cube on a base of a few torii.

 

Backgrounds

 

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...

 

Dealing with multiple draws

 

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!

 

Adding the background code

 

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.

 

Adding more colour schemes

 

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:

 

  • Copy bigfoot.xml over the top of the tutorial2.xml that you created
  • Open up 'bigfoot.xml' in your favourite text editor.
  • Choose 'Replace All' and replace all the text 'bigfoot' with 'tutorial2'
  • Now save it and restart Morphyre, and you should now have all the same colour scenes the Bigfoot scene had, but in yours!

For more information on creating and modifying the Xml, see the Xml Reference