Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

85
Animation, Walking Between Points, and Basic Quaternions Any problems you encounter during working with this tutorial should be posted in the Help Forum . Table of contents Introduction Prerequisites Getting Started Setting up the Scene Animation Moving the Robot Additional Information Walking on terrain (aka modifying the robots y-axis) My robot does not face to the direction at the beginning? Exercises for Further Study Easy Questions Intermediate Questions Difficult Questions Expert Questions 1. Definitions 2. Fair Use Rights 3. License Grant 4. Restrictions 5. Representations, Warranties and Disclaimer 6. Limitation on Liability. 7. Termination 8. Miscellaneous Introduction In this tutorial we will be covering how to take an Entity, animate it, and have it walk between predefined points. This will also cover the basics of Quaternion rotation by showing how to keep the Entity facing the direction it is moving. As you go through the demo you should be slowly adding code to your own project and watching the results as we build it. You can see the final state of this tutorial here. Prerequisites This tutorial will assume that you already know how to set up an Ogre project and make it compile successfully. This tutorial also makes use of the STL deque data structure. While no prior knowledge of how to use a deque is required, you should at least know what templates are. If you are not familiar with STL, I would recommend picking up STL Pocket Reference [ISBN 0-596-00556-3]. This will save you a lot of time in the future. You can read the first part of "STL Pocket Reference" here Getting Started First, you need to create a new project (I called it ITutorial01) and add the following code: ITutorial01 header 1 de 15 Página Intermediate Tutorial 1e 21-05-2012 http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Transcript of Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Page 1: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Animation, Walking Between Points, and Basic Quaternions

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Table of contents

Introduction

Prerequisites

Getting Started

Setting up the Scene

Animation

Moving the Robot

Additional Information

Walking on terrain (aka modifying the robots y-axis)

My robot does not face to the direction at the beginning?

Exercises for Further Study

Easy Questions

Intermediate Questions

Difficult Questions

Expert Questions

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Introduction

In this tutorial we will be covering how to take an Entity, animate it, and have it walk between predefined points.

This will also cover the basics of Quaternion rotation by showing how to keep the Entity facing the direction it is

moving. As you go through the demo you should be slowly adding code to your own project and watching the

results as we build it.

You can see the final state of this tutorial here.

Prerequisites

This tutorial will assume that you already know how to set up an Ogre project and make it compile successfully.

This tutorial also makes use of the STL deque data structure. While no prior knowledge of how to use a deque

is required, you should at least know what templates are. If you are not familiar with STL, I would recommend

picking up STL Pocket Reference [ISBN 0-596-00556-3]. This will save you a lot of time in the future.

You can read the first part of "STL Pocket Reference" here

Getting Started

First, you need to create a new project (I called it ITutorial01) and add the following code:

ITutorial01 header

1 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 2: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#ifndef __ITutorial01_h_

#define __ITutorial01_h_

#include "BaseApplication.h"

#include <deque>

class ITutorial01 : public BaseApplication

{

public:

ITutorial01(void);

virtual ~ITutorial01(void);

protected:

virtual void createScene(void);

virtual void createFrameListener(void);

virtual bool nextLocation(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent &evt);

Ogre::Real mDistance; // The distance the object has

left to travel

Ogre::Vector3 mDirection; // The direction the object is

moving

Ogre::Vector3 mDestination; // The destination the object

is moving towards

Ogre::AnimationState *mAnimationState; // The current animation state

of the object

Ogre::Entity *mEntity; // The Entity we are animating

Ogre::SceneNode *mNode; // The SceneNode that the

Entity is attached to

std::deque<Ogre::Vector3> mWalkList; // The list of points we are

walking to

Ogre::Real mWalkSpeed; // The speed at which the

object is moving

};

#endif // #ifndef __ITutorial01_h_

#include "ITutorial01.h"

//-------------------------------------------------------------------------

------------

ITutorial01::ITutorial01(void)

{

ITutorial01 implementation

2 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 3: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

}

//-------------------------------------------------------------------------

------------

ITutorial01::~ITutorial01(void)

{

}

//-------------------------------------------------------------------------

------------

void ITutorial01::createScene(void)

{

}

void ITutorial01::createFrameListener(void){

BaseApplication::createFrameListener();

}

bool ITutorial01::nextLocation(void){

return true;}

bool ITutorial01::frameRenderingQueued(const Ogre::FrameEvent &evt){

return BaseApplication::frameRenderingQueued(evt);

}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

ITutorial01 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception

has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() << std::endl;

#endif

}

3 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 4: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

return 0;

}

#ifdef __cplusplus

}

#endif

Be sure you can compile this code before continuing.

Setting up the Scene

Before we begin, notice that we have already defined 3 variables in the header file. The mEntity will hold the

entity we create, mNode will hold the node we create, and mWalkList will contain all the points we wish the

object to walk to.

Go to the ITutorial01::createScene function and add the following code to it. First we are going to set the

ambient light to full so that we can see objects we put on the screen.

// Set the default lighting.

mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0f, 1.0f, 1.0f));

Next we will create a Robot on the screen so that we can play with him. To do this we will create the entity for

the Robot, then create a SceneNode for him to dangle from.

// Create the entity

mEntity = mSceneMgr->createEntity("Robot", "robot.mesh");

// Create the scene node

mNode = mSceneMgr->getRootSceneNode()->

createChildSceneNode("RobotNode", Ogre::Vector3(0.0f, 0.0f,

25.0f));

mNode->attachObject(mEntity);

This all should be very basic, so I will not go into detail about any of it. In the next chunk of code, we are going

to tell the robot where he needs to be moved to. For those of you who don't know anything about STL, the

deque object is an efficient implementation of a double ended queue. We will only be using a few of its

methods. The push_front and push_back methods put items at the front and back of the deque respectively.

The front and back methods return the values at the front and back of the deque respectively. The pop_front

and pop_back methods remove the items from the front and back of the queue respectively. Finally, the empty

method returns whether or not the deque is empty. This code adds two Vectors to the deque, which we will

later make the robot move to.

// Create the walking list

mWalkList.push_back(Ogre::Vector3(550.0f, 0.0f, 50.0f ));

mWalkList.push_back(Ogre::Vector3(-100.0f, 0.0f, -200.0f));

Next, we want to place some objects on the scene to show where the robot is supposed to be moving to. This

will allow us to see the robot moving with respect to other objects on the screen. Notice the negative Y

4 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 5: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

component to their position. This puts the objects under where the robot is moving to, and he will stand on top

of them when he gets to the right spot.

// Create objects so we can see movement

Ogre::Entity *ent;

Ogre::SceneNode *node;

ent = mSceneMgr->createEntity("Knot1", "knot.mesh");

node = mSceneMgr->getRootSceneNode()->createChildSceneNode

("Knot1Node",

Ogre::Vector3(0.0f, -10.0f, 25.0f));

node->attachObject(ent);

node->setScale(0.1f, 0.1f, 0.1f);

ent = mSceneMgr->createEntity("Knot2", "knot.mesh");

node = mSceneMgr->getRootSceneNode()->createChildSceneNode

("Knot2Node",

Ogre::Vector3(550.0f, -10.0f, 50.0f));

node->attachObject(ent);

node->setScale(0.1f, 0.1f, 0.1f);

ent = mSceneMgr->createEntity("Knot3", "knot.mesh");

node = mSceneMgr->getRootSceneNode()->createChildSceneNode

("Knot3Node",

Ogre::Vector3(-100.0f, -10.0f,-200.0f));

node->attachObject(ent);

node->setScale(0.1f, 0.1f, 0.1f);

Finally, we want to set the camera to a good viewing point to see this from. We will move the camera to get a

better position.

// Set the camera to look at our handiwork

mCamera->setPosition(90.0f, 280.0f, 535.0f);

mCamera->pitch(Ogre::Degree(-30.0f));

mCamera->yaw(Ogre::Degree(-15.0f));

Now compile and run the code.

Animation

We are now going to setup some basic animation. Animation in Ogre is very simple. To do this, you need to get

the AnimationState from the Entity object, set its options, and enable it. This will make the animation active, but

you will also need to add time to it after each frame in order for the animation to run. We'll take this one step at

a time. First, go to ITutorial01::createFrameListener and add the following code, after the call to

BaseApplication::createFrameListener:

// Set idle animation

mAnimationState = mEntity->getAnimationState("Idle");

mAnimationState->setLoop(true);

mAnimationState->setEnabled(true);

5 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 6: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

The second line gets the AnimationState out of the entity. In the third line we call setLoop( true ), which makes

the animation loop over and over. For some animations (like the death animation), we would want to set this to

false instead. The fourth line actually enables the Animation. But wait...where did we get ‘idle’ from? How did

this magic constant slip in there? Every mesh has their own set of Animations defined for them. In order to see

all of the Animations for the particular mesh you are working on, you need to download the OgreMeshViewer

and view the mesh from there.

Now, if we compile and run the demo we see...nothing has changed. This is because we need to update the

animation state with a time every frame. Find the ITutorial01::frameRenderingQueued method, and add this line

of code at the beginning of the function:

mAnimationState->addTime(evt.timeSinceLastFrame);

Now build and run the application. You should see a robot performing his idle animation standing in place.

Moving the Robot

Now we are going to perform the tricky task of making the robot walk from point to point. Before we begin I

would like to describe the variables that we have defined. We are going to use 4 variables to accomplish the

task of moving the robot. First of all, we are going to store the direction the robot is moving in mDirection. We

will store the current destination the Robot is traveling to in mDestination. We will store the distance the robot

has left to travel in mDistance. Finally, we will store the robot's moving speed in mWalkSpeed.

The first thing we need to do is to set up these variables. We'll set the walk speed to 35 units per second. There

is one big thing to note here. We are explicitly setting mDirection to be the ZERO vector because later we will

use this to determine if we are moving the Robot or not. Add the following code to

ITutorial01::createFrameListener:

// Set default values for variables

mWalkSpeed = 35.0f;

mDirection = Ogre::Vector3::ZERO;

Now that this is done, we need to set the robot in motion. To make the robot move, we simply tell it to change

animations. However, we only want to start the robot moving if there is another location to move to. For this

reason we call the ITutorial01::nextLocation function. Add this code to the top of the

ITutorial01::frameRenderingQueued method just before the AnimationState::addTime call:

if (mDirection == Ogre::Vector3::ZERO)

{

if (nextLocation())

{

// Set walking animation

mAnimationState = mEntity->getAnimationState("Walk");

mAnimationState->setLoop(true);

mAnimationState->setEnabled(true);

}

}

6 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 7: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

If you compile and run the code right now, the robot will walk in place. This is because the robot starts out with

a direction of ZERO and our ITutorial01::nextLocation function always returns true. In later steps we will be

adding a bit more intelligence to the ITutorial01::nextLocation function.

Now we are going to actually move the robot in the scene. To do this we need to have him move a small bit

every frame. Go to the ITutorial01::frameRenderingQueued method. We will be adding the following code just

after our previous if statement and just above the AnimationState::addTime call. This code will handle the case

when the robot is actually moving; mDirection != Ogre::Vector3::ZERO.

the reason why mWalkspeed is multiplied by evt.timeSinceLastFrame, is to keep the walkspeed constant,

despite variations in framerate.

if you only had written Real move = mWalkspeed, the robot would walk slow on a slow computer, and walk fast

on a fast one.

else

{

Ogre::Real move = mWalkSpeed * evt.timeSinceLastFrame;

mDistance -= move;

Now, we need to check and see if we are going to overshoot the target position. That is, if mDistance is now

less than zero, we need to jump to the point and set up the move to the next point. Note that we are setting

mDirection to the ZERO vector. If the nextLocation method does not change mDirection (IE there is nowhere

left to go) then we no longer have to move around.

if (mDistance <= 0.0f)

{

mNode->setPosition(mDestination);

mDirection = Ogre::Vector3::ZERO;

Now that we have moved to the point, we need to setup the motion to the next point. Once we know if we need

to move to another point or not, we can set the appropriate animation; walking if there is another point to go to

and idle if there are no more destination points. This is a simple matter of setting the Idle animation if there are

no more locations.

// Set animation based on if the robot has another point to walk to.

if (! nextLocation())

{

// Set Idle animation

mAnimationState = mEntity->getAnimationState("Idle");

mAnimationState->setLoop(true);

mAnimationState->setEnabled(true);

}

else

{

// Rotation Code will go here later

7 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 8: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

}

}

Note that we have no need to set the walking animation again if there are more points in the queue to walk to.

Since the robot is already walking there is no reason to tell him to do so again. However, if the robot needs to

go to another point, then we need to rotate him to face that point. For now we leave a placeholder comment in

the else clause; remember this spot as we will come back to it later.

This takes care of when we are very close to the target position. Now we need to handle the normal case, when

we are just on the way to the position but we're not there yet. To do that we will translate the robot in the

direction we are traveling, and move it by the amount specified by the move variable. This is accomplished by

adding the following code:

else

{

mNode->translate(mDirection * move);

} // else

} // if

We are almost done. Our code now does everything except set up the variables required for movement. If we

can properly set the movement variables our Robot will move like he is supposed to. Find the

ITutorial01::nextLocation function. This function returns false when we run out of points to go to. This will be the

first line of our function. (Note you should leave the return true statement at the bottom of the function.)

if (mWalkList.empty())

return false;

Now we need to set the variables (still in the nextLocation method). First we will pull the destination vector from

the deque. We will set the direction vector by subtracting the SceneNode's current position from the destination.

We have a problem though. Remember how we multiplied mDirection by the move amount in

frameRenderingQueued? If we do this, we need the direction vector to be a unit vector (that is, it's length

equals one). The normalise function does this for us, and returns the old length of the vector. Handy that, since

we need to also set the distance to the destination.

mDestination = mWalkList.front(); // this gets the front of the deque

mWalkList.pop_front(); // this removes the front of the

deque

mDirection = mDestination - mNode->getPosition();

mDistance = mDirection.normalise();

Now compile and run the code. It works! Sorta. The robot now walks to all the points, but he is always facing

the Ogre::Vector3::UNIT_X direction (his default). We will need to change the direction he is facing when he is

moving towards points.

What we need to do is get the direction the Robot is facing, and use rotate function to rotate the object in the

right position. Insert the following code where we left our placeholder comment in the previous step. The first

line gets the direction the Robot is facing. The second line builds a Quaternion representing the rotation from

the current direction to the destination direction. The third line actually rotates the Robot.

8 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 9: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_X;

Ogre::Quaternion quat = src.getRotationTo(mDirection);

mNode->rotate(quat);

We briefly mentioned Quaternions in Basic Tutorial 4, but this is the first real use we have had for them.

Basically speaking, Quaternions are representations of rotations in 3 dimensional space. They are used to keep

track of how the object is positioned in space, and may be used to rotate objects in Ogre. In the first line we call

the getOrientation method, which returns a Quaternion representing the way the Robot is oriented in space.

Since Ogre has no idea which side of the Robot is the "front" of the robot, we must multiply this orientation by

the UNIT_X vector (which is the direction the robot "naturally" faces) to we obtain the direction the robot is

currently facing. We store this direction in the src variable. In the second line, the getRotationTo method gives

us a Quaternion that represents the rotation from the direction the Robot is facing to the direction we want him

to be facing. In the third line, we rotate the node so that it faces the new orientation.

There is only one problem with the code we have created. There is a special case where SceneNode::rotate

will fail. If we are trying to turn the robot 180 degrees, the rotate code will bomb with a divide by zero error. In

order to fix that, we will test to see if we are performing a 180 degree rotation. If so, we will simply yaw the robot

by 180 degrees instead of using rotate. To do this, delete the three lines we just put in and replace them with

this:

Ogre::Vector3 src = mNode->getOrientation() * Ogre::Vector3::UNIT_X;

if ((1.0f + src.dotProduct(mDirection)) < 0.0001f)

{

mNode->yaw(Ogre::Degree(180));

}

else

{

Ogre::Quaternion quat = src.getRotationTo(mDirection);

mNode->rotate(quat);

} // else

All of this should now be self explanatory except for what is wrapped in that if statement. If two unit vectors

oppose each other (that is, the angle between them is 180 degrees), then their dot product will be -1. So, if we

dotProduct the two vectors together and the result equals -1.0f, then we need to yaw by 180 degrees,

otherwise we use rotate instead. Why do I add 1.0f and check to see if it is less than 0.0001f? Don't forget

about floating point rounding error. You should never directly compare two floating point numbers. Finally, note

that in this case the dot product of these two vectors will fall in the range [-1, 1]. In case it is not abundantly

clear, you need to know at minimum basic linear algebra to do graphics programming! At the very least you

should review the Quaternion and Rotation Primer and consult a book on basic vector and matrix operations.

Now our code is complete! Compile and run the demo to see the Robot walk the points he was given.

