OGRE (Object-Oriented Graphics Rendering Engine)

Using Waveform Controllers

Controllers, ControllerFunctions, and ControllerValues

When I initially wrote the source for the tutorial I used two variables of type Real, and two booleans for each light to control its flashing. The code was a couple pages long and it would have been very hard to extend the functionality of it. I thought to myself that it would be nice if I could just set up the logic of controlling the colour value of a Light and Billboard pair just once and let different time based functions control the intensity. Just when I was about to make my own I discovered that Ogre has these built into it. And now they are my new best friends.

The way it hangs together in Ogre is that you create a class descending from ControllerValue which overrides the methods getValue and setValue. The names should make their function obvious enough but one thing to note is that they only deal with values between 0.0 and 1.0.

A ControllerFunction is given a value to operate on and outputs a result between 0.0 and 1.0, which gets fed to a ControllerValue. These functions can be based on anything, but for this tutorial we will only focus on using it to control values based on time.

A Controller connects a ControllerFunction with an input ControllerValue and an output ControllerValue. The most useful input ControllerValue to us comes from a method on the ControllerManager called getFrameTimeSource which will give the value of time between frames.

Controllers, ControllerFunctions, and ControllerValues

With our new found knowledge of controllers lets make a ControllerValue and ControllerFunction to control these lights. Go to the top of the Space.h file just after #include "ExampleApplication.h and enter this

    class LightFlasher : public ControllerValue<Real>
    {
    protected:
    	Light* mLight;
    	Billboard* mBillboard;
    	ColourValue mMaxColour;
    	Real intensity;
    public:
    	LightFlasher(Light* light, Billboard* billboard, ColourValue maxColour)
    	{
    		mLight = light;
    		mBillboard = billboard;
    		mMaxColour = maxColour;
    	}

    	virtual Real  getValue (void) const
    	{
    		return intensity;
    	}

    	virtual void  setValue (Real value)
    	{
    		intensity = value;

    		ColourValue newColour;

    		// Attenuate the brightness of the light
    		newColour.r = mMaxColour.r * intensity;
    		newColour.g = mMaxColour.g * intensity;
    		newColour.b = mMaxColour.b * intensity;

    		mLight->setDiffuseColour(newColour);
    		mBillboard->setColour(newColour);
    	}
    };

When the controller calls setValue, the value is used to attentuate the Red, Green, and Blue values of the Light and Billboard. The effect of this will be that when the value is set to 0.0 the light will be black which is the same as being off, and when set to 1.0 the light will be at its brightest.

The ControllerFunction that we will use will be based on WaveformControllerFunction. We do not need to modify the class very much, in fact all we will do is override the constructor and default some of the arguments to make using it a little easier.

    class LightFlasherControllerFunction : public WaveformControllerFunction
    {
    public:
    	LightFlasherControllerFunction(WaveformType wavetype, Real frequency, Real phase) : WaveformControllerFunction(wavetype, 0, frequency, phase, 1, true)
    	{

    	}
    };

The constructor for a WaveformControllerFunction is

    WaveformControllerFunction::WaveformControllerFunction(WaveformType wType, Real base,  Real frequency, Real phase, Real amplitude, bool delta)

The first argument, WaveformType is the type of wave to be generated, which can be a sine, traingle, square, sawtooth, or inverse sawtooth. We will use most of these wave forms in the tutorial. Base is added to the value generated, it effectively sets the bottom value of the wave and we will only use 0.0 in this tutorial so it has been hardcoded in the constructor. Frequency is how often the whole wave form is produced within one second. Phase is how far through the wave to start in. Amplitude is multiplied to the wave value to increase the size of the wave, we will only use 1.0 for this. Delta is used internally to specify wether or not the values are supplied as delta or absolute values, defaulting here to true.

Switching the lights on

In the previous tutorial we had to turn the ambient lighting on so that we could see the ship but now we want to turn it back down again so that we can see the effects of our new light sources. So in the createScene method change the line setting the ambient lighting to

    		// Set a very low level of ambient lighting
    		mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));

We need some member variables to hold the Controllers and their Values and Functions. Add these to the protected section of the class.

    	// Light flashers
    	LightFlasher* mRedLightFlasher;
    	LightFlasher* mBlueLightFlasher;
    	LightFlasher* mWhiteLightFlasher;

    	// Light controller functions
    	LightFlasherControllerFunction* mRedLightControllerFunc;
    	LightFlasherControllerFunction* mBlueLightControllerFunc;
    	LightFlasherControllerFunction* mWhiteLightControllerFunc;

    	// Light controllers
    	Controller<Real>* mRedLightController;
    	Controller<Real>* mBlueLightController;
    	Controller<Real>* mWhiteLightController;

