#pragma once
#include "actor.h"
struct Grid: public Actor
{
Grid()
{}
void render()
{
const float RANGE = 15.0f;
glLineWidth(5);
glColor3f(0.0,1.0,0.0);
glBegin(GL_LINES);
for(int i=-5; i<=5; ++i) {
float offset = 0.2*float(i)*RANGE;
glVertex3f(-RANGE, 0, offset);
glVertex3f( RANGE, 0, offset);
glVertex3f( offset, 0,-RANGE);
glVertex3f( offset, 0, RANGE);
}
glEnd();
glLineWidth(1);
}
};
#pragma once
#include "libs.h"
#include "model.h"
#include "actor.h"
struct Scene
{
Scene();
void loadActors(Model*);
virtual Actor* loadActor(GeometryMap::value_type &value);
virtual void render();
virtual void tick(float secondsDelta) {}
ActorMap actors;
};
#include "scene.h"
#include "colourcube.h"
#include "jetplane.h"
#include "grid.h"
using namespace std;
Scene:: Scene()
{
}
void Scene::loadActors(Model*model)
{
string actorName;
foreach (GeometryMap::value_type &value, model->entities)
{
Actor* actor = loadActor(value);
if (actor)
{
actorName = value.first;
actors.insert(actorName, actor);
}
}
actorName = "grid";
actors.insert (actorName, new Grid());
}
Actor* Scene::loadActor(GeometryMap::value_type &value)
{
return new Actor(&value.second);
}
void Scene::render()
{
foreach (ActorMap::value_type value, actors)
{
value->second->render();
}
}
Scene *scene = new Scene ();
scene->loadActors(model);
theWorld.scene = scene;
This adjustment will enable us to load a scene with actors loaded from multiple model files.
Build and test this now.
#pragma once
#include "libs.h"
#include "scene.h"
#include "animateactor.h"
#include "forcegeneratorregistry.h"
struct AnimateScene : public Scene
{
AnimateScene();
Actor* loadActor(GeometryMap::value_type &value);
void tick(float secondsDelta);
AnimateActorMap animateActors;
ForceGeneratorRegistry forceGeneratorRegistry;
};
#include "animatescene.h"
#include "sphereactor.h"
using namespace std;
AnimateScene :: AnimateScene()
{
}
Actor* AnimateScene::loadActor(GeometryMap::value_type &value)
{
Actor *actor = 0;
if (value.first == "pSphere1")
{
PhysicsActor * sphere = new SphereActor(&value.second);
actor = dynamic_cast<Actor*> (sphere);
// create an anchor
Vector3 anchor = sphere->position + Vector3(0,5,0);
// create force generator
AnchoredSpringForceGenerator * fg =
new AnchoredSpringForceGenerator(anchor, 1.0, 3.0);
// add actor<->forceGenerator pair to registry
forceGeneratorRegistry.add(sphere, fg);
}
else if (value.first == "pSphere2" || value.first == "pSphere3")
{
actor = new SphereActor(&value.second);
}
else
{
return Scene::loadActor(value);
}
if (actor)
{
animateActors[value.first] = (AnimateActor*) actor;
}
return actor;
}
void AnimateScene::tick(float secondsDelta)
{
forceGeneratorRegistry.applyForce(secondsDelta);
foreach (AnimateActorMap::value_type value, animateActors)
{
value.second->integrate(secondsDelta);
}
}
and leaving all other objects to the base loadActors method to create.
We also override the tick method
Back in main, we can just create an animated scene:
Scene *scene = new AnimateScene ();
and we are 'live' again with the physics simulation
Build and test. Note how we have a static cube object, and three animated spheres.
If you revert to:
Scene *scene = new Scene ();
Currently materials are mixed in with light functions in lights.h/cpp. These should be refactored into their own files, and moved into the 'scene' package.
This is the header for a new file 'materials.h'
#pragma once
#include "libs.h"
enum MaterialTypes {flatRed, flatYellow, flatBlue, flatGreen, flatGray, plasticRed, shinyWhite, brass, bronze,
chrome, copper, gold, pewter, silver, polishSilver, plasticBlack} ;
void applyMaterial(MaterialTypes material);
#include "libs.h"
#include "materials.h"
struct Material
{
GLfloat ambient[4];
GLfloat diffuse[4];
GLfloat specular[4];
GLfloat shiny;
};
Material materials [] =
{
{ {0.8, 0.0, 0.0, 1.0}, // flatRed
{0.8, 0.0, 0.0, 1.0},
{0.8, 0.0, 0.0, 1.0}, 25.0 },
{ {0.8, 0.0, 0.0, 1.0}, //flatYellow
{0.8, 0.0, 0.0, 1.0},
{0.8, 0.0, 0.0, 1.0}, 25.0 },
{ {0.0, 0.0, 0.8, 1.0}, // flatblue
{0.0, 0.0, 0.8, 1.0},
{0.0, 0.0, 0.8, 1.0}, 25.0 },
{ {0.0, 0.8, 0.0, 1.0}, // flatgreen
{0.0, 0.8, 0.0, 1.0},
{0.0, 0.8, 0.0, 1.0}, 25.0 },
{ {0.5, 0.5, 0.5, 1.0}, // flatgrey
{0.5, 0.5, 0.5, 1.0},
{0.5, 0.5, 0.5, 1.0}, 25.0 },
{ {0.3, 0.0, 0.0, 1.0}, //plasticRed
{0.6, 0.0, 0.0, 1.0},
{0.8, 0.6, 0.6, 1.0}, 32.0 },
{ {1.0, 1.0, 1.0, 1.0}, //shinyWhite
{1.0, 1.0, 1.01, 1.0},
{1.0, 1.0, 1.0, 1.0}, 100.0 },
{ {0.329412, 0.223529, 0.027451, 1.0}, //brass
{0.780392, 0.568627, 0.113725, 1.0},
{0.9922157, 0.941176, 0.807843, 1.0}, 27.8974 },
{ {0.2125, 0.1275, 0.054, 1.0}, //bronze
{0.714, 0.4284, 0.18144, 1.0},
{0.303548, 0.271906, 0.106721, 1.0}, 25.6 },
{ {0.25, 0.25, 0.25, 1.0}, //chrome
{0.4, 0.4, 0.4, 1.0},
{0.774597, 0.774597, 0.774597, 1.0}, 76.8 },
{ {0.19125, 0.0735, 0.0225, 1.0}, //copper
{0.7038, 0.27048, 0.0828, 1.0},
{0.256777, 0.137622, 0.086014, 1.0}, 12.8 },
{ {0.24725, 0.1995, 0.0745, 1.0},// gold
{0.75164, 0.60648, 0.22658, 1.0},
{0.628281, 0.555802, 0.366065, 1.0}, 51.2 },
{ {0.10558, 0.058824, 0.113725, 1.0}, //pewter
{0.427451, 0.470588, 0.541176, 1.0},
{0.3333, 0.3333, 0.521569, 1.0}, 9.84615 },
{ {0.19225, 0.19225, 0.19225, 1.0}, //silver
{0.50754, 0.50754, 0.50754, 1.0},
{0.508273, 0.508273, 0.508273, 1.0}, 51.2 },
{ {0.23125, 0.23125, 0.23125, 1.0}, //polishSilver
{0.2775, 0.2775, 0.2775, 1.0},
{0.773911, 0.773911, 0.773911, 1.0}, 89.6 },
{ {0.0, 0.0, 0.0, 1.0}, //plasticBlack
{0.01, 0.01, 0.01, 1.0},
{0.50, 0.50, 0.50, 1.0},32.0 }
};
void applyMaterial(MaterialTypes material)
{
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, materials[material].ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, materials[material].diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, materials[material].specular);
glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, materials[material].shiny);
}
Delete 'lights.h' and 'lights.cpp' from world. We will be replacing them with encapsulated versions.
First, in 'light.h' introduced these abstractions:
#pragma once
#include "libs.h"
#include "vector3.h"
struct LightSetting
{
float* components;
int type;
};
struct lttype
{
bool operator() (const LightSetting& s1, const LightSetting& s2) const
{
return (s1.type < s2.type);
}
};
struct AmbientLight
{
enum AmbientLightSetting {fullLight, halfLight, defaultLight};
AmbientLight(AmbientLightSetting setting);
void switchOn();
AmbientLightSetting setting;
};
struct SpotLight
{
SpotLight (GLenum id, Vector3 p);
void augment(LightSetting &setting);
void switchOn();
void reposition();
GLenum id;
Vector3 position;
std::set<LightSetting, lttype> lightSettings;
};
struct ltspotlight
{
bool operator() (const SpotLight& s1, const SpotLight& s2) const
{
return (s1.id < s2.id);
}
};
typedef std::set<SpotLight, ltspotlight> SpotLightSet;
These encapsulate basic light settings, classes for ambient light and spotlights + support classes or sets of these.
This is the implementation:
#include "light.h"
float settingTable[][4] =
{
{ 1.0f, 1.0f, 1.0f, 1.0f }, // full
{ 0.5f, 0.5f, 0.5f, 1.0f }, // half
{ 0.2f, 0.2f, 0.2f, 1.0f } // default
};
AmbientLight::AmbientLight(AmbientLightSetting setting)
: setting(setting)
{
}
void AmbientLight::switchOn()
{
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, settingTable[setting]);
}
SpotLight::SpotLight (GLenum id, Vector3 p)
: id(id), position(p)
{
}
void SpotLight::augment(LightSetting &setting)
{
lightSettings.insert(setting);
}
void SpotLight::switchOn()
{
foreach (LightSetting setting, lightSettings)
{
glLightfv(id, setting.type, setting.components);
}
glEnable(id);
}
void SpotLight::reposition()
{
float pos[4];
pos[0] = position.X;
pos[1] = position.Y;
pos[2] = position.Z;
pos[3] = 1;
glLightfv(id, GL_POSITION, pos);
}
Note the key features - light can be declared, their characteristics set, switched on and, for spotlights, positioned.
These classes should build successfully. To use them, we with introduce a 'LightingModel'
#pragma once
#include "libs.h"
#include "light.h"
struct LightingModel
{
LightingModel();
void switchOnLights();
void positionLights();
SpotLightSet spotLights;
AmbientLight ambientLight;
};
#include "lightingmodel.h"
float ambientLightModerate[] = { 0.3f, 0.3f, 0.3f, 1.0f };
float diffuseLightModerate[] = { 0.7f, 0.7f, 0.7f, 1.0f };
LightingModel::LightingModel()
: ambientLight(AmbientLight::fullLight)
{
SpotLight spotLight0 ((GLenum)GL_LIGHT0, Vector3(-50, 50, 0));
LightSetting setting1 = {ambientLightModerate, GL_AMBIENT};
LightSetting setting2 = {diffuseLightModerate, GL_DIFFUSE};
spotLight0.augment(setting1);
spotLight0.augment(setting2);
spotLights.insert(spotLight0);
}
void LightingModel::switchOnLights()
{
ambientLight.switchOn();
foreach (SpotLight spotLight, spotLights)
{
spotLight.switchOn();
}
glEnable(GL_NORMALIZE);
}
void LightingModel::positionLights()
{
foreach (SpotLight spotLight, spotLights)
{
spotLight.reposition();
}
}
LightingModel lightingModel;
void World::initialize(string name, int width, int height)
{
int argc=0;
char** argv;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(width, height);
glutCreateWindow(name.c_str());
Color::Black.renderClear();
glEnable(GL_DEPTH_TEST);
glFrontFace(GL_CCW);
glPolygonMode(GL_FRONT,GL_LINE);
glPolygonMode(GL_BACK,GL_LINE);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glutDisplayFunc(renderScene);
glutSpecialFunc(keyboardSpecial);
glutPassiveMotionFunc(::mouseMovement);
glEnable(GL_LIGHTING);
lightingModel.switchOnLights();
}
Note we have removed some duplication residue (left over from projection initialisation).
In the last line, we 'switch on' the lights.
Finally, we make sure to position the lights in our scene:
void World::render()
{
Color::Blue.renderClear();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (projectors.isPerspective())
{
glLoadIdentity();
cameras.currentCamera->render();
}
lightingModel.positionLights();
scene->render();
glutSwapBuffers();
}
Build and test.
You may need to re-introduce the jet plane in scene to verify that lights are operating as expected:
void Scene::loadActors(Model*model)
{
//....
actorName = "jet";
actors.insert (actorName, new JetPlane());
}
This is the complete project to date:
The lighting settings should ideally be extenalised to a resource file.
This could contain ambient light settings, and then specifications for each spot light.
LightModel would then load this and render it into the scene.
For this type of information, the Yaml file format is useful format:
http://yaml.org/
http://en.wikipedia.org/wiki/YAML
There are many parsers available. This one looks reasonably active, and includes a cross-platform version:
http://code.google.com/p/yaml-cpp/