Additional Information

Walking on terrain (aka modifying the robots y-axis)

The code for rotation of the robot will not work properly when also changing the y-axis of the robot. You might

see unexpected roll and pitch.

This can be fixed with the code below which was sponsered by the article Quaternion and Rotation Primer.

9 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 10: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Vector3 mDestination = mWalkList.front( ); //

mDestination is the next location

Vector3 mDirection = mDestination - mNode->getPosition(); // B-A = A->B

(see vector questions above)

Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; //

Orientation from initial direction

src.y = 0; // Ignore

pitch difference angle

mDirection.y = 0;

src.normalise();

Real mDistance = mDirection.normalise( ); // Both

vectors modified so renormalize them

Quaternion quat = src.getRotationTo(mDirection);

mNode->rotate(quat);

My robot does not face to the direction at the beginning?

The robot is turned when it arrives a waypoint. To properly turn the robot in all cases you best put the rotation

code to a new function:

//put this line to the header

void rotateRobotToDirection(void);

//this to the application itself

void ITutorial01::rotateRobotToDirection(void) {

Vector3 src = mNode->getOrientation() * Vector3::UNIT_X; //

Orientation from initial direction

src.y = 0; // Ignore

pitch difference angle

mDirection.y = 0;

src.normalise();

Real mDistance = mDirection.normalise( ); // Both

vectors modified so renormalize them

Quaternion quat = src.getRotationTo(mDirection);

mNode->rotate(quat);

}

Now you can use this function in the else-part of "if (! nextLocation())". And you can add it to this part of the

code to rotate the robot to the walking direction right at the beginning:

if (mDirection == Ogre::Vector3::ZERO)

{

if (nextLocation())

{

// Set walking animation

mAnimationState = mEntity->getAnimationState("Walk");

mAnimationState->setLoop(true);

10 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 11: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

mAnimationState->setEnabled(true);

rotateRobotToDirection();

}

}

else

//...

Exercises for Further Study

Easy Questions

1. Add more points to the robot's path. Be sure to also add more knots that sit under his position so you

can track where he is supposed to go.

2. Robots who have outlived their usefulness should not continue existing! When the robot has finished

walking, have him perform the death animation instead of idle. The animation for death is ‘Die’.

Intermediate Questions

1. There is something wrong with mWalkSpeed. Did you notice this when going through the tutorial? We

only set the value once, and never change it. This should be a constant static class variable. Change the

variable so that it is.

2. The code does something very hacky, and that's track whether or not the Robot is walking by looking at

the mDirection vector and comparing it to Vector3::ZERO. It would have been better if we instead had a

boolean variable called mWalking that kept track of whether or not the robot is moving. Implement this

change.

Difficult Questions

1. One of the limitations of this class is that you cannot add points to the robot's walking path after you

have created the object. Fix this problem by implementing a new method which takes in a Vector3 and

adds it to the mWalkList deque. (Hint, if the robot has not finished walking you will only need to add the

point to the end of the deque. If the robot has finished, you will need to make him start walking again,

and call nextLocation to start him walking again.)

Expert Questions

1. Another major limitation of this class is that it only tracks one object. Reimplement this class so that it

can move and animate any number of objects independently of each other. (Hint, you should create

another class that contains everything that needs to be known to animate one object completely. Store

this in a STL map object so that you can retrieve data later based on a key.) You get bonus points if you

can do this without registering any additional frame listeners.

2. After making the previous change, you might have noticed that Robots can now collide with each other.

Fix this by either creating a smart path finding function, or detecting when robots collide and stopping

them from passing through each other.

11 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 12: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Proceed to Intermediate Tutorial 2 RaySceneQueries and Basic Mouse Usage

Alias: Intermediate_Tutorial_1?

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR

COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE

BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED

HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

• "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the

Work in its entirety in unmodified form, along with a number of other contributions, constituting separate

and independent works in themselves, are assembled into a collective whole. A work that constitutes a

Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this

License.

• "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works,

such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound

recording, art reproduction, abridgment, condensation, or any other form in which the Work may be

recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be

considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the

Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with

a moving image ("synching") will be considered a Derivative Work for the purpose of this License.

• "Licensor" means the individual or entity that offers the Work under the terms of this License.

• "Original Author" means the individual or entity who created the Work.

• "Work" means the copyrightable work of authorship offered under the terms of this License.

• "You" means an individual or entity exercising rights under this License who has not previously violated

the terms of this License with respect to the Work, or who has received express permission from the

Licensor to exercise rights under this License despite a previous violation.

• "License Elements" means the following high-level license attributes as selected by Licensor and

indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other

limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-

exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as

stated below:

12 de 15Página Intermediate Tutorial 1e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=273&page=Intermediate...

Page 13: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

RaySceneQueries and Basic Mouse Usage (Part 1 of 2)

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Table of contents

Introduction

Prerequisites

Getting Started

Setting up the Scene

Introducing the FrameListener

Setting up the FrameListener

Adding Mouse Look

Terrain Collision Detection

Terrain Selection

Exercises for Further Study

Easy Exercises

Intermediate Exercises

Advanced Exercises

Exercises for Further Study

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Introduction

In this tutorial we will create the beginnings of a basic Scene Editor. During this process, we will cover:

1. How to use RaySceneQueries to keep the camera from falling through the terrain

2. How to use the MouseListener and MouseMotionListener interfaces

3. Using the mouse to select x and y coordinates on the terrain

Here is the code for Intermediate Tutorial 2. As you go through the tutorial you should be slowly adding code

to your own project and watching the results as we build it.

Prerequisites

This tutorial will assume that you already know how to set up an Ogre project and make it compile successfully.

Knowledge of basic Ogre objects (SceneNodes, Entities, etc) is assumed. You should also be familiar with

basic STL iterators, as this tutorial uses them. (Ogre also uses a lot of STL, if you are not familiar with it, you

should take the time to learn it.)

This Tutorial makes use of CEGUI, you should have completed the steps in the Basic Tutorial 7 CEGUI and

Ogre To ensure that this tutorial will work as expected.

1 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 14: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

NOTE: If you want to try the tutorial without using CEGUI you can find the source code using the built in

SdkTrays here.

Getting Started

First, you need to create a new project and add the following code:

#ifndef __ITutorial02_h_

#define __ITutorial02_h_

#include "BaseApplication.h"

class ITutorial02 : public BaseApplication

{

public:

ITutorial02(void);

virtual ~ITutorial02(void);

protected:

virtual void createScene(void);

virtual void chooseSceneManager(void);

virtual void createFrameListener(void);

//frame listener

virtual bool frameRenderingQueued(const Ogre::FrameEvent &evt);

//mouse listener

virtual bool mouseMoved(const OIS::MouseEvent &arg);

virtual bool mousePressed(const OIS::MouseEvent

&arg,OIS::MouseButtonID id);

virtual bool mouseReleased(const OIS::MouseEvent

&arg,OIS::MouseButtonID id);

protected:

Ogre::RaySceneQuery *mRaySceneQuery;// The ray scene query pointer

bool mLMouseDown, mRMouseDown; // True if the mouse

buttons are down

int mCount; // The

number of robots on the screen

Ogre::SceneNode *mCurrentObject; // The newly created object

CEGUI::Renderer *mGUIRenderer; // CEGUI renderer

float mRotateSpeed;

};

#endif // #ifndef __ITutorial02_h_

ITutorial02 header

ITutorial02 implementation

2 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 15: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#include <CEGUISystem.h>

#include <CEGUISchemeManager.h>

#include <RendererModules/Ogre/CEGUIOgreRenderer.h>

#include "ITutorial02.h"

//-------------------------------------------------------------------------

------------

ITutorial02::ITutorial02(void)

{

}

//-------------------------------------------------------------------------

------------

ITutorial02::~ITutorial02(void)

{

}

//-------------------------------------------------------------------------

------------

void ITutorial02::createScene(void)

{}

void ITutorial02::createFrameListener(void)

{

BaseApplication::createFrameListener();

}

void ITutorial02::chooseSceneManager(void)

{

// Use the terrain scene manager.

mSceneMgr = mRoot->createSceneManager(Ogre::ST_EXTERIOR_CLOSE);

}

bool ITutorial02::frameRenderingQueued(const Ogre::FrameEvent &evt)

{return BaseApplication::frameRenderingQueued(evt);}

bool ITutorial02::mouseMoved(const OIS::MouseEvent &arg)

{return true;}

bool ITutorial02::mousePressed(const OIS::MouseEvent &arg,

OIS::MouseButtonID id)

{return true;}

bool ITutorial02::mouseReleased(const OIS::MouseEvent &arg,

OIS::MouseButtonID id)

{return true;}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

3 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 16: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

ITutorial02 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception

has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() << std::endl;

#endif

}

return 0;

}

#ifdef __cplusplus

}

#endif

Be sure this code compiles before continuing. You will most likely need to configure your project to recognise

CEGUI since it is no longer packaged with Ogre as of Ogre 1.7, so remember to add to your project properties

(in Visual Studio):

• In C/C++, add the path to the CEGUI include files to Additional Include Directories,

• In the Linker options, add the path to CEGUI lib files to Additional Library Directories and add

"CEGUIOgreRenderer_d.lib" and "CEGUIBase_d.lib" to the Additional Dependencies (remove the _d for

the Release configuration)

• Add the path to the CEGUI bin directory to the Environment Variables or copy the CEGUI DLLs

somewhere your project can find them.

Similarly, add these to your project (in Eclipse):

Right click project -> Properties -> C/C++ Build -> Settings

-> GCC C++ Compiler -> Includes

and add the full path : /usr/local/include/CEGUI (on Linux)

-> GCC C++ Linker -> Linker -> Libraries

4 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 17: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

and this to Libraries (-l): CEGUIOgreRenderer

Setting up the Scene

Go to the ITutorial02::createScene method. The following code should all be familiar. If you do not

know what something does, please consult the Ogre API reference before continuing. Add this to createScene:

// Set ambient light

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

// World geometry

mSceneMgr->setWorldGeometry("terrain.cfg");

// Set camera look point

mCamera->setPosition(40, 100, 580);

mCamera->pitch(Ogre::Degree(-30));

mCamera->yaw(Ogre::Degree(-45));

Now that we have the basic world geometry set up, we need to turn on the cursor. We do this using some

CEGUI function calls. Before we can do that, however, we need to start up CEGUI. This is now very easy- the

bootstrapSystem() method will do all the required setup for us. Note that this also makes CEGUI use

Ogre's resource management system.

// CEGUI setup

mGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem();

Now we need to actually show the cursor. Again, I'm not going to explain most of this code. We will revisit it in a

later tutorial.

// Mouse

CEGUI::SchemeManager::getSingleton().create

((CEGUI::utf8*)"TaharezLook.scheme");

CEGUI::MouseCursor::getSingleton().setImage("TaharezLook",

"MouseArrow");

If you compile and run the code, you will see a cursor at the center of the screen, but it will not move (yet).

It is likely that you will also need to tell the Ogre resource manager about the CEGUI resources. If you get

exceptions at runtime, try adding the following to resources.cfg:

[CEGUI]

FileSystem=/usr/share/CEGUI/schemes

FileSystem=/usr/share/CEGUI/fonts

FileSystem=/usr/share/CEGUI/imagesets

5 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 18: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

FileSystem=/usr/share/CEGUI/layouts

FileSystem=/usr/share/CEGUI/looknfeel

FileSystem=/usr/share/CEGUI/lua_scripts

FileSystem=/usr/share/CEGUI/schemes

FileSystem=/usr/share/CEGUI/xml_schemas

Introducing the FrameListener

That was all that needed to be done for the application. The FrameListener is the complicated portion of the

code, so I will spend some time outlining what we are trying to accomplish with the application so you have an

idea before we start implementing it.

• First, we want to bind the right mouse button to a "mouse look" mode. It's fairly annoying not being able

to use the mouse to look around, so our first priority will be adding mouse control back to the program

(though only when we hold the right mouse button down). NOTE: the tutorial framework already handles

camera control thanks to the sdkCameraMan class from OgreBites but for the sake of learning we will

be implementing camera control from scratch.

• Second, we want to make it so that the camera does not pass through the Terrain. This will make it

closer to how we would expect a program like this to work.

• Third, we want to add entities to the scene anywhere on the terrain we left click.

• Finally, we want to be able to "drag" entities around; that is, by left clicking and holding the button down

we want to see the entity, and move it to where we want to place it. Letting go of the button will actually

lock it in place.

To do this we are going to use several protected variables (these are already added to the class):

Ogre::RaySceneQuery *mRaySceneQuery; // The ray scene query pointer

bool mLMouseDown, mRMouseDown; // True if the mouse buttons are

down

int mCount; // The number of robots on the

screen

Ogre::SceneNode *mCurrentObject; // The newly created object

CEGUI::Renderer *mGUIRenderer; // cegui renderer

float mRotateSpeed;

The mRaySceneQuery variable holds a copy of the RaySceneQuery we will be using to find the coordinates

on the terrain. The mLMouseDown and mRMouseDown variables will track whether we have the mouse held

down (IE mLMouseDown is true when the user holds down the left mouse button, false otherwise). mCount

counts the number of entities we have on screen. mCurrentObject holds a pointer to the most recently

created SceneNode (we will be using this to "drag" the entity around). Finally, mGUIRenderer holds a pointer

to the CEGUI Renderer, which we will be using to update CEGUI.

Also note that there are many functions related to Mouse listeners that we are overriding to provide camera

control

Setting up the FrameListener

6 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 19: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Go to the createFrameListener method, and add the following initialization code after the call to

BaseApplication::createFrameListener(). Note that we are also reducing rotation speed of the

camera since the Terrain is fairly small.

// Setup default variables

mCount = 0;

mCurrentObject = NULL;

mLMouseDown = false;

mRMouseDown = false;

// Reduce rotate speed

mRotateSpeed =.1;

Finally, we need to create the RaySceneQuery object. This is done with a call to the SceneManager:

// Create RaySceneQuery

mRaySceneQuery = mSceneMgr->createRayQuery(Ogre::Ray());

This is all we need for createFrameListener(), but if we create a RaySceneQuery, we must later destroy

it. Go to the ITutorial02 destructor (~ITutorial02) and add the following line:

// We created the query, and we are also responsible for deleting it.

mSceneMgr->destroyQuery(mRaySceneQuery);

Be sure you can compile your code before moving on to the next section.

Adding Mouse Look

We are going to bind the mouse look mode to the right mouse button. To do this, we are going to:

• update CEGUI when the mouse is moved (so that the cursor is also moved)

• set mRMouseButton to true when the right mouse button is pressed

• set mRMouseButton to false when it is released

• change the view when the mouse is "dragged" (that is, when a button is held down as the mouse

moves)

• hide the mouse cursor when the mouse is dragging

Find the ITutorial02::mouseMoved method. We will be adding code to move the mouse cursor every time

the mouse has been moved. Add this code to the function:

// Update CEGUI with the mouse motion

CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel,

arg.state.Y.rel);

7 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 20: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Now find the ITutorial02::mousePressed method. This chunk of code hides the cursor when the right

mouse button goes down, and sets the mRMouseDown variable to true.

// Left mouse button down

if (id == OIS::MB_Left)

{

mLMouseDown = true;

} // if

// Right mouse button down

else if (id == OIS::MB_Right)

{

CEGUI::MouseCursor::getSingleton().hide();

mRMouseDown = true;

} // else if

Next we need to show the mouse cursor again and toggle mRMouseDown when the right button is let up. Find

the mouseReleased function, and add this code:

// Left mouse button up

if (id == OIS::MB_Left)

{

mLMouseDown = false;

} // if

// Right mouse button up

else if (id == OIS::MB_Right)

{

CEGUI::MouseCursor::getSingleton().show();

mRMouseDown = false;

} // else if

Now we have all of the prerequisite code written, we want to change the view when the mouse is moved while

holding the right button down. What we are going to do is read the distance it has moved since the last time the

method was called. This is done in the same way that we rotated the camera in Basic Tutorial 5. Find the

ITutorial::mouseMoved function and add the following code just before the return statement:

// If we are dragging the left mouse button.

if (mLMouseDown)

{

} // if

// If we are dragging the right mouse button.

else if (mRMouseDown)

{

mCamera->yaw(Ogre::Degree(-arg.state.X.rel * mRotateSpeed));

8 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 21: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

mCamera->pitch(Ogre::Degree(-arg.state.Y.rel * mRotateSpeed));

} // else if

Now if you compile and run this code you will be able to control where the camera looks by holding the right

mouse button down.

Terrain Collision Detection

We are now going to make it so that when we move towards the terrain, we cannot pass through it. Since

BaseApplication::createFrameListener() already handles the camera movement, we are not going