And now, finally, we create our controllers. Type this into the bottom of the createScene method.

    		// Light flashers
    		mRedLightFlasher = new LightFlasher(mRedLight, mRedLightBoard, ColourValue::Red);
    		mBlueLightFlasher = new LightFlasher(mBlueLight, mBlueLightBoard, ColourValue::Blue);
    		mWhiteLightFlasher = new LightFlasher(mWhiteLight, mWhiteLightBoard, ColourValue::White);

    		// Light controller functions
    		mRedLightControllerFunc = new LightFlasherControllerFunction(Ogre::WFT_SINE, 0.5, 0.0);
    		mBlueLightControllerFunc = new LightFlasherControllerFunction(Ogre::WFT_SQUARE, 0.5, 0.5);
    		mWhiteLightControllerFunc = new LightFlasherControllerFunction(Ogre::WFT_TRIANGLE, 4, 0.0);

    		// Light controllers
    		ControllerManager* mControllerManager = &ControllerManager::getSingleton();
    		mRedLightController = mControllerManager->createController(mControllerManager->getFrameTimeSource(), mRedLightFlasher, mRedLightControllerFunc);
    		mBlueLightController = mControllerManager->createController(mControllerManager->getFrameTimeSource(), mBlueLightFlasher, mBlueLightControllerFunc);
    		mWhiteLightController = mControllerManager->createController(mControllerManager->getFrameTimeSource(), mWhiteLightFlasher, mWhiteLightControllerFunc);

There is one LightFlasher for each of Red, Blue, and White connected to their respective Light and Billboard. Then we create one ControllerFunction to pulse the red light in a sine wave fashion, one to flash the blue light on and off, and one to pulse the white light 4 times a second in a triangle wave fashion. We then use the ControllerManager to create three Controllers, one for each light and hook them up to their ControllerFunctions and ControllerValues. The first argument to their constructors is a call to getFrameTimeSource, which will give us an object that provides the Controller with time information.

Compile and run the program. The lights should be flashing or pulsing as described above.

Blinking Lights

I said earlier that is is easier to extend the functionality of this than my first attempt. Lets do it now by changing the way that the white light works. Lets make it blink on for one tenth of a second once every 4 seconds. To do this we will create a new class called LightBlinker inheriting from the LightFlasher. It will be designed to accept input from the sawtooth wave and when the wave hits a certain value we will turn the light on, otherwise the light will remain off.

Put this class definition just after the definition of LightFlasher.

    class LightBlinker : public LightFlasher
    {
    protected:
    	ColourValue mMinColour;
    	Real mActivationLevel;
    public:
    	LightBlinker(Light* light, Billboard* billboard, ColourValue maxColour, ColourValue minColour, Real activationLevel) : LightFlasher(light, billboard, maxColour)
    	{
    		mMinColour = minColour;
    		mActivationLevel = activationLevel;
    	}

    	virtual Real  getValue (void) const
    	{
    		return intensity;
    	}

    	virtual void setValue(Real value)
    	{
    		intensity = value;

    		if(value < mActivationLevel)
    		{
    			// Light is off
    			mLight->setDiffuseColour(mMinColour);
    			mBillboard->setColour(mMinColour);
    		} else {
    			// Light is blinking on
    			mLight->setDiffuseColour(mMaxColour);
    			mBillboard->setColour(mMaxColour);
    		}
    	}

    };

Lets now use our new class. Change the line creating the light controller function for the white light to be

    	mWhiteLightControllerFunc = new LightFlasherControllerFunction(Ogre::WFT_SAWTOOTH, 0.25, 0.0);

And the line creating the white light flasher.

    	mWhiteLightFlasher = new LightBlinker(mWhiteLight, mWhiteLightBoard, ColourValue::White, ColourValue::Black, 0.975);

So what is this value of 0.975? As described above it is the value that the wave must reach before the light will turn on. But why that particular value? This is the way it works: the saw tooth wave starts at 0 and hits 1.0 at the end of 4 seconds (frequency is 0.25 as stated in the constructor for the LightFlasherFunction). This means that each second will contribute 0.25 to the value. We however only want one tenth of a second, leaving us with 0.025 and since we want the light to be on for only that last tenth of a second we subtract 0.025 from 1.0 and we get 0.975, clear enough?

Compile and run the program so far. You should have all three lights working but the white light will blink on only once every 4 seconds.

Back to Index |  | << Previous section | Next section >>