to touch that code. Instead, after BaseApplication::createFrameListener() moves the camera we are

going to make sure the camera is 10 units above the terrain. If it is not, we are going to move it there. Please

follow this code closely. We will use the RaySceneQuery to do several other things by the time this tutorial is

finished, and I will not go into as much detail after this section.

Go to the ITutorial02::frameRenderingQueued() method and remove its contents. The first thing we

are going to do is call the BaseApplication::frameRenderingQueued method to do all of its normal

functions. If it returns false, we will return false as well.

// Process the base frame listener code. Since we are going to be

// manipulating the translate vector, we need this to happen

first.

if (!BaseApplication::frameRenderingQueued(evt))

return false;

We do this at the top of our frameRenderingQueued function because the

BaseApplication::frameRenderingQueued member function handles the updating of the TrayManager

window from OgreBites (the FPS window and Ogre logo) and we need to perform the rest of our actions in this

function after this happens. Our goal is to find the camera's current position, and fire a Ray straight down into

the terrain. This is called a RaySceneQuery, and it will tell us the height of the Terrain below us. After getting

the camera's current position, we need to create a Ray. A Ray takes in an origin (where the ray starts), and a

direction. In this case our direction will be NEGATIVE_UNIT_Y, since we are pointing the ray straight down.

Once we have created the ray, we tell the RaySceneQuery object to use it.

// Setup the scene query

Ogre::Vector3 camPos = mCamera->getPosition();

Ogre::Ray cameraRay(Ogre::Vector3(camPos.x, 5000.0f, camPos.z),

Ogre::Vector3::NEGATIVE_UNIT_Y);

mRaySceneQuery->setRay(cameraRay);

Note that we have used a height of 5000.0f instead of the camera's actual position. If we used the camera's Y

position instead of this height, we would miss the terrain entirely if the camera were under it. Now we need to

execute the query and get the results. The results of the query come in the form of an std::iterator, which

I will briefly describe.

9 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 22: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

// Perform the scene query

Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();

Ogre::RaySceneQueryResult::iterator itr = result.begin();

The result of the query is basically (oversimplification here) a list of worldFragments (in this case the Terrain)

and a list of movables (we will cover movables in a later tutorial). If you are not familiar with STL iterators, just

know that to get the first element of the iterator, call the begin method. If the result.begin() ==

result.end(), then there were no results to return. In the next tutorial we will have to deal with multiple

return values for SceneQuerys. For now, we'll just do some hand waving and move through it. The following

line of code ensures that the query returned at least one result ( itr != result.end() ), and that the result

is the terrain (itr->worldFragment).

// Get the results, set the camera height

if (itr != result.end() && itr->worldFragment)

{

The worldFragment struct contains the location where the Ray hit the terrain in the singleIntersection

variable (which is a Vector3). We are going to get the height of the terrain by assigning the y value of this

vector to a local variable. Once we have the height, we are going to see if the camera is below the height, and if

so we are going to move the camera up to that height. Note that we actually move the camera up by 10 units.

This ensures that we can't see through the Terrain by being too close to it.

Ogre::Real terrainHeight = itr->worldFragment->singleIntersection.y;

if ((terrainHeight + 10.0f) > camPos.y)

mCamera->setPosition( camPos.x, terrainHeight + 10.0f,

camPos.z );

}

return true;

Lastly, we return true to continue rendering. At this point you should compile and test your program.

Terrain Selection

In this section we will be creating and adding objects to the screen every time you click the left mouse button.

Every time you click and hold the left mouse button, an object will be created and "held" on your cursor. You

can move the object around until you let go of the button, at which point it will lock into place. To do this we are

going to need to change the mousePressed function to do something different when you click the left mouse

button. Find the following code in the ITutorial02::mousePressed function. We will be adding code

inside this if statement.

// Left mouse button down

if (id == OIS::MB_Left)

{

mLMouseDown = true;

} // if

10 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 23: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

The first piece of code will look very familiar. We will be creating a Ray to use with the mRaySceneQuery

object, and setting the Ray. Ogre provides us with Camera::getCameraToViewportRay; a nice function

that translates a click on the screen (x and y coordinates) into a Ray that can be used with a RaySceneQuery

object.

// Left mouse button down

if (id == OIS::MB_Left)

{

// Setup the ray scene query, use CEGUI's mouse position

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton

().getPosition();

Ogre::Ray mouseRay = mCamera->getCameraToViewportRay

(mousePos.d_x/float(arg.state.width), mousePos.d_y/float

(arg.state.height));

mRaySceneQuery->setRay(mouseRay);

Next we will execute the query and make sure it returned a result.

// Execute query

Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute

();

Ogre::RaySceneQueryResult::iterator itr = result.begin( );

// Get results, create a node/entity on the position

if (itr != result.end() && itr->worldFragment)

{

Now that we have the worldFragment (and therefore the position that was clicked on), we are going to create

the object and place it on that position. Our first difficulty is that each Entity and SceneNode in ogre needs a

unique name. To accomplish this we are going to name each Entity "Robot1", "Robot2", "Robot3"... and each

SceneNode "Robot1Node", "Robot2Node", "Robot3Node"... and so on. First we create the name (consult a

reference on C for more information on sprintf).

char name[16];

sprintf( name, "Robot%d", mCount++ );

Next we create the Entity and SceneNode. Note that we use itr->worldFragment-

>singleIntersection for our default position of the Robot. We also scale him down to 1/10th size because

of how small the terrain is. Be sure to take note that we are assigning this newly created object to the member

variable mCurrentObject. We will be using that in the next section.

Ogre::Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");

mCurrentObject = mSceneMgr->getRootSceneNode()-

>createChildSceneNode(std::string(name) + "Node", itr->worldFragment-

11 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 24: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

>singleIntersection);

mCurrentObject->attachObject(ent);

mCurrentObject->setScale(0.1f, 0.1f, 0.1f);

} // if

mLMouseDown = true;

} // if

Now compile and run the demo. You can now place Robots on the scene by clicking anywhere on the Terrain.

We have almost completed our program, but we need to implement object dragging before we are finished. We

will be adding code inside this if statement:

// If we are dragging the left mouse button.

if (mLMouseDown)

{

} // if

This next chunk of code should now be self explanatory. We create a Ray based on the mouse's current

location, then execute a RaySceneQuery and move the object to the new position. Note that we don't have to

check mCurrentObject to see if it's the latest object or not, because mLMouseDown and mCurrentObject

are both set in the mousePressed() function: mLMouseDown is set to true, and mCurrentObject is set to

the most recently created SceneNode.

if (mLMouseDown)

{

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton

().getPosition();

Ogre::Ray mouseRay = mCamera->getCameraToViewportRay

(mousePos.d_x/float(arg.state.width),mousePos.d_y/float(arg.state.height));

mRaySceneQuery->setRay(mouseRay);

Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();

Ogre::RaySceneQueryResult::iterator itr = result.begin();

if (itr != result.end() && itr->worldFragment)

mCurrentObject->setPosition(itr->worldFragment-

>singleIntersection);

} // if

Compile and run the program. We are now finished!

Note: You (= the Ray's origin) must be over the Terrain for the RaySceneQuery to report the intersection

when using the TerrainSceneManager.

Note: If you are using your own framework, make sure your scene query has access to the frame listener,

e.g. your frameStarted() method. Otherwise, if you use it in an init() function you may get no results.

12 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 25: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Exercises for Further Study

Easy Exercises

1. To keep the camera from looking through the terrain, we chose 10 units above the Terrain. This

selection was arbitrary. Could we improve on this number and get closer to the Terrain without going

through it? If so, make this variable a static class member and assign it there.

2. We sometimes do want to pass through the terrain, especially in a SceneEditor. Create a flag which

turns toggles collision detection on and off, and bind this to a key on the keyboard. Be sure you do not

make a SceneQuery in frameStarted if collision detection is turned off.

Intermediate Exercises

1. We are currently doing the SceneQuery every frame, regardless of whether or not the camera has

actually moved. Fix this problem and only do a SceneQuery if the camera has moved. (Hint: Find the

translation vector in ExampleFrameListener, after the function is called test it against Vector3::ZERO.)

Advanced Exercises

1. Notice that there is a lot of code duplication every time we make a scene query call. Wrap all of the

SceneQuery related functionality into a protected function. Be sure to handle the case where the Terrain

is not intersected at all.

Exercises for Further Study

1. In this tutorial we used RaySceneQueries to place objects on the Terrain. We could have used it for

many other purposes. Take the code from Tutorial 1 and complete Difficult Question 1 and Expert

Question 1. Then merge that code with this one so that the Robot now walks on the terrain instead of

empty space.

2. Add code so that every time you click on a point on the scene, the robot moves to that location.

Proceed to Intermediate Tutorial 3 Mouse Picking (3D Object Selection) and SceneQuery Masks

Alias: Intermediate_Tutorial_2

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

13 de 17Página Intermediate Tutorial 2e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=274&page=Intermediate...

Page 26: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Mouse Picking (3D Object Selection) and SceneQuery Masks (Part 2 of 2)

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Introduction

In this tutorial we will continue the work on the previous tutorial. We will be covering how to select any object on

the screen using the mouse, and how to restrict what is selectable.

You can find the code for this tutorial here. As you go through the demo you should be slowly adding code to

your own project and watching the results as we build it.

NOTE: If you want to try the tutorial without using CEGUI you can find the source code using the built in

SdkTrays here.

Table of contents

Introduction

Prerequisites

Getting Started

Showing Which Object is Selected

Adding Ninjas

Selecting Objects

Query Masks

Query Type Masks

More on Masks

Setting a MovableObject's Mask

Querying for Multiple Masks

Querying for Everything but a Mask

Selecting all Objects or No Objects

Exercises

Easy Exercises

Intermediate Exercises

Advanced Exercises

Exercises for Further Study

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Prerequisites

This tutorial will assume that you have gone through the previous tutorial using the Ogre Tutorial Framework.

We will also be using STL iterators to go through multiple results of SceneQueries, so basic knowledge of them

will be helpful.

1 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 27: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Getting Started

Now if you want to follow this tutorial completely, or you just copy and paste, make sure your .h file looks

something like this:

#ifndef __IntermediateTutorial3_h_

#define __IntermediateTutorial3_h_

#include "BaseApplication.h"

#include <CEGUISystem.h>

#include <CEGUISchemeManager.h>

#include <RendererModules/Ogre/CEGUIOgreRenderer.h>

class IntermediateTutorial3 : public BaseApplication

{

public:

IntermediateTutorial3(void);

virtual ~IntermediateTutorial3(void);

protected:

virtual void createScene(void);

virtual void chooseSceneManager(void);

virtual void createFrameListener(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent& arg);

virtual bool mouseMoved(const OIS::MouseEvent& arg);

virtual bool mousePressed(const OIS::MouseEvent& arg,

OIS::MouseButtonID id);

virtual bool mouseReleased(const OIS::MouseEvent& arg,

OIS::MouseButtonID id);

Ogre::SceneNode *mCurrentObject; //pointer to our currently

selected object

Ogre::RaySceneQuery* mRayScnQuery; //pointer to our ray scene

query

CEGUI::Renderer* mGUIRenderer; //our CEGUI renderer

bool bLMouseDown, bRMouseDown; //true if mouse buttons are held

down

int mCount; //number

of objects created

float mRotateSpeed; //the rotation

speed for the camera

};

#endif // #ifndef __IntermediateTutorial3_h_

2 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 28: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

You should also make sure IntermediateTutorial3.cpp looks like this. If you went through the previous tutorial

and are continuing on from it, your code should already look something like this. If it doesn't, make sure that it

does and that it compiles and runs successfully. The only thing different should be the comments and some of

the variable initialization has been moved to the constructor.

#include "IntermediateTutorial3.h"

//-------------------------------------------------------------------------

------------

IntermediateTutorial3::IntermediateTutorial3(void):

mCount(0),

mCurrentObject(0),

bLMouseDown(false),

bRMouseDown(false),

mRotateSpeed(0.1f)

{

}

//-------------------------------------------------------------------------

------------

IntermediateTutorial3::~IntermediateTutorial3(void)

{

mSceneMgr->destroyQuery(mRayScnQuery);

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial3::createScene(void)

{

//Scene setup

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));

mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

//World geometry

mSceneMgr->setWorldGeometry("terrain.cfg");

//Camera setup

mCamera->setPosition(40, 100, 580);

mCamera->pitch(Ogre::Degree(-30));

mCamera->yaw(Ogre::Degree(-45));

//CEGUI setup

mGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem();

//show the CEGUI cursor

CEGUI::SchemeManager::getSingleton().create

((CEGUI::utf8*)"TaharezLook.scheme");

CEGUI::MouseCursor::getSingleton().setImage("TaharezLook",

"MouseArrow");

}

3 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 29: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

void IntermediateTutorial3::chooseSceneManager(void)

{

//create a scene manager that is meant for handling outdoor scenes

mSceneMgr = mRoot->createSceneManager(Ogre::ST_EXTERIOR_CLOSE);

}

void IntermediateTutorial3::createFrameListener(void)

{

//we still want to create the frame listener from the base app

BaseApplication::createFrameListener();

//but we also want to set up our raySceneQuery after everything

has been initialized

mRayScnQuery = mSceneMgr->createRayQuery(Ogre::Ray());

}

bool IntermediateTutorial3::frameRenderingQueued(const Ogre::FrameEvent&

arg)

{

//we want to run everything in the previous frameRenderingQueued

call

//but we also want to do something afterwards, so lets start off

with this

if(!BaseApplication::frameRenderingQueued(arg))

{

return false;

}

/*

This next big chunk basically sends a raycast straight down from

the camera's position

It then checks to see if it is under world geometry and if it is

we move the camera back up

*/

Ogre::Vector3 camPos = mCamera->getPosition();

Ogre::Ray cameraRay(Ogre::Vector3(camPos.x, 5000.0f, camPos.z),

Ogre::Vector3::NEGATIVE_UNIT_Y);

mRayScnQuery->setRay(cameraRay);

Ogre::RaySceneQueryResult& result = mRayScnQuery->execute();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

if(iter != result.end() && iter->worldFragment)

{

Ogre::Real terrainHeight = iter->worldFragment-

>singleIntersection.y;

if((terrainHeight + 10.0f) > camPos.y)

{

4 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 30: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

mCamera->setPosition(camPos.x, terrainHeight +

10.0f, camPos.z);

}

}

return true;

}

bool IntermediateTutorial3::mouseMoved(const OIS::MouseEvent& arg)

{

//updates CEGUI with mouse movement

CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel,

arg.state.Y.rel);

//if the left mouse button is held down

if(bLMouseDown)

{

//find the current mouse position

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton

().getPosition();

//create a raycast straight out from the camera at the

mouse's location

Ogre::Ray mouseRay = mCamera->getCameraToViewportRay

(mousePos.d_x/float(arg.state.width), mousePos.d_y/float

(arg.state.height));

mRayScnQuery->setRay(mouseRay);

Ogre::RaySceneQueryResult& result = mRayScnQuery->execute

();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

//check to see if the mouse is pointing at the world and

put our current object at that location

if(iter != result.end() && iter->worldFragment)

{

mCurrentObject->setPosition(iter->worldFragment-

>singleIntersection);

}

}

else if(bRMouseDown) //if the right mouse button is held down,

be rotate the camera with the mouse

{

mCamera->yaw(Ogre::Degree(-arg.state.X.rel *

mRotateSpeed));

mCamera->pitch(Ogre::Degree(-arg.state.Y.rel *

mRotateSpeed));

}

return true;

}

5 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 31: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

bool IntermediateTutorial3::mousePressed(const OIS::MouseEvent& arg,

OIS::MouseButtonID id)

{

if(id == OIS::MB_Left)

{

//find the current mouse position

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton

().getPosition();

//then send a raycast straight out from the camera at the

mouse's position

Ogre::Ray mouseRay = mCamera->getCameraToViewportRay

(mousePos.d_x/float(arg.state.width), mousePos.d_y/float

(arg.state.height));

mRayScnQuery->setRay(mouseRay);

/*

This next chunk finds the results of the raycast

If the mouse is pointing at world geometry we spawn a

robot at that position

*/

Ogre::RaySceneQueryResult& result = mRayScnQuery->execute

();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

if(iter != result.end() && iter->worldFragment)

{

char name[16];

sprintf(name, "Robot%d", mCount++);

Ogre::Entity* ent = mSceneMgr->createEntity(name,

"robot.mesh");

mCurrentObject = mSceneMgr->getRootSceneNode()-

>createChildSceneNode(std::string(name) + "Node", iter->worldFragment-

>singleIntersection);

mCurrentObject->attachObject(ent);

mCurrentObject->setScale(0.1f, 0.1f, 0.1f);

}

bLMouseDown = true;

}

else if(id == OIS::MB_Right) // if the right mouse button is

held we hide the mouse cursor for view mode

{

CEGUI::MouseCursor::getSingleton().hide();

bRMouseDown = true;

}

return true;

}

6 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 32: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

bool IntermediateTutorial3::mouseReleased(const OIS::MouseEvent& arg,

OIS::MouseButtonID id)

{

if(id == OIS::MB_Left)

{

bLMouseDown = false;

}

else if(id == OIS::MB_Right) //when the right mouse is released

we then unhide the cursor

{

CEGUI::MouseCursor::getSingleton().show();

bRMouseDown = false;

}

return true;

}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine,

INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

IntermediateTutorial3 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(),

"An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() <<

std::endl;

#endif

}

return 0;

7 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 33: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

}

#ifdef __cplusplus

}

#endif

Be sure you can compile and run this code before continuing. It should work the exact same as in the last

tutorial.

Showing Which Object is Selected

In this tutorial we will be making it so that you can "pick up" and move objects after you have placed them. We

would like to allow the user to know which object is currently being manipulated. In a game, we would probably

like to create a special way of highlighting the object, but for our tutorial (and for your applications before they

are release-ready), you can use the showBoundingBox method to create a box around objects.

Our basic idea is to disable the bounding box on the old current object when the mouse is first clicked, then

enable the bounding box as soon as we have the new object. To do this, we will add the following code to the

beginning of the mousePressed() function, inside the first if statement:

//show that the current object has been deselected by removing the bounding

box visual

if(mCurrentObject)

{

mCurrentObject->showBoundingBox(false);

}

Then add the following code near the end of the mousePressed() function, just before the return statement:

//now we show the bounding box so the user can see that this object is

selected

if(mCurrentObject)

{

mCurrentObject->showBoundingBox(true);

}

Now the mCurrentObject is always highlighted on the screen.

Adding Ninjas

We now want to modify the code to not just support robots, but also placing and moving ninjas. We will have a

"Robot Mode" and a "Ninja Mode" that will determine which object we are placing on the screen. Let's make the

space key be our mode switching button.

First, we will set up the IntermediateTutorial to be in Robot Mode from the beginning. We need to add a

variable to hold the state of the object (that is, if we are placing robots or ninjas). Go to the protected section of

IntermediateTutorial and add this variable:

bool bRobotMode; // The current state

Now add this code to the into the IntermediateTutorial3 constructor:

8 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 34: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

// Set the default state

bRobotMode = true;

This puts us in Robot Mode! If only it were that simple. Now we need to create either a Robot or a Ninja mesh

based on the bRobotMode variable. Locate this code in mousePressed:

char name[16];

sprintf(name, "Robot%d", mCount++);

Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");

The replacement should be straight forward. Depending on the bRobotMode state we either put out a Robot or

a Ninja, and name it accordingly:

Entity *ent;

char name[16];

if (bRobotMode)

{

sprintf(name, "Robot%d", mCount++);

ent = mSceneMgr->createEntity(name, "robot.mesh");

} // if

else

{

sprintf(name, "Ninja%d", mCount++);

ent = mSceneMgr->createEntity(name, "ninja.mesh");

} // else

Now we are almost done. The only thing left to do is to bind the spacebar to change states. Go back to

IntermediateTutorial3.h and add an override to the keyPressed function like this:

virtual bool keyPressed(const OIS::KeyEvent& arg);

Now in IntermediateTutorial3.cpp let's fill in the function. We need to add functionality for the space bar, but we

also want to keep the functionality of the BaseApplication. So how do we do this? Easy, it's actually only a few

lines:

bool IntermediateTutorial3::keyPressed(const OIS::KeyEvent& arg)

{

//check and see if the spacebar was hit, and this will switch

which mesh is spawned

if(arg.key == OIS::KC_SPACE)

{

bRobotMode = !bRobotMode;

}

//then we return the base app keyPressed function so that we get

9 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 35: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

all of the functionality

//and the return value in one line

return BaseApplication::keyPressed(arg);

}

You see, we check for the space bar and switch the mode we are in. We also call the BaseApplication's

keyPressed() function within the return statement, that way we check everything programmed before, plus

we get its return value.

Now we are done! Compile and run the demo. You can now choose which object you place by using the space

bar.

Selecting Objects

Now we are going to dive into the meat of this tutorial: using RaySceneQueries to select objects on the screen.

Before we start making changes to the code I will first explain a RaySceneQueryResultEntry in more detail.

(Please follow the link and briefly look at the struct.)

The RaySceneQueryResult returns an iterator of RaySceneQueryResultEntry structs. This struct contains three

variables. The distance variable tells you how far away the object is along the ray. One of the other two

variables will be non-null. The movable variable will contain a MovableObject if the Ray intersected one.

The worldFragment will contain a WorldFragment object if it hit a world fragment (like the terrain).

MovableObjects are basically any object you would attach to a SceneNode (such as Entities, Lights, etc). See

the inheritance tree on this page to find out what type of objects would be returned. Most normal

applications of RaySceneQueries will involve selecting and manipulating either the MovableObject you have

clicked on, or the SceneNodes they are attached to. To get the name of the MovableObject, call the getName

method. To get the SceneNode (or Node) the object is attached to, call getParentSceneNode (or

getParentNode). The movable variable in a RaySceneQueryResultEntry will be equal to NULL if the result is

not a MovableObject.

The WorldFragment is a different beast all together. When the worldFragment member of a

RaySceneQueryResult is set, it means that the result is part of the world geometry created by the

SceneManager. The type of world fragment that is returned is based on the SceneManager. The way this is

implemented is that the WorldFragment struct contains the fragmentType variable which specifies the type

of world fragment it contains. Based on the fragmentType variable, one of the other variables will be set

(singleIntersection, planes, geometry, or renderOp ). Generally speaking, RaySceneQueries only return

WFT_SINGLE_INTERSECTION WorldFragments. The singleIntersection variable is simply a Vector3 reporting

the location of the intersection. Other types of world fragments are beyond the scope of this tutorial.

Now let's look at an example. Let's say we wanted to print out the results of a RaySceneQuery. The following

code would do this. (Assume that the fout object is of type ofstream, and that it has already been created

using the open() method.)

// Do not add this code to the program, just read along:

RaySceneQueryResult &result = mRayScnQuery->execute();

RaySceneQueryResult::iterator itr;

// loop through the results

for ( itr = result.begin( ); itr != result.end(); itr++ )

{

// Is this result a WorldFragment?

10 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 36: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

if ( itr->worldFragment )

{

Vector3 location = itr->worldFragment->singleIntersection;

fout << "WorldFragment: (" << location.x << ", " << location.y <<

", " << location.z << ")" << endl;

} // if

// Is this result a MovableObject?

else if ( itr->movable )

{

fout << "MovableObject: " << itr->movable->getName() << endl;

} // else if

} // for

This would print out the names of all MovableObjects that the ray intersects, and it would print the location of

where it intersected the world geometry (if it did hit it). Note that this can sometimes act in strange ways. For

example, if you are using the TerrainSceneManager, the origin of the Ray you fire must be over the Terrain or

the intersection query will not register it as a hit. Different scene managers implement RaySceneQueries in

different ways. Be sure to experiment with it when you use it with a new SceneManager.

Now, if we look back at our RaySceneQuery code, something should jump out at you: we are not looping

through all the results! In fact we are only looking at the first result, which (in the case of the

TerrainSceneManager) is the world geometry. This is bad, since we cannot be sure that the

TerrainSceneManager will always return the world geometry first. We need to loop through the results to make

sure we are finding what we are looking for. Another thing that we want to do is to "pick up" and drag objects

that have already been placed. Currently if you click on an object that has already been placed, the program

ignores it and places a robot behind it. Let's fix that now.

Go to the mousePressed function in the code. We first want to make sure that when we click, we get the first

thing along the Ray. To do that, we need to set the RaySceneQuery to sort by depth. Find this code in the

mousePressed() function:

// Set up the ray scene query, use CEGUI's mouse position

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float

(arg.state.width), mousePos.d_y/float(arg.state.height));

mRayScnQuery->setRay(mouseRay);

// Execute query

RaySceneQueryResult &result = mRayScnQuery->execute();

RaySceneQueryResult::iterator itr = result.begin();

And change it to this:

// Set up the ray scene query

CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float

(arg.state.width), mousePos.d_y/float(arg.state.height));

mRayScnQuery->setRay(mouseRay);

mRayScnQuery->setSortByDistance(true);

11 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 37: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

// Execute query

RaySceneQueryResult &result = mRayScnQuery->execute();

RaySceneQueryResult::iterator iter = result.begin();

Now that we are returning the results in order, we need to update the query results code. We are going to

rewrite that section, so remove this code:

// Get results, create a node/entity on the position

if (iter != result.end() && iter->worldFragment)

{

Entity *ent;

char name[16];

if (bRobotMode)

{

sprintf(name, "Robot%d", mCount++);

ent = mSceneMgr->createEntity(name, "robot.mesh");

} // if

else

{

sprintf(name, "Ninja%d", mCount++);

ent = mSceneMgr->createEntity(name, "ninja.mesh");

} // else

mCurrentObject = mSceneMgr->getRootSceneNode()-

>createChildSceneNode(String(name) + "Node", iter->worldFragment-

>singleIntersection);

mCurrentObject->attachObject(ent);

mCurrentObject->setScale(0.1f, 0.1f, 0.1f);

} // if

We want to make it so that we can select objects that are already placed on the screen. Our strategy is going to

have two parts. First, if the user clicks on an object, then make mCurrentObject refer to its parent SceneNode.

If the user does not click on an object (if he clicks on the terrain instead), place a new Robot there like we did

before. The first change is that we will be using a for loop instead of an if statement:

// Get results, create a node/entity on the position

for ( iter; iter != result.end(); iter++ )

{

First we will check if the first intersection is a MovableObject, if so we'll assign mCurrentObject to be its parent

SceneNode. There is a catch though. The TerrainSceneManager creates MovableObjects for the terrain itself,

so we might actually be intersecting one of the tiles. In order to fix that, I check the name of the object to make

sure that it does not resemble a terrain tile name; a sample tile name would be "tile[0][0,2]" . Finally, notice the

break statement. We only need to act on the first object, so as soon as we find a valid one we need to get out

of the for loop altogether.

if (iter->movable && iter->movable->getName().substr(0, 5) != "tile[")

{

mCurrentObject = iter->movable->getParentSceneNode();

12 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 38: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

break;

} // if

Next, we will check to see if the intersection returned a WorldFragment.

else if (iter->worldFragment)

{

Entity *ent;

char name[16];

Now we are either going to create a Robot entity or a Ninja entity based on mRobotState. After creating the

entity we will create the SceneNode and break out of the for loop.

if (bRobotMode)

{

sprintf(name, "Robot%d", mCount++);

ent = mSceneMgr->createEntity(name, "robot.mesh");

} // if

else

{

sprintf(name, "Ninja%d", mCount++);

ent = mSceneMgr->createEntity(name, "ninja.mesh");

} // else

mCurrentObject = mSceneMgr->getRootSceneNode()-

>createChildSceneNode(String(name) + "Node", iter->worldFragment-

>singleIntersection);

mCurrentObject->attachObject(ent);

mCurrentObject->setScale(0.1f, 0.1f, 0.1f);

break;

} // else if

} // for

Believe it or not, that's all that has to be done! Compile and play with the code. Now we create the correct type

of object when we click on terrain, and when we click on an object we will see the bounding box (no dragging it

around right now, this comes in the next step). One valid question is: since we only want the first intersection,

and since we sorted by depth, why not just use an if statement? The main reason is to have a fall-through if the

first returned object is one of those pesky tiles. We have to loop until we find something other than a tile or we

hit the end of the list.

Now that we are on the subject, we need to also update the RaySceneQuery code in other locations. In both

the frameRenderingQueued and the mouseMoved functions we only need to find the Terrain. There is no

reason to sort the results because the terrain will be at the end of the sorted list (so we are going to turn sorting

off). We still want to loop through the results though, just in case the TerrainSceneManager is changed at a

future date to not return the terrain first. First, find this section of code in frameRenderingQueued:

// Perform the scene query

Ogre::RaySceneQueryResult &result = mRayScnQuery->execute();

Ogre::RaySceneQueryResult::iterator itr = result.begin();

13 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 39: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

// Get the results, set the camera height

if (iter != result.end() && iter->worldFragment)

{

Ogre::Real terrainHeight = iter->worldFragment-

>singleIntersection.y;

if ((terrainHeight + 10.0f) > camPos.y)

{

mCamera->setPosition(camPos.x, terrainHeight + 10.0f,

camPos.z);

}

}

Then replace it with this:

// Perform the scene query

mRayScnQuery->setSortByDistance(false);

Ogre::RaySceneQueryResult &result = mRayScnQuery->execute();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

// Get the results, set the camera height

for (iter; iter != result.end(); iter++)

{

if (iter->worldFragment)

{

Ogre::Real terrainHeight = iter->worldFragment-

>singleIntersection.y;

if ((terrainHeight + 10.0f) > camPos.y)

{

mCamera->setPosition(camPos.x, terrainHeight +

10.0f, camPos.z);

}

break;

} // if

} // for

This should be self explanatory. We add a line to turn off sorting, then we convert the if statement into a for

loop, and break as soon as we have found the position we are looking for. We will do the exact same thing with

the mouseMoved function. Find this section of code in mouseMoved():

mRayScnQuery->setRay(mouseRay);

Ogre::RaySceneQueryResult &result = mRayScnQuery->execute();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

if (iter != result.end() && iter->worldFragment)

{

mCurrentObject->setPosition(iter->worldFragment-

>singleIntersection);

}

14 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 40: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

And replace it with this code:

mRayScnQuery->setRay(mouseRay);

mRayScnQuery->setSortByDistance(false);

Ogre::RaySceneQueryResult &result = mRayScnQuery->execute();

Ogre::RaySceneQueryResult::iterator iter = result.begin();

for (iter; iter != result.end(); iter++)

{

if (iter->worldFragment)

{

mCurrentObject->setPosition(iter->worldFragment-

>singleIntersection);

break;

} // if

}

Compile and test the code. There shouldn't be any noticeable difference since the last time we ran the code,

but now we are doing it the correct way, and future updates to the TerrainSceneManager will not break our

code.

Query Masks

Notice that no matter what mode we are in we can select either object. Our RaySceneQuery will return either

Robots or Ninjas, whichever is in front. It doesn't have to be this way though. All MovableObjects allow you to

set a mask value for them, and SceneQueries allow you to filter your results based on this mask. All masks are

done using the binary AND operation, so if you are unfamiliar with this, you should brush up on it before

continuing.

The first thing we are going to do is create the mask values. Go to the very beginning of the

IntermediateTutorial3 class and add this after the public statement:

enum QueryFlags

{

NINJA_MASK = 1<<0,

ROBOT_MASK = 1<<1

};

This creates an enum with two values, which in binary are 0001 and 0010. Now, every time we create a Robot

entity, we call its "setMask" function to set the query flags to be ROBOT_MASK. Every time we create a Ninja

entity we call its "setMask" function and use NINJA_MASK instead. Now, when we are in Ninja mode, we will

make the RaySceneQuery only consider objects with the NINJA_MASK flag, and when we are in Robot mode

we will make it only consider ROBOT_MASK.

Find this section of mousePressed:

if (bRobotMode)

{

sprintf(name, "Robot%d", mCount++);

ent = mSceneMgr->createEntity(name, "robot.mesh");

15 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 41: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

} // if

else

{

sprintf(name, "Ninja%d", mCount++);

ent = mSceneMgr->createEntity(name, "ninja.mesh");

} // else

We will add two lines to set the mask on both of them:

if (bRobotMode)

{

sprintf(name, "Robot%d", mCount++);

ent = mSceneMgr->createEntity(name, "robot.mesh");

ent->setQueryFlags(ROBOT_MASK);

} // if

else

{

sprintf(name, "Ninja%d", mCount++);

ent = mSceneMgr->createEntity(name, "ninja.mesh");

ent->setQueryFlags(NINJA_MASK);

} // else

We still need to make it so that when we are in one mode, we can only click and drag objects of the

corresponding type. We need to set the query flags so that only the correct object type can be selected. We

accomplish this by setting the query mask in the RaySceneQuery to be the ROBOT_MASK in Robot mode, and

set it to NINJA_MASK in Ninja mode. Find this code in the mousePressed() function:

mRayScnQuery->setSortByDistance(true);

Add this line of code after it:

mRayScnQuery->setQueryMask(bRobotMode ? ROBOT_MASK : NINJA_MASK);

Compile and run the tutorial. We now select only the objects we are looking for. All rays that pass through other

objects go through them and hit the correct object. We are now finished working on this code. The next section

will not be modifying it.

Query Type Masks

There's one more thing to consider when using scene queries. Suppose you added a billboardset or a particle

system to your scene above, and you want to move it around. You will find that the query never returns the

billboardset that you click on. This is because the SceneQuery has another mask, the QueryTypeMask, that

limits you to selecting only the type specified as the flag. By default when you do a query, it returns only objects

of entity type.

In your code, if you want your query to return BillboardSets or ParticleSystems, you'll have to do this first before

executing your query:

mRayScnQuery->setQueryTypeMask(SceneManager::FX_TYPE_MASK);

16 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 42: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Now the query will only return BillboardSets or ParticleSystems as results.

There are 6 types of QueryTypeMask defined in the SceneManager class as static members:

WORLD_GEOMETRY_TYPE_MASK //Returns world geometry.

ENTITY_TYPE_MASK //Returns entities.

FX_TYPE_MASK //Returns billboardsets / particle systems.

STATICGEOMETRY_TYPE_MASK //Returns static geometry.

LIGHT_TYPE_MASK //Returns lights.

USER_TYPE_MASK_LIMIT //User type mask limit.

The default QueryTypeMask when the property is not set manually is ENTITY_TYPE_MASK.

More on Masks

Our mask example is very simple, so I would like to go through a few more complex examples.

Setting a MovableObject's Mask

Every time we want to create a new mask, the binary representation must contain only one 1 in it. That is, these

are valid masks:

00000001

00000010

00000100

00001000

00010000

00100000

01000000

10000000

And so on. We can very easily create these values by taking 1 and bitshifting them by a position value. That is:

00000001 = 1<<0

00000010 = 1<<1

00000100 = 1<<2

00001000 = 1<<3

00010000 = 1<<4

00100000 = 1<<5

01000000 = 1<<6

10000000 = 1<<7

All the way up to 1<<31. This gives us 32 distinct masks we can use for MovableObjects.

Querying for Multiple Masks

We can query for multiple masks by using the bitwise OR operator. Let's say we have three different groups of

objects in a game:

enum QueryFlags

{

17 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 43: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

FRIENDLY_CHARACTERS = 1<<0,

ENEMY_CHARACTERS = 1<<1,

STATIONARY_OBJECTS = 1<<2

};

Now, if we wanted to query for only friendly characters we could do:

mRayScnQuery->setQueryMask(FRIENDLY_CHARACTERS);

If we want the query to return both enemy characters and stationary objects, we would use:

mRayScnQuery->setQueryMask(ENEMY_CHARACTERS | STATIONARY_OBJECTS);

If you use a lot of these types of queries, you might want to define this in the enum:

OBJECTS_ENEMIES = ENEMY_CHARACTERS | STATIONARY_OBJECTS

And then simply use OBJECTS_ENEMIES to query.

Querying for Everything but a Mask

You can also query for anything other than a mask using the bit inversion operator, like so:

mRayScnQuery->setQueryMask(~FRIENDLY_CHARACTERS);

Which will return everything other than friendly characters. You can also do this for multiple masks:

mRayScnQuery->setQueryMask(~(FRIENDLY_CHARACTERS | STATIONARY_OBJECTS));

Which would return everything other than friendly characters and stationary objects.

Selecting all Objects or No Objects

You can do some very interesting stuff with masks. The thing to remember is, if you set the query mask QM for

a SceneQuery, it will match all MovableObjects that have the mask OM if (QM & OM) contains at least one 1.

Thus, setting the query mask for a SceneQuery to 0 will make it return no MovableObjects. Setting the query

mask to ~0 (0xFFFFF...) will make it return all MovableObjects that do not have a 0 query mask.

Using a query mask of 0 can be highly useful in some situations. For example, the TerrainSceneManager does

not use QueryMasks when it returns a worldFragment. By doing this:

mRayScnQuery->setQueryMask(0);

You will get ONLY the worldFragment in your RaySceneQueries for that SceneManager. This can be very

useful if you have a lot of objects on screen and you do not want to waste time looping through all of them if

you only need to look for the Terrain intersection.

18 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 44: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Exercises

Easy Exercises

1. The TerrainSceneManager creates tiles with a default mask of ~0 (all queries select it). We fixed this

problem by testing to see if the name of the movable object equaled "tile00,2". Even though it's not

implemented yet, the TerrainSceneManager supports multiple pages, and if there were more things than

just "tile00,2" this would cause our code to break down. Instead of making the test in the loop, fix the

problem properly by setting all of the tile objects created by the TerrainSceneManager to have a unique

mask. (Hint: The TerrainSceneManager creates a SceneNode called "Terrain" which contains all of

these tiles. Loop through them and set the attached object's masks to something of your choosing.)

Intermediate Exercises

1. Our program delt with two things, Robots and Ninjas. If we were going to implement a scene editor, we

would want to place any number of different object types. Generalize this code to allow the placement of

any type of object from a predefined list. Create an overlay with the list of objects you want the editor to

have (such as Ninjas, Robots, Knots, Ships, etc), and have the SceneQueries only select that type of

object.

2. Since we are using multiple types of objects now, use the Factory Pattern to properly create the

SceneNodes and Entities.

Advanced Exercises

1. Generalize the previous exercises to read in all of the meshes that Ogre knows about (IE everything that

was parsed in from the Media directory), and give the ability to place them. Note that there should not be

a limit as to how many types of objects ogre can place. Since you only have 32 unique query masks to

use, you may need to come up with a way to quickly change all of the query flags for objects on the

screen.

2. You might have noticed that when you click on an object, the object is "lifted" from the bottom of the

bounding box. To see this, click on the top of any character and move him. He will be transported

instantly elsewhere. Modify the program to fix this problem.

Exercises for Further Study

1. Add a way to select multiple objects to the program such that when you hold the Ctrl key and click

multiple objects are highlighted. When you move these objects, move all of them as a group.

2. Many scene editing programs allow you to group objects so that they are always moved together.

Implement this in the program.

Proceed to Intermediate Tutorial 4 Volume Selection and Basic Manual Objects

Alias: Intermediate_Tutorial_3

19 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 45: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR

COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE

BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED

HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

• "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the

Work in its entirety in unmodified form, along with a number of other contributions, constituting separate

and independent works in themselves, are assembled into a collective whole. A work that constitutes a

Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this

License.

• "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works,

such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound

recording, art reproduction, abridgment, condensation, or any other form in which the Work may be

recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be

considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the

Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with

a moving image ("synching") will be considered a Derivative Work for the purpose of this License.

• "Licensor" means the individual or entity that offers the Work under the terms of this License.

• "Original Author" means the individual or entity who created the Work.

• "Work" means the copyrightable work of authorship offered under the terms of this License.

• "You" means an individual or entity exercising rights under this License who has not previously violated

the terms of this License with respect to the Work, or who has received express permission from the

Licensor to exercise rights under this License despite a previous violation.

• "License Elements" means the following high-level license attributes as selected by Licensor and

indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other

limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-

exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as

stated below:

• to reproduce the Work, to incorporate the Work into one or more Collective Works, and to reproduce the

Work as incorporated in the Collective Works;

• to create and reproduce Derivative Works;

• to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means

of a digital audio transmission the Work including as incorporated in Collective Works;

• to distribute copies or phonorecords of, display publicly, perform publicly, and perform publicly by means

of a digital audio transmission Derivative Works.

• For the avoidance of doubt, where the work is a musical composition:

20 de 23Página Intermediate Tutorial 3e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=275&page=Intermediate...

Page 46: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Volume Selection and Basic Manual Objects

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Table of contents

Introduction

Prerequisites

ManualObjects

A Crash Course in 3D Objects

Introduction

Selection Box

The Code

Volume Selection

Setup

Mouse Handlers

PlaneBoundedVolumeListSceneQuery

A Final Note About Bounding Boxes

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Introduction

In this tutorial we will be covering how to do volume selection. The idea is that when you click and drag the

mouse across the screen, a white rectangle will trace the area you are selecting. When you let go of the

mouse, all objects within the selection area will be highlighted. In order to accomplish this we will be learning

how to use two objects: ManualObject (to create the rectangle) and PlaneBoundedVolumeListSceneQuery.

Note that while we will cover some basic uses of ManualObject, this is only an introduction to it, and not a

tutorial on how to completely create 3D objects with it. We will only cover what we need to use.

You can find the code for this tutorial here. As you go through the tutorial you should be slowly adding code to

your own project and watching the results as we build it.

Prerequisites

Make sure you have the Basic Tutorial Framework, which you should at this point. Then make sure that your

application header and cpp file look like this:

IntermediateTutorial4.h

#ifndef __IntermediateTutorial4_h_

#define __IntermediateTutorial4_h_

#include "BaseApplication.h"

1 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 47: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#include <CEGUI.h>

#include <CEGUISchemeManager.h>

#include <RendererModules/Ogre/CEGUIOgreRenderer.h>

class IntermediateTutorial4 : public BaseApplication

{

public:

IntermediateTutorial4(void);

virtual ~IntermediateTutorial4(void);

protected:

virtual void createScene(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

virtual bool mouseMoved(const OIS::MouseEvent& arg);

virtual bool mousePressed(const OIS::MouseEvent& arg,

OIS::MouseButtonID id);

virtual bool mouseReleased(const OIS::MouseEvent& arg,

OIS::MouseButtonID id);

void performSelection(const Ogre::Vector2& first, const

Ogre::Vector2& second);

void deselectObjects();

void selectObject(Ogre::MovableObject* obj);

private:

Ogre::Vector2 mStart, mStop;

Ogre::PlaneBoundedVolumeListSceneQuery* mVolQuery;

std::list<Ogre::MovableObject*> mSelected;

bool mSelecting;

CEGUI::OgreRenderer* mGUIRenderer;

static void swap(float& x, float& y);

};

#endif // #ifndef __IntermediateTutorial4_h_

IntermediateTutorial.cpp

#include "IntermediateTutorial4.h"

//-------------------------------------------------------------------------

------------

IntermediateTutorial4::IntermediateTutorial4(void)

{

}

//-------------------------------------------------------------------------

------------

IntermediateTutorial4::~IntermediateTutorial4(void)

2 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 48: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial4::createScene(void)

{

mSceneMgr->setAmbientLight(Ogre::ColourValue(1.0f, 1.0f, 1.0f));

for(int i = 0; i < 10; ++i)

{

for(int j = 0; j < 10; ++j)

{

Ogre::Entity* ent = mSceneMgr->createEntity

("Robot" + Ogre::StringConverter::toString(i+j*10), "robot.mesh");

Ogre::SceneNode* node = mSceneMgr->getRootSceneNode

()->createChildSceneNode(Ogre::Vector3(i * 15, 0, j * 15));

node->attachObject(ent);

node->setScale(0.1f, 0.1f, 0.1f);

}

}

mCamera->setPosition(-60, 100, -60);

mCamera->lookAt(60, 0, 60);

mGUIRenderer = &CEGUI::OgreRenderer::bootstrapSystem();

CEGUI::SchemeManager::getSingleton().create

((CEGUI::utf8*)"TaharezLook.scheme");

CEGUI::MouseCursor::getSingleton().setImage("TaharezLook",

"MouseArrow");

}

//-------------------------------------------------------------------------

------------

bool IntermediateTutorial4::frameRenderingQueued(const Ogre::FrameEvent&

evt)

{

return BaseApplication::frameRenderingQueued(evt);

}

//-------------------------------------------------------------------------

------------

bool IntermediateTutorial4::mouseMoved(const OIS::MouseEvent& arg)

{

CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel,

arg.state.Y.rel);

return true;

}

//-------------------------------------------------------------------------

3 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 49: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

------------

bool IntermediateTutorial4::mousePressed(const OIS::MouseEvent& arg,

OIS::MouseButtonID id)

{

return true;

}

//-------------------------------------------------------------------------

------------

bool IntermediateTutorial4::mouseReleased(const OIS::MouseEvent& arg,

OIS::MouseButtonID id)

{

return true;

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial4::performSelection(const Ogre::Vector2& first,

const Ogre::Vector2& second)

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial4::deselectObjects()

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial4::selectObject(Ogre::MovableObject* obj)

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial4::swap(float& x, float& y)

{

float temp = x;

x = y;

y = temp;

}

//-------------------------------------------------------------------------

------------

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

4 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 50: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine,

INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

IntermediateTutorial4 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(),

"An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() <<

std::endl;

#endif

}

return 0;

}

#ifdef __cplusplus

}

#endif

Be sure this code compiles before continuing. When you run it, you should be able to move the mouse cursor,

but the application does nothing else at this point. Press Escape to exit.

ManualObjects

A Crash Course in 3D Objects

Before we start diving directly into making a mesh, it would probably be useful to talk about what a mesh is, and

what it is made of. Though this is a gross oversimplification, a mesh consists of roughly two parts: the vertex

buffer and the index buffer.

Vertex buffers define points in 3D space. Each element in the vertex buffer is defined by several attributes you

can set. The only attribute you must set is the position of the vertex. Aside from that, there are many optional

5 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 51: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

properties you can set, such as the color of the vertex, the texture coordinates, and so on. Which ones you will

actually need to use is dependent on what you are trying to do with the mesh.

Index buffers "connect the dots" by selecting points from the vertex buffer. Every three indexes specified in the

index buffer defines a single triangle to be drawn by the GPU. The order in which you select vertices in the

index buffer tells the graphics card which way the triangle faces. A triangle which is drawn counter-clockwise is

facing you, one drawn clockwise is facing away from you. Normally only the front of a triangle is rendered, so it

is important to be sure that your triangles are set up properly.

Though all meshes have a vertex buffer, not all meshes will have an index buffer. For example, the mesh we

are about to create will not have an index buffer since we want to create an empty rectangle (as opposed to a

filled rectangle). Lastly, note that vertex and index buffers are usually stored in the video card's own memory,

so your software can just send the card one simple, discrete set of commands to tell it to use those predefined

buffers to render an entire 3D mesh in one go.

Introduction

There are two ways to create your own mesh within Ogre. The first way is to subclass the SimpleRenderable

object and provide it with the vertex and index buffers directly. This is the most direct way to create one, but

it's also the most cryptic. The Generating A Mesh code snippet shows an example of this. To make things

easier, Ogre provides a much nicer interface called ManualObject, which allows you to use some simple

functions to define a mesh instead of writing raw data to the buffer objects. Instead of dropping the position,

color, and so on into a buffer, you simply call the "position" and "colour" functions.

In this tutorial we need to create a white rectangle to display when we are dragging the mouse to select objects.

There really isn't a class in Ogre we could use to display a 2D rectangle. We will have to come up with a way of

doing it on our own. We could use an Overlay and resize it to display the selection rectangle, but the problem

with doing it this way is that the image you use for the selection rectangle could get stretched out of shape and

look awkward. Instead, we will generate a very simple 2D mesh to act as our selection rectangle.

Selection Box

Now, we can handle all of the selection box functionality within our tutorial application, but that is just going to

add clutter that doesn't need to be there. It is very good practice to keep separate objects in separate classes,

because it makes things neater and easier to use. So basically for the selection box we are going to make a

separate header and cpp file called SelectionBox.h and SelectionBox.cpp respectively, and we are just going to

declare this class just like any other class except we are going to extend from Ogre::ManualObject. So make

your header and cpp look something like this.

SelectionBox.h

#ifndef __SelectionBox_h_

#define __SelectionBox_h_

#include "OgreManualObject.h"

class SelectionBox : public Ogre::ManualObject

{

public :

SelectionBox(const Ogre::String& name);

~SelectionBox(void);

void setCorners(float left, float top, float right, float bottom);

6 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 52: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

void setCorners(const Ogre::Vector2& topLeft, const Ogre::Vector2&

bottomRight);

};

#endif

SelectionBox.cpp

#include "SelectionBox.h"

SelectionBox::SelectionBox(const Ogre::String& name): Ogre::ManualObject

(name)

{

}

SelectionBox::~SelectionBox()

{

}

void SelectionBox::setCorners(float left, float top, float right, float

bottom)

{

}

void SelectionBox::setCorners(const Ogre::Vector2& topLeft, const

Ogre::Vector2& bottomRight)

{

setCorners(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);

}

The Code

When we create the selection rectangle, we have to create it such that it will render in 2D. We also have to be

sure that it will render when Ogre's Overlays render so that it sits on top of all other objects on screen. Doing

this is actually very easy. Find the SelectionBox's constructor and add the following code:

setRenderQueueGroup(Ogre::RENDER_QUEUE_OVERLAY); // when using this, ensure

Depth Check is Off in the material

setUseIdentityProjection(true);

setUseIdentityView(true);

setQueryFlags(0);

The first function sets the render queue for the object to be the Overlay queue. The next two functions set the

projection and view matrices to be the identity. Projection and view matrices are used by many rendering

systems (such as OpenGL and DirectX) to define where objects go in the world. Since Ogre abstracts this away

7 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 53: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

for us, we won't go into detail about what these matrices actually are or what they do. Instead what you need to

know is that if you set the projection and view matrix to be the identity, as we have here, we will basically create

a 2D object. When defining this object, the coordinate system changes a bit. We no longer deal with the Z axis

(if you are asked for the Z axis, set the value to -1). Instead we have a new coordinate system with X and Y

running from -1 to 1 inclusive. Lastly, we will set the query flags for the object to be 0, which will prevent the

selection rectangle from being included in the query results.

Now that the object is set up, we need to actually build the rectangle. We have one small snag before we get

started. We are going to be calling this function with mouse locations. That is, we will be given a number

between 0 and 1 for the x and y coordinates, yet we need to convert these to numbers in the range -1, 1. To

make matters slightly more complicated, the y coordinate is backwards too. The mouse cursor in CEGUI

defines the top of the screen at 0, the bottom at 1. In our new coordinate system, the top of the screen is +1,

the bottom is -1. Thankfully, a few quick conversions will take care of this problem. Find the setCorners()

function and add the following:

left = left * 2 - 1;

right = right * 2 - 1;

top = 1 - top * 2;

bottom = 1 - bottom * 2;

Now the positions are in the new coordinate system. Next we need to actually build the object. To do this, we

first call the begin() method. It takes in two parameters, the name of the material to use for this section of the

object, and the render operation to be used to draw it. Since we are not putting a texture on this, we will leave

the material blank. The second parameter is the RenderOperation. We can render the mesh using points, lines,

or triangles. We would use triangles if we were rendering a full mesh, but since we want an empty rectangle,

we will use the line strip. The line strip draws a line to each vertex from the previous vertex you defined. So to

create our rectangle, we will define five points (the first and the last point are the same to connect the entire

rectangle). Add the following to setCorners():

clear();

begin("", Ogre::RenderOperation::OT_LINE_STRIP);

position(left, top, -1);

position(right, top, -1);

position(right, bottom, -1);

position(left, bottom, -1);

position(left, top, -1);

end();

Note that since we will be calling this many times, we have added the clear() call at the beginning to remove the

previous rectangle before redrawing it. When defining a manual object, you may call begin/end multiple times to

create multiple sub-meshes (which can have different materials/RenderOperations). Note we have also set the

Z parameter to be -1, since we are trying to define a 2D object which will not use that axis. Setting it to be -1 will

ensure that we are not on top of, or behind, the camera when rendering.

The last thing we need to do is set the bounding box for this object. Many SceneManagers cull objects which

are off screen. Even though we've basically created a 2D object, Ogre is still a 3D engine, and treats our 2D

object as if it sits in 3D space. This means that if we create this object and attach it to a SceneNode (as we will

do in the next section), it will disappear on us when we look away. To fix this we will set the bounding box of the

object to be infinite, so that the camera will always be inside of it:

8 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 54: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

setBoundingBox(AxisAlignedBox::BOX_INFINITE);

Be sure to note that we have added this code after the clear() call. Every time you call ManualObject::clear(),

the bounding box is reset, so be careful if you create another ManualObject which is cleared often because the

bounding box will have to be set every time you recreate it.

That's all we have to do for the SelectionBox class. Be sure your code compiles before continuing, but note that

no functionality has changed in the program yet.

Volume Selection

Setup

Before we can jump into the selection code, we first have to set up a few things. First of all, include

SelectionBox.h to our IntermediateTutorial4.h, so that we can use it, and we also need to declare a pointer

variable for a selection box in the protected section of your IntermediateTutorial4 class.

#include SelectionBox.h

SelectionBox* mSelectionBox;

Now that it is all set up we need to create an instance of the SelectionBox class, and have the SceneManager

create a volume query for us. Add the following code at the end of createScene(), so we know for sure that the

SceneManager has been initialized:

mSelectionBox = new SelectionBox("SelectionBox");

mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject

(mSelectionBox);

mVolQuery = mSceneMgr->createPlaneBoundedVolumeQuery(PlaneBoundedVolumeList

());

Secondly we need to be sure the frame listener cleans up after itself when we are done. Add the following code

to ~IntermediateTutorial4:

mSceneMgr->destroyQuery(mVolQuery);

if(mSelectionBox)

{

delete mSelectionBox;

}

Note that we let the SceneManager clean up the query for us instead of deleting it directly.

Mouse Handlers

9 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 55: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

The feature we are trying to implement is volume selection. This means that when the user clicks the mouse

down and drags it across the screen a rectangle will be drawn. When they let go of the mouse, all objects within

the rectangle will be selected. The first thing we will need to do is handle when the mouse is pressed. We'll

need to store the starting location and set the SelectionBox to be visible. Find the mousePressed function and

add the following code:

if (id == OIS::MB_Left)

{

CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr();

mStart.x = mouse->getPosition().d_x / (float)arg.state.width;

mStart.y = mouse->getPosition().d_y / (float)arg.state.height;

mStop = mStart;

mSelecting = true;

mSelectionBox->clear();

mSelectionBox->setVisible(true);

}

Note that we use the CEGUI::MouseCursor's x and y coordinates and not OIS's mouse coordinates. This is

because OIS sometimes thinks the mouse is in a different location than what CEGUI is actually displaying. To

make sure we are in sync with what the user is seeing, we'll use CEGUI's mouse coordinates.

The next thing we will need to do is stop displaying the selection rectangle and perform the selection query

when the user releases the mouse. Add the following code to the mouseReleased code:

if (id == OIS::MB_Left)

{

performSelection(mStart, mStop);

mSelecting = false;

mSelectionBox->setVisible(false);

}

Additionally, every time the mouse is moved, we need to update the rectangle to the new coordinates:

if (mSelecting)

{

CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr

();

mStop.x = mouse->getPosition().d_x / (float)arg.state.width;

mStop.y = mouse->getPosition().d_y / (float)arg.state.height;

mSelectionBox->setCorners(mStart, mStop);

}

We adjust the mStop vector every time the mouse is moved so that we can simply use it for the setCorners

member function. Finally, we initialize mSelecting to false in the constructor, since at start up we haven't made

a selection yet. Replace the IntermediateTutorial4 constructor by the following code:

IntermediateTutorial4::IntermediateTutorial4(void):

mSelecting(false)

10 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 56: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

{

}

Compile and run your application. You can now draw a rectangle using the mouse.

PlaneBoundedVolumeListSceneQuery

Now that we have the SelectionBox properly rendering, we need to actually perform the volume selection. Find

the performSelection() function and add the following code:

float left = first.x, right = second.x,

top = first.y, bottom = second.y;

if (left > right)

swap(left, right);

if (top > bottom)

swap(top, bottom);

In this code section, we have assigned the vector parameters into the left, right, top, and bottom variables. The

if statements ensure that we actually have the lowest value in left and top. (If the rectangle is drawn

"backwards", meaning bottom-right to top-left, then we have to perform this swap.)

Next, we have to check and see how big of an area the rectangle actually makes. If the rectangle is too small,

our method of creating a plane bound volumes will fail and we will end up selecting too many or too few

objects. If the rectangle is less than a certain percentage of the screen, we will simply return and not perform

the selection. I have arbitrarily selected 0.0001 as the threshold for canceling the query, though you should

probably play around with this value for your own program. Also, in a real application you should probably find

the center of the rectangle and perform a standard RaySceneQuery instead of doing nothing:

if ((right - left) * (bottom - top) < 0.0001)

return;

Now we are at the meat of the function, and we need to perform the query itself. PlaneBoundedVolumeQueries

use planes to enclose an area, then select any objects inside that area. For this example we will build an area

enclosed by five planes which face inward. To create these planes out of our rectangle, we will create 4 rays,

one for each corner of the rectangle. Once we have these four rays, we will grab points along the rays to create

the planes:

Ogre::Ray topLeft = mCamera->getCameraToViewportRay(left, top);

Ogre::Ray topRight = mCamera->getCameraToViewportRay(right, top);

Ogre::Ray bottomLeft = mCamera->getCameraToViewportRay(left, bottom);

Ogre::Ray bottomRight = mCamera->getCameraToViewportRay(right, bottom);

Now we will create the planes. Note that we are grabbing a point 100 units along the ray. This was chosen fairly

arbitrarily. We could have chosen 2 instead of 100. The only point which matters here is the front plane, which

we are starting 3 units in front of the Camera.

Ogre::PlaneBoundedVolume vol;

vol.planes.push_back(Ogre::Plane(topLeft.getPoint(3), topRight.getPoint

11 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 57: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

(3), bottomRight.getPoint(3))); // front plane

vol.planes.push_back(Ogre::Plane(topLeft.getOrigin(), topLeft.getPoint

(100), topRight.getPoint(100))); // top plane

vol.planes.push_back(Ogre::Plane(topLeft.getOrigin(), bottomLeft.getPoint

(100), topLeft.getPoint(100))); // left plane

vol.planes.push_back(Ogre::Plane(bottomLeft.getOrigin(),

bottomRight.getPoint(100), bottomLeft.getPoint(100))); // bottom plane

vol.planes.push_back(Ogre::Plane(topRight.getOrigin(), topRight.getPoint

(100), bottomRight.getPoint(100))); // right plane

These planes have now defined an "open box" which extends to infinity in front of the camera. You can think of

the rectangle we drew with the mouse as being the termination point of the box just in front of the camera. Now

that we have created the planes, we need to execute the query:

Ogre::PlaneBoundedVolumeList volList;

volList.push_back(vol);

mVolQuery->setVolumes(volList);

Ogre::SceneQueryResult result = mVolQuery->execute();

Finally we need to handle the results of the query. First we will deselect all previously selected objects, then we

will select all objects which were found by the query.

deselectObjects();

Ogre::SceneQueryResultMovableList::iterator iter;

for (iter = result.movables.begin(); iter != result.movables.end(); ++iter)

selectObject(*iter);

That's all we need to do for the query. Note we can also use query flags on volume queries, even though we

have not done so in this tutorial. See the previous tutorial for more information on query flags.

Now before we forget, we need to fill in our selectObject and deselectObjects functions.

void IntermediateTutorial4::deselectObjects()

{

std::list<Ogre::MovableObject*>::iterator iter = mSelected.begin();

for(iter; iter != mSelected.end(); iter++)

{

(*iter)->getParentSceneNode()->showBoundingBox(false);

}

}

void IntermediateTutorial4::selectObject(Ogre::MovableObject* obj)

{

obj->getParentSceneNode()->showBoundingBox(true);

mSelected.push_back(obj);

}

12 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 58: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Compile and run the application. You can now volume select objects in the scene!

A Final Note About Bounding Boxes

As you may have noticed from this tutorial and the previous two tutorials, selection in Ogre relies on the

bounding box of the objects in question and not on the mesh itself. This means that a RaySceneQuery and

PlaneBoundedVolumeQuery will always be too accepting in what is actually hit by the query. There are ways of

performing pixel perfect ray selection (such as what you would need to do to see if a gunshot in an FPS

actually hit its target) and volume selection which will give you perfectly accurate results for a trade-off in

speed. Unfortunately, this is beyond the scope of this tutorial. Take a look at Raycasting to the polygon level

for more information on how to do this in pure Ogre. If you have integrated Ogre with a physics library, such as

OgreNewt, they should also provide methods to do this for you.

You did not learn all of this about ray queries and volume queries for nothing though. Doing a mesh based

selection is very time consuming and can quickly kill your framerate if you try to check everything in a scene. In

fact, the most common way to do "true" mouse selection is to first perform an Ogre query (such as a

RaySceneQuery), then individually test each intersection returned from the query with a physics engine that will

actually check the mesh's geometry to see if you actually hit it or just came very close.

Alias: Intermediate_Tutorial_4

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR

COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE

BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED

HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

• "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the

Work in its entirety in unmodified form, along with a number of other contributions, constituting separate

and independent works in themselves, are assembled into a collective whole. A work that constitutes a

Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this

License.

• "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works,

such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound

recording, art reproduction, abridgment, condensation, or any other form in which the Work may be

recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be

considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the

Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with

a moving image ("synching") will be considered a Derivative Work for the purpose of this License.

• "Licensor" means the individual or entity that offers the Work under the terms of this License.

• "Original Author" means the individual or entity who created the Work.

• "Work" means the copyrightable work of authorship offered under the terms of this License.

13 de 16Página Intermediate Tutorial 4e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=276&page=Intermediate...

Page 59: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Static Geometry

Intermediate Tutorial 5: Static Geometry

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Introduction

There will be a lot of situations when you need to add objects to your scene, but you won't need to move them

at all. For example, unless you are adding some kind of physics into the mix, a rock or a tree will rarely ever

need to move. For these situations, Ogre provides the StaticGeometry class, which allows you to build batches

of objects to render in a big bunch. This is generally faster than doing it manually with SceneNodes. In this

tutorial we will cover basic uses of StaticGeometry in your application. We will also cover a few more things you

can do with ManualObject. See the previous tutorial for more information about ManualObject.

In this tutorial we will manually create a grass mesh, then add many of them to a StaticGeometry object in our

scene.

You can find the code for this tutorial here. As you go through the tutorial you should be slowly adding code to

your own project and watching the results as we build it. Note that this tutorial has been build largely from the

Grass Demo that comes with Ogre's samples. You can also dig around in the source of that demo for more

information.

Table of contents

Introduction

Prerequisites

Creating the Scene

Creating the Mesh

Adding Static Geometry

Conclusion

Modifying the StaticGeometry Object

Advanced Object Batching

Creating a ManualObject from the SceneManager

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Prerequisites

Load up a new project with the Ogre Tutorial Framework and set up your project to look like this:

IntermediateTutorial5.h

1 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 60: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#ifndef __IntermediateTutorial5_h_

#define __IntermediateTutorial5_h_

#include "BaseApplication.h"

class IntermediateTutorial5 : public BaseApplication

{

public:

IntermediateTutorial5(void);

virtual ~IntermediateTutorial5(void);

protected:

virtual void createScene(void);

virtual void createGrassMesh(void);

};

#endif // #ifndef __IntermediateTutorial5_h_

IntermediateTutorial5.cpp

#include "IntermediateTutorial5.h"

//-------------------------------------------------------------------------

------------

IntermediateTutorial5::IntermediateTutorial5(void)

{

}

//-------------------------------------------------------------------------

------------

IntermediateTutorial5::~IntermediateTutorial5(void)

{

}

void IntermediateTutorial5::createGrassMesh(void)

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial5::createScene(void)

{

createGrassMesh();

mSceneMgr->setAmbientLight(Ogre::ColourValue::White);

mCamera->setPosition(150, 50, 150);

mCamera->lookAt(0, 0, 0);

Ogre::Entity* robot = mSceneMgr->createEntity("robot",

"robot.mesh");

2 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 61: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject

(robot);

Ogre::Plane plane;

plane.normal = Ogre::Vector3::UNIT_Y;

plane.d = 0;

Ogre::MeshManager::getSingleton().createPlane("floor",

Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane, 450.0f,

450.0f, 10, 10, true, 1, 50.0f, 50.0f, Ogre::Vector3::UNIT_Z);

Ogre::Entity* planeEnt = mSceneMgr->createEntity("plane", "floor");

planeEnt->setMaterialName("Examples/GrassFloor");

planeEnt->setCastShadows(false);

mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject

(planeEnt);

}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

IntermediateTutorial5 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception

has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() << std::endl;

#endif

}

return 0;

}

3 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 62: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

#ifdef __cplusplus

}

#endif

Be sure you can compile and run the application before continuing. You should see a Robot standing on a flat

plane.

Creating the Scene

Creating the Mesh

The first thing we need to do is create the grass mesh we will be rendering. The general idea is to create three

square quads which overlap each other. Each quad will have a grass texture on it, so that when you look at it

from any direction other than straight down it will look somewhat like 3D grass. The easiest way to overlap

these quads will be to create one, then create another at a 60 degree rotation, and then a third at yet another

60 degrees.

As with the previous tutorial, we will be using ManualObject to generate our object, but unlike the previous

tutorial we will be actually creating a mesh instead of simple line lists (which will require us to build an index

buffer too, specifying the triangles as we build it).

The first thing we will do is define some basic variables. Since we are going to be rotating a quad that we are

defining, it will be easiest if we use Ogre constructs like Vector3 and Quaternion to do the math for us instead

of trying to do it manually. Our plan is to create a Vector3 with the X and Z coordinates in it, build a single quad

from it, then rotate it with a Quaternion and repeat. Find the createGrassMesh member function and add the

following code:

const float width = 25;

const float height = 30;

Ogre::ManualObject mo("GrassObject");

Ogre::Vector3 vec(width/2, 0, 0);

Ogre::Quaternion rot;

rot.FromAngleAxis(Ogre::Degree(60), Ogre::Vector3::UNIT_Y);

Now that we have set up our variables, we need to start defining the ManualObject. Unlike the previous tutorial,

we will actually be designating a material to use with this object. Our RenderOperation will be set to be a

triangle list as well, meaning after we define our vertices we must define a list of triangles which create the

faces of our quads:

mo.begin("Examples/GrassBlades", Ogre::RenderOperation::OT_TRIANGLE_LIST);

for (int i = 0; i < 3; ++i)

{

For each quad we are going to define four vertices, one for each corner. For each vertex we are also going to

specify a texture coordinate. Texture coordinates tell Ogre how to sample the texture we have specified in the

Examples/GrassBlades material. We will make the top left corner the (0, 0) point of the texture, the bottom right

corner (1, 1) and so on:

4 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 63: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

mo.position(-vec.x, height, -vec.z);

mo.textureCoord(0, 0);

mo.position(vec.x, height, vec.z);

mo.textureCoord(1, 0);

mo.position(-vec.x, 0, -vec.z);

mo.textureCoord(0, 1);

mo.position(vec.x, 0, vec.z);

mo.textureCoord(1, 1);

Now that we have defined the four corners of our quad, we now need to create faces. As we mentioned briefly

in the previous tutorial, you must specify faces by creating triangles, and you must be sure to wind them

counter clockwise to face towards you. For each quad, we will build two triangles. The first will be from the (0th,

3rd, 1st) vertices defined, and the second from the (0th, 2nd, 3rd) vertices defined. This properly defines the

quad. Also remember that we are looping a few times, and every time through we create 4 vertices, thus we

have to use an offset variable to select the proper vertex:

int offset = i * 4;

mo.triangle(offset, offset+3, offset+1);

mo.triangle(offset, offset+2, offset+3);

Next we need to rotate the vector we are using to create the current quad and continue looping. After the loop

is finished we must call ManualObject::end to complete the object:

vec = rot * vec;

}

mo.end();

Now that we have defined the manual object, we are almost ready to start creating our StaticGeometry. One

last thing we are going to do is create a mesh out of our ManualObject. Meshes are a touch more optimized

than using a ManualObject directly for rendering. To do this, we simply need to call the

ManualObject::convertToMesh with a name to store the mesh as:

mo.convertToMesh("GrassBladesMesh");

We are now finished creating the grass mesh. Note that if you have created a highly complex mesh in this way,

you may save it to file and simply load the file back in instead of recreating the ManualObject every time you

load the program. To do this, we will take the return value of convertToMesh (which we discarded in the actual

code), and feed the Mesh it returns to the MeshSerializer::exportMesh function. Here is an example of how to

do that:

// Do not add to the code!

Ogre::MeshPtr ptr = mo.convertToMesh("GrassBladesMesh");

5 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 64: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::MeshSerializer ser;

ser.exportMesh(ptr.getPointer(), "grass.mesh");

We are now ready to create the StaticGeometry.

Adding Static Geometry

The createScene method is already populated with several things. I have already added code to create a floor

plane, add a robot, set the Camera's position, and so on since we have covered how to do those things in

previous tutorials. Be sure to make sure you understand the current contents of this function before continuing.

The first thing we are going to do now is create an Entity based off of the grass mesh we created earlier and

create a StaticGeometry object. Note that we will only create one Entity to use with our StaticGeometry. Find

the createScene method and add the following code to the end of it:

Ogre::Entity *grass = mSceneMgr->createEntity("grass", "GrassBladesMesh");

Ogre::StaticGeometry *sg = mSceneMgr->createStaticGeometry

("GrassArea");

const int size = 375;

const int amount = 20;

The size variable will define how large of an area we are covering with grass and the amount variable will

define how many objects we will put in each row of our StaticGeometry.

The next thing we need to do is define the size and origin of the StaticGeometry. Once we build the object (by

calling StaticGeometry::build), we can no longer change the origin or region the StaticGeometry defines. The

origin is the top left corner of the region that the StaticGeometry defines. If you want to place the

StaticGeometry around a point, you will need set the origin's x and z coordinates to be half of the region's size

for x and z:

sg->setRegionDimensions(Ogre::Vector3(size, size, size));

sg->setOrigin(Ogre::Vector3(-size/2, 0, -size/2));

This will center the object around the point (0, 0, 0). To center it around a point in 3D space, you would need to

do something similar to this:

// Do not add to the project!

sg->setOrigin(Vector3(-size/2, -size/2, -size/2) + Vector3(x, y, z));

Where x, y, z is the point in 3D space to center it around. Also note that we do define the vertical height of the

object when setting the region. Be sure that the y component of setRegionDimensions is at least as large as

the highest object in the StaticGeometry.

The next thing we need to do is add objects to the StaticGeometry. This next piece of code is somewhat

complex because we are adding a whole grid of grass to the geometry, and giving a random shift in x, z

position, a random rotation, and a random vertical scale to it. In reality, the most important thing to understand

in this is the StaticGeometry::addEntity:

6 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 65: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

for (int x = -size/2; x < size/2; x += (size/amount))

{

for (int z = -size/2; z < size/2; z += (size/amount))

{

Ogre::Real r = size / (float)amount / 2;

Ogre::Vector3 pos(x + Ogre::Math::RangeRandom(-r, r), 0, z

+ Ogre::Math::RangeRandom(-r, r));

Ogre::Vector3 scale(1, Ogre::Math::RangeRandom(0.9, 1.1),

1);

Ogre::Quaternion orientation;

orientation.FromAngleAxis(Ogre::Degree

(Ogre::Math::RangeRandom(0, 359)), Ogre::Vector3::UNIT_Y);

sg->addEntity(grass, pos, orientation, scale);

}

}

The addEntity function takes in the Entity to use, the position of the object, the orientation of the object, and the

scale of the object. When you are defining StaticGeometry you will either use the addEntity function or the

addSceneNode function. The addSceneNode function walks the -SceneNode adding all Entities to the static

geometry, using the position, orientation, and scale of the children SceneNodes instead of specifying them

manually. Note that if you use the addSceneNode function, be sure to remove the node from its parent -

SceneNode, since the addSceneNode function does not remove it for you. If you do not, Ogre will render both

the StaticGeometry you created and the original -SceneNode which is not what you want.

Finally we need to build the StaticGeometry before it is displayed:

sg->build();

Compile and run your application, you should now see a robot standing in a small patch of grass.

Conclusion

Modifying the StaticGeometry Object

Once the StaticGeometry is created, you are not supposed to do too much with it, since that would mostly

defeat the purpose. You can, however, do things like wave the grass with the wind. If you are interested in how

to do this, take a look at the grass demo which comes with Ogre. The GrassListener::waveGrass function

modifies the grass to perform a wave-like motion.

Advanced Object Batching

This is, of course, just the beginnings of object batching. You should use StaticGeometry any time you have

objects that are grouped together and will not move. If you are trying to create something as intensive or as

expansive as a forest or trying to add grass to a huge amount of terrain, you should take a look at one of the

more advanced batching techniques, like the PagedGeometry Engine.

Creating a ManualObject from the SceneManager

7 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 66: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

In addition to instantiating a ManualObject directly, you can also use

Ogre::SceneManager::createManualObject to instantiate a ManualObject. One user reported an inability to

render ManualObjects when they were instantiated directly, so if you're encountering errors, it may be worth

using the SceneManager to instantiate your ManualObjects as part of troubleshooting.

Proceed to Intermediate Tutorial 6 Projective Decals

Alias: Intermediate_Tutorial_5

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR

COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE

BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED

HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

• "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the

Work in its entirety in unmodified form, along with a number of other contributions, constituting separate

and independent works in themselves, are assembled into a collective whole. A work that constitutes a

Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this

License.

• "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works,

such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound

recording, art reproduction, abridgment, condensation, or any other form in which the Work may be

recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be

considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the

Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with

a moving image ("synching") will be considered a Derivative Work for the purpose of this License.

• "Licensor" means the individual or entity that offers the Work under the terms of this License.

• "Original Author" means the individual or entity who created the Work.

• "Work" means the copyrightable work of authorship offered under the terms of this License.

• "You" means an individual or entity exercising rights under this License who has not previously violated

the terms of this License with respect to the Work, or who has received express permission from the

Licensor to exercise rights under this License despite a previous violation.

• "License Elements" means the following high-level license attributes as selected by Licensor and

indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other

limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

3. License Grant

8 de 11Página Intermediate Tutorial 5e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=277&page=Intermediate...

Page 67: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Projective Decals

Projective Decals

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Table of contents

Projective Decals

Introduction

Getting Started

New Textures

The Initial Code

Projecting Decals

Frustums

Modifying the Material

Calling the Functions

Getting Rid of the Back Projection

Introduction

Modifying the Projector

Modifying the Material

Showing Off the Projection

Simple Rotation

One Final Note

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Introduction

In this tutorial we will be covering how to add projective decals to an object in the scene. Projective texturing is

useful when you want to do something like a selection indicator on the ground, an aiming sight that's projected

on what you're aiming at, or some other type of decal that's projected onto something (but doesn't become a

permanent part of the target like splatting). Here's a screenshot of an aiming site being projected onto

everyone's favorite ogre head:

1 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 68: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

You can find the code for this tutorial here. As you go through the tutorial you should be slowly adding code to

your own project and watching the results as we build it.

Getting Started

New Textures

Before we get started on this project, we need to add two new images we will be using:

No such attachment on this page

No such attachment on this page

The best place to put these files is in the media/materials/textures folder (for most people this should be located

in the OgreSDK folder). Also note the character case by making sure the file names are all lower case as they

are referenced as such in the tutorial.

The Initial Code

Set up your Intermediate Tutorial 6 project to look something like this:

IntermediateTutorial6.h

#ifndef __IntermediateTutorial6_h_

#define __IntermediateTutorial6_h_

#include "BaseApplication.h"

class IntermediateTutorial6 : public BaseApplication

{

public:

IntermediateTutorial6(void);

virtual ~IntermediateTutorial6(void);

protected:

virtual void createScene(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

virtual void createProjector();

virtual void makeMaterialReceiveDecal(const Ogre::String& matName);

Ogre::SceneNode* mProjectorNode;

Ogre::Frustum* mDecalFrustum;

Ogre::Frustum* mFilterFrustum;

2 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 69: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

float mAnim;

};

#endif // #ifndef __IntermediateTutorial6_h_

IntermediateTutorial6.cpp

#include "IntermediateTutorial6.h"

//-------------------------------------------------------------------------

------------

IntermediateTutorial6::IntermediateTutorial6(void)

{

}

//-------------------------------------------------------------------------

------------

IntermediateTutorial6::~IntermediateTutorial6(void)

{

}

//-------------------------------------------------------------------------

------------

void IntermediateTutorial6::createScene(void)

{

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));

Ogre::Light* light = mSceneMgr->createLight("MainLight");

light->setPosition(20, 80, 50);

mCamera->setPosition(60, 200, 70);

mCamera->lookAt(0,0,0);

Ogre::Entity* ent;

for (int i = 0; i < 6; i++)

{

Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()-

>createChildSceneNode();

ent = mSceneMgr->createEntity("head" +

Ogre::StringConverter::toString(i), "ogrehead.mesh");

headNode->attachObject(ent);

Ogre::Radian angle(i + Ogre::Math::TWO_PI / 6);

headNode->setPosition(75 * Ogre::Math::Cos(angle), 0, 75 *

Ogre::Math::Sin(angle));

}

}

bool IntermediateTutorial6::frameRenderingQueued(const Ogre::FrameEvent&

evt)

{

3 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 70: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

return BaseApplication::frameRenderingQueued(evt);

}

void IntermediateTutorial6::createProjector()

{

}

void IntermediateTutorial6::makeMaterialReceiveDecal(const Ogre::String&

matName)

{

}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

IntermediateTutorial6 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception

has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() << std::endl;

#endif

}

return 0;

}

#ifdef __cplusplus

}

#endif

4 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 71: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Compile and run this program before continuing. You should see six Ogre heads.

Projecting Decals

Frustums

A frustum represents a pyramid capped at the near and far ends, which represents a visible area or a

projection. Ogre uses this to represent cameras with (the Camera class derives directly from the Frustum

class). In this tutorial, we will be using a frustum to project the decal onto the meshes in the scene.

The first thing we will do to create the projector is to create the frustum which represents it and attach it to a

SceneNode. Find the createProjector() method and add the following code:

mDecalFrustum = new Ogre::Frustum();

mProjectorNode = mSceneMgr->getRootSceneNode()->createChildSceneNode

("DecalProjectorNode");

mProjectorNode->attachObject(mDecalFrustum);

mProjectorNode->setPosition(0,5,0);

This creates a projector which will grow the decal as you get farther and farther away from it, a lot like how a

film projector works. If you want to create a projector which maintains a constant size and shape of decal at

whatever distance we set, you should add the following code (but we do not for this tutorial):

// Do not add this to the project

mDecalFrustum->setProjectionType(Ogre::PT_ORTHOGRAPHIC);

mDecalFrustum->setOrthoWindowHeight(100);

setOrthoWindowHeight() is used together with aspect ratio to set the size of an orthographic frustum.

Before continuing, please take note of where our frustum is projecting the decal. In this application there is a

ring of Ogre heads and the frustum sits in the dead center of them (though shifted up slightly, by 5 units),

pointed in the -Z direction (which is the default since we did not change the orientation). This means that,

eventually, when we run the application decals will be projected onto the back Ogre heads.

Modifying the Material

In order for the decal to actually show up on an object, the material that it uses has to receive the decal. We do

this by creating a new pass which renders the decal on top of the regular texture. The frustum determines the

location, size, and shape of the projected decal. In this demo we will be modifying the material itself to receive

the decal, but for most real applications, you should probably create a clone of the material to modify so you

can switch it off by setting the material back to the original one.

The first thing we will do is get the material and create a new pass for the material. Find

makeMaterialReceiveDecal() and add the following code:

Ogre::MaterialPtr mat = (Ogre::MaterialPtr)

Ogre::MaterialManager::getSingleton().getByName(matName);

Ogre::Pass *pass = mat->getTechnique(0)->createPass();

Now that we have created our pass, we need to set up blending and lighting. We will be adding a new texture

which must be blended properly with the current texture already on the object. To do this we will set the scene

5 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 72: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

blending to be transparent alpha, and the depth bias to be 1 (so that there is no transparency in the decal).

Lastly we need to disable lighting for the material so that it always shows up no matter what the lighting of the

scene is. If you want the decal in your application to be affected by the scene lighting you should not add that

last function call:

pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);

pass->setDepthBias(1);

pass->setLightingEnabled(false);

Now that we have our new pass we need to create a new texture unit state using our decal.png image. The

second function call turns on projective texturing and takes in the frustum we have created. The final two calls

set up the filtering and addressing modes:

Ogre::TextureUnitState *texState = pass->createTextureUnitState

("decal.png");

texState->setProjectiveTexturing(true, mDecalFrustum);

texState->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);

texState->setTextureFiltering(Ogre::FO_POINT, Ogre::FO_LINEAR,

Ogre::FO_NONE);

We have set the texture addressing mode to clamp so that the decal doesn't "loop" itself on the object. For the

filtering options, we have set the magnification of the object to use standard linear, but we have basically turned

off filtering for minification (FO_POINT), and turned off mipmapping entirely. This prevents the border of the

decal (which is transparent) from getting blurred into the rest of the texture when it is minimized. If we do not do

that, there will be ugly smearing all over the outside of the place the decal is projected.

This is all you need to do to set up the material.

Calling the Functions

Now that we have built the functions, we need to call them to set up the projector and the material. Add the

following code to the end of the createScene method:

createProjector();

for (unsigned int i = 0; i < ent->getNumSubEntities(); i++)

{

makeMaterialReceiveDecal(ent->getSubEntity(i)->getMaterialName());

}

Note that the ent variable already has one of the ogre head entities stored in it from the previous loop. Since all

the ogre heads use the same material, we only need to select a random one of them to grab the material

names from.

Compile and run the application, you should see a few Ogre heads with a decal projected onto them.

Getting Rid of the Back Projection

Introduction

As you have probably noticed when running the application, there are actually two decals being projected. The

first is projected in the -Z direction, which is where our frustum is facing, the other is projected in the +Z

6 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 73: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

direction, onto the ogre heads behind the frustum we have created. The reason for this is when a decal is

projected out of the front of the frustum, a corresponding (inverted) decal is projected out of the back of it.

This is obviously not what we want. To fix it we will introduce a filter that will remove the back projection.

Modifying the Projector

To filter the back projection, we need a new frustum for the filter which points in the direction we wish to filter.

Add the following code to the createProjector() method:

mFilterFrustum = new Ogre::Frustum();

mFilterFrustum->setProjectionType(Ogre::PT_ORTHOGRAPHIC);

Ogre::SceneNode *filterNode = mProjectorNode->createChildSceneNode

("DecalFilterNode");

filterNode->attachObject(mFilterFrustum);

filterNode->setOrientation(Ogre::Quaternion(Ogre::Degree

(90),Ogre::Vector3::UNIT_Y));

This should all be familiar. The only difference is that we have rotated the node by 90 degrees to face

backwards.

Modifying the Material

Now we need to add another texture state to the pass we added on the material. Add the following to

makeMaterialReceiveDecal:

texState = pass->createTextureUnitState("decal_filter.png");

texState->setProjectiveTexturing(true, mFilterFrustum);

texState->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP);

texState->setTextureFiltering(Ogre::TFO_NONE);

This all should look familiar. Note that we are using the filter texture, the filter frustum, and the we have turned

off filtering. Compile and run the application. You should now see only the forward projection of the decals.

Showing Off the Projection

Simple Rotation

To show off the projection, we will rotate the projector. To rotate the projector, simply add the following line of

code to the beginning of the frameRenderingQueued method:

mProjectorNode->rotate(Ogre::Vector3::UNIT_Y, Ogre::Degree

(evt.timeSinceLastFrame * 10));

Compile and run the application. You will now see the decal projected along the circle of ogre heads.

One Final Note

One last thing to note about decals, if you use decals in your application, be sure that the outer border pixels of

the decals are completely transparent (zero alpha). If not, the decal will smear due to the way texture clamping

works.

Work around

7 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 74: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

The above notice is true, but there's a way around it if your texture doesn't already have transparent borders:

As long as your texture is in a format that supports alpha channels (such as PNG), you can set the Texture

Address Mode to TAM_BORDER (or border, if you're doing this in a script) and set the Texture Border Colour

to (0, 0, 0, 0). This causes any texture coordinates outside the range 0, 1 to have the border color you specify,

which is black with 0 alpha. So essentially, you just added a transparent border to your texture.

You can find a quick implementation in Projective Decals.

Alias: Intermediate_Tutorial_6

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR

COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE

BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED

HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS.

1. Definitions

• "Collective Work" means a work, such as a periodical issue, anthology or encyclopedia, in which the

Work in its entirety in unmodified form, along with a number of other contributions, constituting separate

and independent works in themselves, are assembled into a collective whole. A work that constitutes a

Collective Work will not be considered a Derivative Work (as defined below) for the purposes of this

License.

• "Derivative Work" means a work based upon the Work or upon the Work and other pre-existing works,

such as a translation, musical arrangement, dramatization, fictionalization, motion picture version, sound

recording, art reproduction, abridgment, condensation, or any other form in which the Work may be

recast, transformed, or adapted, except that a work that constitutes a Collective Work will not be

considered a Derivative Work for the purpose of this License. For the avoidance of doubt, where the

Work is a musical composition or sound recording, the synchronization of the Work in timed-relation with

a moving image ("synching") will be considered a Derivative Work for the purpose of this License.

• "Licensor" means the individual or entity that offers the Work under the terms of this License.

• "Original Author" means the individual or entity who created the Work.

• "Work" means the copyrightable work of authorship offered under the terms of this License.

• "You" means an individual or entity exercising rights under this License who has not previously violated

the terms of this License with respect to the Work, or who has received express permission from the

Licensor to exercise rights under this License despite a previous violation.

• "License Elements" means the following high-level license attributes as selected by Licensor and

indicated in the title of this License: Attribution, ShareAlike.

2. Fair Use Rights

Nothing in this license is intended to reduce, limit, or restrict any rights arising from fair use, first sale or other

limitations on the exclusive rights of the copyright owner under copyright law or other applicable laws.

8 de 11Página Intermediate Tutorial 6e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=278&page=Intermediate...

Page 75: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Render to texture (RTT)

Intermediate Tutorial 7: Render to texture

Any problems you encounter during working with this tutorial should be posted in the Help Forum .

Table of contents

Introduction

The Initial Code

Render to texture

Creating the render textures

Write texture to file

Implementing a mini screen

General

Setting up the rectangle

Creating a material from scratch

Usage of RenderTargetListener

General

Implementing a RenderTargetListener

RTTs and shaders

Passing an RTT to a shader

Conclusion

1. Definitions

2. Fair Use Rights

3. License Grant

4. Restrictions

5. Representations, Warranties and Disclaimer

6. Limitation on Liability.

7. Termination

8. Miscellaneous

Introduction

This tutorial will teach you the basics of rendering to textures. This technique is necessary for a variety of effects

especially in combination with shaders, e.g Motion Blur.

The idea behind rendering to textures RTT is rather simple. Instead of sending the render output data of your

scene to your render window, you just send it to a texture. This texture can then be used just as a normal texture

from your harddrive.

1 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 76: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

You can find the code for this tutorial here.

The Initial Code

Set up your IntermediateTutorial7 application to look like this:

IntermediateTutorial7.h

#ifndef __IntermediateTutorial7_h_

#define __IntermediateTutorial7_h_

#include "BaseApplication.h"

class IntermediateTutorial7 : public BaseApplication

{

public:

IntermediateTutorial7(void);

virtual ~IntermediateTutorial7(void);

protected:

virtual void createScene(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

Ogre::MovablePlane* mPlane;

2 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 77: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::Entity* mPlaneEnt;

Ogre::SceneNode* mPlaneNode;

};

#endif // #ifndef __IntermediateTutorial7_h_

IntermediateTutorial7.cpp

#include "IntermediateTutorial7.h"

//--------------------------------------------------------------------------

-----------

IntermediateTutorial7::IntermediateTutorial7(void)

{

}

//--------------------------------------------------------------------------

-----------

IntermediateTutorial7::~IntermediateTutorial7(void)

{

}

//--------------------------------------------------------------------------

-----------

void IntermediateTutorial7::createScene(void)

{

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));

Ogre::Light* light = mSceneMgr->createLight("MainLight");

light->setPosition(20, 80, 50);

mCamera->setPosition(60, 200, 70);

mCamera->lookAt(0,0,0);

Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create

("PlaneMat", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

Ogre::TextureUnitState* tuisTexture = mat->getTechnique(0)->getPass

(0)->createTextureUnitState("grass_1024.jpg");

mPlane = new Ogre::MovablePlane("Plane");

mPlane->d = 0;

mPlane->normal = Ogre::Vector3::UNIT_Y;

Ogre::MeshManager::getSingleton().createPlane("PlaneMesh",

Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, *mPlane, 120, 120,

1, 1, true, 1, 1, 1, Ogre::Vector3::UNIT_Z);

mPlaneEnt = mSceneMgr->createEntity("PlaneEntity", "PlaneMesh");

mPlaneEnt->setMaterialName("PlaneMat");

mPlaneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

mPlaneNode->attachObject(mPlaneEnt);

}

3 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 78: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

//--------------------------------------------------------------------------

-----------

bool IntermediateTutorial7::frameRenderingQueued(const Ogre::FrameEvent&

evt)

{

mPlaneNode->yaw(Ogre::Radian(evt.timeSinceLastFrame));

return BaseApplication::frameRenderingQueued(evt);

}

//--------------------------------------------------------------------------

-----------

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

#endif

#ifdef __cplusplus

extern "C" {

#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )

#else

int main(int argc, char *argv[])

#endif

{

// Create application object

IntermediateTutorial7 app;

try {

app.go();

} catch( Ogre::Exception& e ) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

MessageBox( NULL, e.getFullDescription().c_str(), "An exception

has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

std::cerr << "An exception has occured: " <<

e.getFullDescription().c_str() << std::endl;

#endif

}

return 0;

}

#ifdef __cplusplus

}

#endif

Compile and run this program before continuing. You should see a simple plane rotating around the Y-axis.

4 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 79: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Render to texture

Creating the render textures

First of all, we need to create a texture.

Ogre::TexturePtr rtt_texture = Ogre::TextureManager::getSingleton

().createManual("RttTex",

Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D,

mWindow->getWidth(), mWindow->getHeight(), 0, Ogre::PF_R8G8B8,

Ogre::TU_RENDERTARGET);

("mWindow" is your Ogre::RenderWindow pointer)

The first parameter to createManual() is the name of the texture, which is commonly named RttTex. The second

specifies the resource group, the third the texture type (in our case a 2D texture), the fourth and the fifth the

width and height of the texture. You also have to pass the number of mip maps you want as well as the texture

format and a usage flag. Concerning the texture format: There are many different ones, but the most simple is

the PF_R8G8B8 which will create a 24-bit RGB-texture. If you are in the need of an alpha channel,

PF_R8G8B8A8 should suit best for this.

Now we need to get the render target of this texture in order to set some parameters and later also add a

RenderTargetListener.

5 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 80: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::RenderTexture *renderTexture = rtt_texture->getBuffer()-

>getRenderTarget();

renderTexture->addViewport(mCamera);

renderTexture->getViewport(0)->setClearEveryFrame(true);

renderTexture->getViewport(0)->setBackgroundColour

(Ogre::ColourValue::Black);

renderTexture->getViewport(0)->setOverlaysEnabled(false);

After getting the render texture, we have to add a viewport to it. This is the viewport with whose content the

RenderTexture will be filled. We also tell the viewport to clear itself every frame, set the background color to

black and request to disable all the overlays for the texture as we don't want to have them on it.

Write texture to file

At this point we've got everything ready to do a first check: We just store the content of our created

RenderTexture in a file. The handy thing is that the RenderTextures are derived from RenderTarget and so have

a ready function to store the content of the texture in a file (as well as we can do it with RenderWindows). But

before saving the content to a file, you should update your RenderTexture. You can do this either manually via

the update() function or request the application to automatically update the RenderTexture by once calling the

setAutoUpdate() function.

// Either this way

renderTexture->setAutoUpdated(true);

// or this way

renderTexture->update();

// Now save the contents

renderTexture->writeContentsToFile("start.png");

After running the application, you will find a new .png file in the directory your .exe is in, that should show the

content of your screen (in our case the textured plane).

Implementing a mini screen

General

Now, we will add a mini screen in the lower right corner of our application window. To do this we'll add a

Rectangle2D to our scene which will get our RenderTexture as texture. Our scene will be shown twice: one time

rendered to the RenderWindow as normal, and a second time as the texture on our Rectangle2D.

Using this method it is also possible to implement TV screens (or CCTV cameras, etc). You can display other

parts of your level by putting a camera there, creating a RenderTexture (as described in the first section) and

displaying it on the TV screen.

Setting up the rectangle

Creating an Ogre::Rectangle2D is rather simple:

Ogre::Rectangle2D *mMiniScreen = new Ogre::Rectangle2D(true);

mMiniScreen->setCorners(0.5f, -0.5f, 1.0f, -1.0f);

mMiniScreen->setBoundingBox(Ogre::AxisAlignedBox(-100000.0f *

Ogre::Vector3::UNIT_SCALE, 100000.0f * Ogre::Vector3::UNIT_SCALE));

6 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 81: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

In the first line, we set the parameter to true to generate texture coordinates, which we later need to map the

texture on the rectangle. In the second line we have to specify the corners of the rectangle. Left is -1.0 and right

+1.0, top is +1.0 and bottom -1.0. So our rectangle is just in the lower right corner of our application window.

We also give the rectangle a real huge bounding box to prevent it from being culled when we are not facing its

scene node. You could also use AxisAlignedBox::setInfinite(), instead of manually setting the size of the box, but

there have been some problems with this in the past, so manually setting a huge box should be the safest way.

Now that we have created the rectangle, we just need to attach it to a SceneNode which should not be anything

new for you.

Ogre::SceneNode* miniScreenNode = mSceneMgr->getRootSceneNode()-

>createChildSceneNode("MiniScreenNode");

miniScreenNode->attachObject(mMiniScreen);

If you run the application at this stage, you will see a white rectangle in the lower right corner of our application

window.

Creating a material from scratch

The next step is now to show the RenderTexture we created in the first section on this rectangle. Therefore we

have to create a material for it. We can do this either in a material script, or directly in the code during runtime. In

this tutorial, we will use the second method to have everything together in one file.

7 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 82: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::MaterialPtr renderMaterial = Ogre::MaterialManager::getSingleton

().create("RttMat",

Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

Ogre::Technique* matTechnique = renderMaterial->createTechnique();

matTechnique->createPass();

renderMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);

renderMaterial->getTechnique(0)->getPass(0)->createTextureUnitState

("RttTex");

In the first line we create an empty material and add a technique and a pass in the next two lines. With the third

line we disable the lightning to prevent our mini screen from being darker than the actual texture. In the last line

we create a new TextureUnitState by passing our created RenderTexture from the first section.

Now we can apply this material to our mini screen.

mMiniScreen->setMaterial("RttMat");

If you run the application now, you will see an undesired effect: The mini screen itself has a miniscreen! To solve

this, Ogre introduces RenderTargetListener.

Usage of RenderTargetListener

General

In many cases you will need RTTs with only some scene objects on it. In our case we need a texture that only

contains the output of the application window without the mini screen yet on it, as this texture is intended to be

applied to our mini screen. So we have to hide the mini screen each time before the window output is stored in

our RenderTexture. And this is where the RenderTargetListener comes in.

This listener has two important functions: preRenderTargetUpdate() and postRenderTargetUpdate(). As the

names induce, the first function is automatically called by the listener before the RenderTexture is filled (so we

can hide our mini screen here) whereas the second function is automatically called after the RenderTexture has

been filled (so we can show our mini screen again).

Implementing a RenderTargetListener

Implementing a RenderTargetListener is quite simple. First of all we let our application class derive from the

RenderTargetListener to make it become its own listener. After that we just have to hide and show our mini

screen in the pre- and postRenderTargetUpdate() functions. So your application class should basically look like

this now:

IntermediateTutorial7.h

class IntermediateTutorial7 : public BaseApplication, public

Ogre::RenderTargetListener

{

public:

IntermediateTutorial7(void);

virtual ~IntermediateTutorial7(void);

protected:

virtual void createScene(void);

virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

virtual void preRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);

virtual void postRenderTargetUpdate(const Ogre::RenderTargetEvent& evt);

8 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 83: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

Ogre::MovablePlane* mPlane;

Ogre::Entity* mPlaneEnt;

Ogre::SceneNode* mPlaneNode;

//This should be taken out of the createScene member and brought here

Ogre::Rectangle2D* mMiniScreen;

};

IntermediateTutorial7.cpp

void IntermediateTutorial7::preRenderTargetUpdate(const

Ogre::RenderTargetEvent& evt)

{

mMiniScreen->setVisible(false);

}

void IntermediateTutorial7::postRenderTargetUpdate(const

Ogre::RenderTargetEvent& evt)

{

mMiniScreen->setVisible(true);

}

Now, the last step we need to do is to add the listener to the RenderTexture. As the application class is derived

from RenderTargetListener we can just pass the this pointer as parameter at the end of the createScene

function.

renderTexture->addListener(this);

That's it. Now you have a simple mini screen in your app. It's that simple.

9 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 84: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

RTTs and shaders

Passing an RTT to a shader

As RTTs are often used with shaders, you have to know how to pass the RenderTexture to one. Fortunately, this

is really simple.

The most simple case is one where you never change the texture for the shader during runtime. If it remains

constant, you just have to tell your material script the name of the texture you create during runtime, in our case

"RttTex". So your texture_unit in the material should look like this:

texture_unit

{

texture RttTex

}

If you need to change the texture the shader should use during runtime, just add the two following lines:

Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().getByName

("Sepia");

material->getTechnique(0)->getPass(0)->getTextureUnitState(0)-

>setTextureName("OtherRttTex");

10 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...

Page 85: Www.ogre3d.org Tikiwiki Tiki-print.php Page Ref Id=273&Page=Intermediate Tutorial 1

With the first line we get a pointer to the material (in this case here a sepia shader material) where we change

the texture name in the second line to the desired one.

Now, as you have set the correct texture name in your material script with one of these two methods, you can

access the texture in your cg shader with the following line:

uniform sampler2D SceneSampler : register(s0)

Well, that's it. Your texture of the mini screen should now be passed through your shader and look like this (with

the sepia shader attached to this tutorial page):

Conclusion

These are all the basics you need to know to start with RTT. Now play around a bit with this code and discover a

new world of graphical effects.

Alias: Intermediate_Tutorial_7

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS

PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER

11 de 15Página Intermediate Tutorial 7e

21-05-2012http://www.ogre3d.org/tikiwiki/tiki-print.php?page_ref_id=279&page=Intermediate...