diff --git a/.hgignore b/.hgignore index b640b3e..811cc28 100644 --- a/.hgignore +++ b/.hgignore @@ -11,6 +11,7 @@ engine/libraries/oglft/liboglft/OGLFT.h start runtests fysxasteroids +fysxasteroidseditor config.rc Debug/ diff --git a/asteroids/AsteroidEntity.cc b/asteroids/AsteroidEntity.cc index fbf2cce..abba494 100644 --- a/asteroids/AsteroidEntity.cc +++ b/asteroids/AsteroidEntity.cc @@ -23,13 +23,15 @@ bool AsteroidEntity::CollisionEvent (Engine::EntityBase* entity_state, vector3d int i; for (i = 0; i < mSubAsteroidsCount; i++) { AsteroidEntity *asteroid = (AsteroidEntity*) Engine::CreateEntity (GameEntityTypeAsteroid); + vector3d position (rand()/float(RAND_MAX) * 1. - 0.5, 0., rand()/float(RAND_MAX) * 1. - 0.5); + asteroid->mPhysicState->mPosition = mPhysicState->mPosition + position; + asteroid->mPhysicState->mVelocity = position + mPhysicState->mVelocity * 0.5; + asteroid->mPhysicState->mAngleVelocity = mPhysicState->mAngleVelocity + (rand()/float(RAND_MAX) * 2. - 1) * mPhysicState->mAngleVelocity; + asteroid->mSubAsteroidsCount = 0; asteroid->mPhysicState->mRadius = 2. * mPhysicState->mRadius / mSubAsteroidsCount; static_cast(asteroid->mPhysicState->mShape)->setRadius (asteroid->mPhysicState->mRadius); - asteroid->mPhysicState->mPosition = mPhysicState->mPosition + position; - asteroid->mPhysicState->mVelocity = position; - asteroid->mPhysicState->mAngleVelocity = mPhysicState->mAngleVelocity + (rand()/float(RAND_MAX) * 2. - 1) * mPhysicState->mAngleVelocity; } Engine::KillEntity (mId); diff --git a/asteroids/Controller.cc b/asteroids/Controller.cc index e98037c..14099a3 100644 --- a/asteroids/Controller.cc +++ b/asteroids/Controller.cc @@ -35,20 +35,19 @@ bool Controller::OnReceiveEvent (const Engine::EventBasePtr &event) { void Controller::ResetPlayerEntity () { Engine::HaltSoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav")); + // Reset the angular velocity of the player entity if one exists + if (GetModel()->GetPlayerEntityId() != Engine::NullEntityId) { + Engine::EntityBase *player_entity = GetModel()->GetEntity(GetModel()->GetPlayerEntityId()); + player_entity->mPhysicState->mAngleVelocity = 0.; + + int i; + for (i = 0; i < EntityControllerMaxKeyStates; i++) + player_entity->UnsetControllerKeyState(i); + + } + // We definitely have to reset the player entity id GetModel()->SetPlayerEntityId(Engine::NullEntityId); - - Engine::EntityBase *player_entity = GetModel()->GetEntity(GetModel()->GetPlayerEntityId()); - - // if we were unable to get the player then there is no need to invalidate - // the state - if (!player_entity) - return; - - int i; - for (i = 0; i < EntityControllerMaxKeyStates; i++) - player_entity->UnsetControllerKeyState(i); - } void Controller::ResetLevel() { diff --git a/asteroids/Model.cc b/asteroids/Model.cc index 2a318ba..60db153 100644 --- a/asteroids/Model.cc +++ b/asteroids/Model.cc @@ -66,8 +66,9 @@ int Model::OnInit (int argc, char* argv[]) { mPlayerName = "Player"; - Engine::PlayMusic (Engine::GetResourceFullPath("/data/sounds/intro_music.ogg")); - + mLevelAuthor = ""; + mLevelTitle = ""; + Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); @@ -260,12 +261,28 @@ int Model::DoLoadLevel (const char* filename) { std::string entity_type_str; int entity_count = 0; + SetLevelTitle (""); + SetLevelAuthor (""); while (level_file >> entity_type_str) { if (entity_type_str[0] == '#') { getline (level_file, entity_type_str); Engine::LogDebug ("Read Comment: %s", entity_type_str.c_str()); continue; + } else if (entity_type_str == "Title") { + std::string level_title; + + getline (level_file, level_title); + level_title = strip_whitespaces (level_title); + SetLevelTitle (level_title); + continue; + } else if (entity_type_str == "Author") { + std::string level_author; + + getline (level_file, level_author); + level_author = strip_whitespaces (level_author); + SetLevelAuthor (level_author); + continue; } GameEntityType entity_type = GameEntityTypeUnknown; @@ -325,6 +342,13 @@ int Model::DoSaveLevel (const char* filename) { level_file << "# Format" << std::endl; level_file << "# " << std::endl; + if (GetLevelTitle() != "") { + level_file << "Title " << mLevelTitle << std::endl; + } + if (GetLevelAuthor() != "") { + level_file << "Author " << mLevelAuthor << std::endl; + } + std::map::iterator iter = mEntities.begin(); unsigned int player_id = GetPlayerEntityId(); @@ -401,6 +425,8 @@ void Model::OnNewGame() { } void Model::OnShipExplode () { + Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav")); + mPlayerLives --; if (mPlayerLives == 0) { diff --git a/asteroids/Model.h b/asteroids/Model.h index 6c6de62..d8dccaf 100644 --- a/asteroids/Model.h +++ b/asteroids/Model.h @@ -37,10 +37,22 @@ class Model : public Engine::ModelBase { std::string GetLevelName() { return mLevelName; }; void SetLevelName(const std::string &name) { - Engine::LogMessage("new level name: %s", name.c_str()); + Engine::LogDebug("new level name: %s", name.c_str()); mLevelName = name; } + std::string GetLevelAuthor() { return mLevelAuthor; }; + void SetLevelAuthor(const std::string &author) { + Engine::LogDebug("new level author: %s", author.c_str()); + mLevelAuthor = author; + } + + std::string GetLevelTitle() { return mLevelTitle; }; + void SetLevelTitle(const std::string &title) { + Engine::LogDebug("new level title: %s", title.c_str()); + mLevelTitle = title; + } + /* Highscore */ struct HighscoreEntry { HighscoreEntry(): name ("unknown"), points (0) { }; @@ -85,6 +97,8 @@ class Model : public Engine::ModelBase { std::string mPlayerName; std::vector mLevelList; std::string mLevelName; + std::string mLevelAuthor; + std::string mLevelTitle; virtual bool OnReceiveEvent (const Engine::EventBasePtr &event); diff --git a/asteroids/RocketEntity.cc b/asteroids/RocketEntity.cc index 8cd835c..ebd1827 100644 --- a/asteroids/RocketEntity.cc +++ b/asteroids/RocketEntity.cc @@ -7,6 +7,9 @@ namespace asteroids { +Engine::Variable RocketEntity::VarTimeToLive ("rocket_timetolive", "1.0"); +Engine::Variable RocketEntity::VarSpeed ("rocket_speed", "15"); + void RocketEntity::Update (float delta_sec) { mSecToLive -= delta_sec; diff --git a/asteroids/RocketEntity.h b/asteroids/RocketEntity.h index a8c8d1f..eaea850 100644 --- a/asteroids/RocketEntity.h +++ b/asteroids/RocketEntity.h @@ -14,6 +14,7 @@ struct RocketEntityPhysicState : public Engine::EntityPhysicState { mType = GameEntityTypeRocket; } virtual ~RocketEntityPhysicState() {}; + }; struct RocketEntity: public Engine::EntityBase { @@ -31,6 +32,9 @@ struct RocketEntity: public Engine::EntityBase { } float mSecToLive; + + static Engine::Variable VarTimeToLive; + static Engine::Variable VarSpeed; }; } diff --git a/asteroids/ShipEntity.cc b/asteroids/ShipEntity.cc index 9725bc4..a2eda63 100644 --- a/asteroids/ShipEntity.cc +++ b/asteroids/ShipEntity.cc @@ -107,7 +107,7 @@ void ShipEntity::Attack () { RocketEntity *rocket_entity = (RocketEntity*) Engine::CreateEntity (GameEntityTypeRocket); - rocket_entity->mSecToLive = 1.75; + rocket_entity->mSecToLive = RocketEntity::VarTimeToLive.GetFloatValue(); RocketEntityPhysicState *rocket_physics = (RocketEntityPhysicState*) rocket_entity->mPhysicState; rocket_physics->mPosition = attack_dir; @@ -115,7 +115,7 @@ void ShipEntity::Attack () { rocket_physics->mPosition += entity_physic->mPosition; rocket_physics->mOrientation = entity_physic->mOrientation; rocket_physics->mVelocity = attack_dir.normalize(); - rocket_physics->mVelocity *= ShipEntity::VarMaxSpeed.GetFloatValue() + 0.5; + rocket_physics->mVelocity *= RocketEntity::VarSpeed.GetFloatValue(); Engine::PlaySound (Engine::GetResourceFullPath("/data/sounds/laser.wav")); } diff --git a/asteroids/View.cc b/asteroids/View.cc index 079c867..e5e11a7 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -149,7 +149,7 @@ bool View::OnReceiveEvent (const Engine::EventBasePtr &event) { 0., rel_velocity[2] * (rand()/float(RAND_MAX)) * 1.5 ); - part_sprite_particle->mPhysicState->mVelocity = part_velocity; + part_sprite_particle->mPhysicState->mVelocity = part_velocity * -1.; part_sprite_particle->mPhysicState->mAngleVelocity = (rand()/float(RAND_MAX) - 0.5 ) * 100.; @@ -410,8 +410,10 @@ void View::DrawUi () { ViewState current_view_state = GetViewState(); + /* SelectFont ("console.ttf size=12"); Engine::GUI::Label (99999, GetStringViewState(current_view_state), 8, 16); + */ switch (current_view_state) { case ViewStateMainMenu: @@ -474,6 +476,11 @@ void View::DrawPageTitle (const std::string& title) { void View::DrawUiMainMenu() { DrawPageTitle ("Asteroids"); + SelectFont("console.ttf size=23 color=#444444"); + DrawGLString( screen_right - 272, screen_bottom - 16, "http://www.fysx.org"); + SelectFont("console.ttf size=23 color=#cccccc"); + DrawGLString( screen_right - 270, screen_bottom - 18, "http://www.fysx.org"); + SelectFont("console.ttf size=23"); if (Engine::GUI::Button (1, "New Game", screen_right * 0.5 - 100, 200, button_width, button_height)) { PushViewState(ViewStateEnterPlayername); @@ -487,10 +494,12 @@ void View::DrawUiMainMenu() { PushViewState(ViewStateShowHighscore); } + /* if (Engine::GUI::Button (6, "E", screen_right - 48, 20, 32, button_height)) { PushViewState(ViewStateEditor); GetController()->ResetLevel(); } + */ if (Engine::GUI::Button (4, "Quit", screen_right * 0.5 - 100, 380, button_width, button_height)) { Engine::RunCommand("quit"); @@ -572,7 +581,7 @@ void View::DrawUiLevelComplete() { GetModel()->SetGameState(GameStatePaused); if (mEditorState == EditorStateTest) { - GetModel()->DoLoadLevel("level_edit_temp.txt"); + GetModel()->DoLoadLevel("level_edit_temp.map"); mEditorState = EditorStateUnknown; } } else { @@ -615,14 +624,14 @@ void View::DrawUiPlayerDied() { if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 380, button_width, button_height)) { PopViewState(); - + // we have to take care when we are testing the level to not proceed to // the next level... if (GetViewState() == ViewStateEditor) { GetModel()->SetGameState(GameStatePaused); if (mEditorState == EditorStateTest) { - GetModel()->DoLoadLevel("level_edit_temp.txt"); + GetModel()->DoLoadLevel("level_edit_temp.map"); mEditorState = EditorStateUnknown; } } else { @@ -673,7 +682,6 @@ void View::DrawUiHighscore() { DrawPageTitle ("Highscores"); SelectFont ("console.ttf size=23"); - unsigned int entry_length = 32; float char_width, height; DrawGLStringMeasure ("M", &char_width, &height); @@ -766,7 +774,7 @@ void View::DrawUiEnterPlayername() { } void View::DrawUiEditor() { -// DrawPageTitle ("Editor"); + // DrawPageTitle ("Editor"); SelectFont ("console.ttf size=23"); // The close button @@ -776,46 +784,52 @@ void View::DrawUiEditor() { if (mEditorState == EditorStateTest) { mEditorState = EditorStateUnknown; - GetModel()->DoLoadLevel("level_edit_temp.txt"); + GetModel()->DoLoadLevel("level_edit_temp.map"); GetModel()->SetGameState(GameStatePaused); return; - } else { + } else if (!Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)){ PopViewState(); } } if (mEditorState != EditorStateTest) { - if (Engine::GUI::Button (2, "Add", 30, 20, 50, button_height)) { + if (Engine::GUI::CheckButton (2, "Add", mEditorState == EditorStateAddEntity, 30, 20, 50, button_height)) { mEditorState = EditorStateAddEntity; Engine::LogDebug ("Editor state now Add"); return; } - if (Engine::GUI::Button (3, "Del", 85, 20, 50, button_height)) { + if (Engine::GUI::CheckButton (3, "Del", mEditorState == EditorStateDelEntity, 85, 20, 50, button_height)) { mEditorState = EditorStateDelEntity; Engine::LogDebug ("Editor state now Del"); return; } - if (Engine::GUI::Button (4, "Move", 140, 20, 65, button_height)) { + if (Engine::GUI::CheckButton (4, "Move", mEditorState == EditorStateMoveEntity, 140, 20, 65, button_height)) { mEditorState = EditorStateMoveEntity; Engine::LogDebug ("Editor state now Move"); return; } - if (Engine::GUI::Button (5, "Spd", 210, 20, 50, button_height)) { + if (Engine::GUI::CheckButton (5, "Rot", mEditorState == EditorStateRotateEntity, 210, 20, 50, button_height)) { + mEditorState = EditorStateRotateEntity; + Engine::LogDebug ("Editor state now Move"); + return; + } + + if (Engine::GUI::CheckButton (6, "Spd", mEditorState == EditorStateEntityVelocity, 265, 20, 50, button_height)) { mEditorState = EditorStateEntityVelocity; Engine::LogDebug ("Editor state now Add"); return; } - if (Engine::GUI::Button (6, "Save", 265, 20, 65, button_height)) { + if (Engine::GUI::Button (7, "Save", 325, 20, 65, button_height)) { mEditorState = EditorStateSave; Engine::LogDebug ("Editor state now Save"); return; } - if (Engine::GUI::Button (7, "Load", 335, 20, 65, button_height)) { + if (Engine::GUI::Button (8, "Load", 400, 20, 65, button_height)) { mEditorState = EditorStateLoad; Engine::LogDebug ("Editor state now Load"); return; @@ -825,10 +839,10 @@ void View::DrawUiEditor() { // (i.e. a player entity and at least one asteroid) if (GetModel()->GetPlayerEntityId() != 0 && GetModel()->mAsteroids.size() > 0) { - if (Engine::GUI::Button (8, "Test", 405, 20, 64, button_height)) { + if (Engine::GUI::Button (9, "Test", 475, 20, 64, button_height)) { mEditorState = EditorStateTest; - GetModel()->DoSaveLevel("level_edit_temp.txt"); + GetModel()->DoSaveLevel("level_edit_temp.map"); GetModel()->SetGameState(GameStateRunning); } } @@ -843,7 +857,7 @@ void View::DrawUiEditor() { if (mEditorState == EditorStateAddEntity) { SelectFont ("console.ttf size=12"); - Engine::GUI::Label (9999, "Adding", 128, 16); +// Engine::GUI::Label (9999, "Adding", 128, 16); if (Engine::GUI::CheckKeyPressed(MouseButtonLeft)) { // if there is no entity so far we create a player, otherwise we create @@ -853,6 +867,7 @@ void View::DrawUiEditor() { if (GetModel()->GetPlayerEntityId() != Engine::NullEntityId) { entity = Engine::CreateEntity(GameEntityTypeAsteroid); entity->mPhysicState->SetVelocity (vector3d (0., 0., -1.)); + entity->mPhysicState->SetAngleVelocity ( (rand()/float(RAND_MAX) - 0.5) * 120); } else { entity = Engine::CreateEntity(GameEntityTypeShip); GetModel()->SetPlayerEntityId(entity->mId); @@ -866,7 +881,7 @@ void View::DrawUiEditor() { if (mEditorState == EditorStateDelEntity) { SelectFont ("console.ttf size=12"); - Engine::GUI::Label (9999, "Deleting", 128, 16); +// Engine::GUI::Label (9999, "Deleting", 128, 16); if (Engine::GUI::CheckKeyPressed(MouseButtonLeft)) { // Check if there is an entity near the given position @@ -881,7 +896,7 @@ void View::DrawUiEditor() { if (mEditorState == EditorStateMoveEntity) { SelectFont ("console.ttf size=12"); - Engine::GUI::Label (9999, "Moving", 128, 16); +// Engine::GUI::Label (9999, "Moving", 128, 16); if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) { // Check if there is an entity near the given position @@ -903,9 +918,44 @@ void View::DrawUiEditor() { } } + if (mEditorState == EditorStateRotateEntity) { + SelectFont ("console.ttf size=12"); + +// static vector3d mouse_start_rotate (0., 0., 0.); + static float start_angle = 0.; + if (mEditorEntityId == 0) { + if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) { + // Check if there is an entity near the given position + Engine::EntityBase* entity = Engine::GetEntityAt (mouse_world_pos); + + if (entity) { + mEditorEntityId = entity->mId; + vector3d mouse_start_rotate = mouse_world_pos - entity->mPhysicState->GetPosition(); + start_angle = atan2 (mouse_start_rotate[0], mouse_start_rotate[2]) * 180 / M_PI - entity->mPhysicState->mOrientation[1]; + } else { + mEditorEntityId = 0; + } + } + } + + if (mEditorEntityId != 0) { + Engine::EntityBase* entity = Engine::GetEntity (mEditorEntityId); + + vector3d new_velocity = mouse_world_pos - entity->mPhysicState->GetPosition(); + new_velocity[1] = 0.; + float angle = atan2 (new_velocity[0], new_velocity[2]) * 180. / M_PI; + + entity->mPhysicState->SetOrientation (vector3d (0., angle - start_angle, 0.)); + + if (!Engine::GUI::CheckKeyPress(MouseButtonLeft)) { + mEditorEntityId = 0; + } + } + } + if (mEditorState == EditorStateEntityVelocity) { SelectFont ("console.ttf size=12"); - Engine::GUI::Label (9999, "Velocity", 128, 16); +// Engine::GUI::Label (9999, "Velocity", 128, 16); if (mEditorEntityId == 0) { if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) { @@ -934,31 +984,41 @@ void View::DrawUiEditor() { if (mEditorState == EditorStateSave) { glColor3f (0.2, 0.2, 0.2); - Engine::GUI::DrawRoundedBlock (140, 150, screen_right - 280, 200); - Engine::GUI::Label (9999, "Saving", 128, 16); + Engine::GUI::DrawRoundedBlock (140, 150, screen_right - 280, 280); +// Engine::GUI::Label (9999, "Saving", 128, 16); SelectFont ("console.ttf size=23"); Engine::GUI::Label(51, "Filename: ", 145, 180); std::string level_name = GetModel()->GetLevelName(); glColor3f (1., 1., 1.); - if (Engine::GUI::LineEdit(52, 145, 210, level_name, 32)) + if (Engine::GUI::LineEdit(52, 150, 200, level_name, 31)) GetModel()->SetLevelName(level_name); - if (Engine::GUI::Button (54, "Save", 145, 300, button_width, button_height)) { - GetModel()->DoSaveLevel((level_name + std::string(".txt")).c_str()); + Engine::GUI::Label(53, "Author: ", 145, 250); + std::string level_author = GetModel()->GetLevelAuthor(); + if (Engine::GUI::LineEdit(531, 150, 270, level_author, 31)) + GetModel()->SetLevelAuthor(level_author); + + Engine::GUI::Label(54, "Level Title: ", 145, 320); + std::string level_title = GetModel()->GetLevelTitle(); + if (Engine::GUI::LineEdit(541, 150, 340, level_title, 31)) + GetModel()->SetLevelTitle(level_title); + + if (Engine::GUI::Button (55, "Save", 145, 380, button_width, button_height)) { + GetModel()->DoSaveLevel((level_name + std::string(".map")).c_str()); Engine::LogMessage ("Save"); mEditorState = EditorStateUnknown; } - if (Engine::GUI::Button (55, "Cancel", screen_right - 140 - 5 - button_width, 300, button_width, button_height)) + if (Engine::GUI::Button (56, "Cancel", screen_right - 140 - 5 - button_width, 380, button_width, button_height)) mEditorState = EditorStateUnknown; } if (mEditorState == EditorStateLoad) { glColor3f (0.2, 0.2, 0.2); Engine::GUI::DrawRoundedBlock (140, 150, screen_right - 280, 200); - Engine::GUI::Label (9999, "Loading", 128, 16); +// Engine::GUI::Label (9999, "Loading", 128, 16); SelectFont ("console.ttf size=23"); Engine::GUI::Label(51, "Filename: ", 145, 180); @@ -968,9 +1028,9 @@ void View::DrawUiEditor() { if (Engine::GUI::LineEdit(52, 145, 210, level_name, 32)) GetModel()->SetLevelName(level_name); - if (Engine::FileExists (level_name + std::string(".txt"))) { + if (Engine::FileExists (level_name + std::string(".map"))) { if (Engine::GUI::Button (54, "Load", 145, 300, button_width, button_height)) { - GetModel()->DoLoadLevel((level_name + std::string(".txt")).c_str()); + GetModel()->DoLoadLevel((level_name + std::string(".map")).c_str()); Engine::LogMessage ("Load"); mEditorState = EditorStateUnknown; } @@ -980,6 +1040,11 @@ void View::DrawUiEditor() { mEditorState = EditorStateUnknown; } + if (Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)) { + if (mEditorState != EditorStateUnknown) + mEditorState = EditorStateUnknown; + } + if (Engine::GUI::CheckKeyPressed(MouseButtonRight) || Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) { mEditorState = EditorStateUnknown; diff --git a/asteroids/View.h b/asteroids/View.h index 84bfc49..58f9cfd 100644 --- a/asteroids/View.h +++ b/asteroids/View.h @@ -31,6 +31,44 @@ class View : public Engine::ViewBase { public: virtual ~View() {}; + /** \brief Pushes the given state onto mViewStateStack */ + void PushViewState (const ViewState state) { + Engine::LogDebug ("Pushing ViewState %s", GetStringViewState(state)); + mViewStateStack.push(state); + + // Fire the view state change event to properly clear the IMGUI state + Engine::EventBasePtr changeviewstate_event (new Engine::EventBase()); + changeviewstate_event->mEventType = EventChangeViewState; + QueueEvent (changeviewstate_event); + } + /** \brief Returns the current ViewState */ + ViewState GetViewState () { + return mViewStateStack.top(); + } + /** \brief Removes the top element of the current ViewState stack */ + void PopViewState () { + // Warning: you must not query for an invalid enum with + // GetStringENUM_NAME! + std::string popped_name = GetStringViewState(mViewStateStack.top()); + mViewStateStack.pop(); + + std::string current_name ("None"); + if (mViewStateStack.size() > 0) + current_name = GetStringViewState(mViewStateStack.top()); + + Engine::LogDebug("Popped ViewState: %s current %s remaining: %u", popped_name.c_str(), current_name.c_str(), mViewStateStack.size()); + + if (mViewStateStack.size() == 0) { + Engine::LogDebug ("No ViewState on the stack! Quitting."); + Engine::RunCommand("quit"); + } + + // Fire the view state change event to properly clear the IMGUI state + Engine::EventBasePtr changeviewstate_event (new Engine::EventBase()); + changeviewstate_event->mEventType = EventChangeViewState; + QueueEvent (changeviewstate_event); + } + protected: /** \brief Initializes the system */ virtual int OnInit (int argc, char* argv[]); @@ -74,38 +112,6 @@ class View : public Engine::ViewBase { /** \brief The ViewState stack that is used for non-linear menus */ std::stack mViewStateStack; - /** \brief Pushes the given state onto mViewStateStack */ - void PushViewState (const ViewState state) { - Engine::LogDebug ("Pushing ViewState %s", GetStringViewState(state)); - mViewStateStack.push(state); - - // Fire the view state change event to properly clear the IMGUI state - Engine::EventBasePtr changeviewstate_event (new Engine::EventBase()); - changeviewstate_event->mEventType = EventChangeViewState; - QueueEvent (changeviewstate_event); - } - /** \brief Returns the current ViewState */ - ViewState GetViewState () { - return mViewStateStack.top(); - } - /** \brief Removes the top element of the current ViewState stack */ - void PopViewState () { - // Warning: you must not query for an invalid enum with - // GetStringENUM_NAME! - std::string popped_name = GetStringViewState(mViewStateStack.top()); - mViewStateStack.pop(); - - std::string current_name ("None"); - if (mViewStateStack.size() > 0) - current_name = GetStringViewState(mViewStateStack.top()); - - Engine::LogDebug("Popped ViewState: %s current %s remaining: %u", popped_name.c_str(), current_name.c_str(), mViewStateStack.size()); - - // Fire the view state change event to properly clear the IMGUI state - Engine::EventBasePtr changeviewstate_event (new Engine::EventBase()); - changeviewstate_event->mEventType = EventChangeViewState; - QueueEvent (changeviewstate_event); - } /** \brief Removes all elements of the ViewState stack */ void ResetViewState() { Engine::LogDebug ("Resetting ViewState stack"); @@ -138,6 +144,7 @@ class View : public Engine::ViewBase { EditorStateAddEntity, EditorStateDelEntity, EditorStateMoveEntity, + EditorStateRotateEntity, EditorStateSave, EditorStateEntityVelocity, EditorStateLoad, diff --git a/asteroids/editormain.cc b/asteroids/editormain.cc new file mode 100644 index 0000000..6417c80 --- /dev/null +++ b/asteroids/editormain.cc @@ -0,0 +1,196 @@ +#include +#include + +#include "Engine.h" + +#include "Controller.h" +#include "View.h" +#include "Model.h" +#include "Physics.h" +#include "EntityFactory.h" + +#include "Game.h" + +#include + +#ifdef WIN32 +#include +#endif + +using namespace std; + +/* Returns a path where files such as logs and config files can be + * written to + */ +std::string create_user_path () { + std::string result_dir = "."; + std::string test_file_path = result_dir; + + // first we check in $HOME/.fysxasteroids +#ifdef WIN32 + size_t env_retval; + char env_home_dir[255]; + getenv_s (&env_retval, env_home_dir, 255, "HOME"); + if (env_retval == 0) { + env_home_dir[0] = '\0'; + } +#else + char* env_home_dir = getenv("HOME"); +#endif + cout << "Yo4 " << env_home_dir << endl; + + result_dir = env_home_dir; + result_dir += "/.fysxasteroids"; + + cout << "Path = " << result_dir << endl; + boost::filesystem::path result_dir_path(result_dir); + if(!boost::filesystem::is_directory (result_dir_path)) { + if (!boost::filesystem::create_directory(result_dir_path)) { + cerr << "Warning: could not create user data directory " << result_dir<< endl; + result_dir = ""; + } + } + + test_file_path = result_dir; + test_file_path += "/game.log"; + ofstream test_file (test_file_path.c_str(), ios_base::app); + if (!test_file) { + test_file.close(); + cerr << "Warning: user data directory not writable! " << result_dir << endl; + result_dir = ""; + } else { + test_file.close(); + return result_dir; + } + + // then we check the local directory + result_dir = "."; + test_file_path = result_dir; + test_file_path += "/game.log"; + test_file.open (test_file_path.c_str(), ios_base::out); + if (test_file) { + test_file.close(); + return result_dir; + } else { + cerr << "Warning could not find suitable user data directory" << endl; + result_dir = ""; + } + test_file.close(); + + return result_dir; +} + +std::string find_game_data_dir () { + std::string result; + + std::vector paths; + paths.push_back("."); + paths.push_back("/usr/local/share/fysxasteroids"); + paths.push_back("/usr/share/fysxasteroids"); + + std::vector::iterator iter = paths.begin(); + for (iter; iter != paths.end(); iter++) { + std::string test_path = *iter; + + if (!boost::filesystem::is_directory(test_path + "/data/fonts")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/levels")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/sounds")) + continue; + if (!boost::filesystem::is_directory(test_path + "/data/textures")) + continue; + + break; + } + + if (iter != paths.end()) + return *iter; + + cerr << "Could not find game data" << endl; + return result; +} + +int main (int argc, char* argv[]) { + cout << "Game Start" << endl; + +#ifdef WIN32 + HWND hWnd = GetConsoleWindow(); + ShowWindow( hWnd, SW_HIDE ); +#endif + + Engine::Engine engine; + + engine.SetEntityFactory (new asteroids::EntityFactory); + engine.SetController (new asteroids::Controller); + engine.SetModel (new asteroids::Model); + engine.SetPhysics (new asteroids::Physics); + engine.SetView (new asteroids::View); + + SetLogPrintLevel (Engine::LogLevelMessage); + + // we assume the user path to be local folder + std::string user_path; + +#ifdef WIN32 + user_path = "."; +#else + user_path = create_user_path(); +#endif + + std::string log_file_path = user_path; + log_file_path += "/editor.log"; + + cout << "User Data Dir = " << user_path << endl; + engine.SetUserDataPath (user_path); + Engine::SetLogFilename (log_file_path.c_str()); + + std::string game_data_path = find_game_data_dir(); + engine.SetGameDataPath (game_data_path); + cout << "Game Data Dir = " << game_data_path << endl; + + if (engine.Init (argc, argv) != 0) { + cout << "Could not start engine!" << endl; + exit (-1); + } + + Engine::SetMusicVolume (0.); + + // Load the icon + Uint32 colorkey; + SDL_Surface *image = NULL; + image = SDL_LoadBMP("./data/textures/icon.bmp"); + if (!image) + Engine::LogWarning("Could not load icon: ./data/textures/icon.bmp"); + else + SDL_WM_SetIcon(image,NULL); + + SDL_WM_SetCaption("fysxasteroids -Editor-","fysxasteroids -Editor-"); + + engine.GetView()->SetGridSize (8,8); + + /// \todo get rid of the world settings in asteroids + dynamic_cast(engine.GetPhysics())->SetWorldSize (26, 20); + engine.GetPhysics()->SetWorldBounds (vector3d (-13, 0, -10), vector3d (13, 0, 10)); + engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeX); + engine.GetPhysics()->EnableWorldWarp(Engine::PhysicsBase::WorldWarpModeZ); + + // run the default commands and load the configuration + Engine::RunCommand ("exec asteroids.rc"); + Engine::RunCommand ("exec config.rc"); + Engine::SetMusicVolume (0.); + + asteroids::GetView()->PopViewState(); + asteroids::GetView()->PushViewState(asteroids::ViewStateEditor); + + engine.MainLoop (); + + SDL_WM_SetIcon(NULL,NULL); + SDL_FreeSurface (image); + + engine.Destroy (); + + cout << "Game Quit" << endl; + + return 0; +} diff --git a/asteroids/main.cc b/asteroids/main.cc index 1003ca3..eaf5ebd 100644 --- a/asteroids/main.cc +++ b/asteroids/main.cc @@ -104,7 +104,7 @@ int main (int argc, char* argv[]) { HWND hWnd = GetConsoleWindow(); ShowWindow( hWnd, SW_HIDE ); #endif - + Engine::Engine engine; engine.SetEntityFactory (new asteroids::EntityFactory); @@ -115,8 +115,15 @@ int main (int argc, char* argv[]) { SetLogPrintLevel (Engine::LogLevelMessage); + std::string user_path = "."; + std::string game_data_path = "."; + +#ifndef WIN32 + user_path = create_user_path(); + game_data_path = find_game_data_dir(); +#endif + // we assume the user path to be local folder - std::string user_path = create_user_path(); std::string log_file_path = user_path; log_file_path += "/game.log"; @@ -124,7 +131,6 @@ int main (int argc, char* argv[]) { engine.SetUserDataPath (user_path); Engine::SetLogFilename (log_file_path.c_str()); - std::string game_data_path = find_game_data_dir(); engine.SetGameDataPath (game_data_path); cout << "Game Data Dir = " << game_data_path << endl; @@ -142,7 +148,7 @@ int main (int argc, char* argv[]) { else SDL_WM_SetIcon(image,NULL); - SDL_WM_SetCaption("Asteroids -BETA1-","Asteroids -BETA 1-"); + SDL_WM_SetCaption("fysxasteroids -BETA 2-","fysxasteroids -BETA 2-"); engine.GetView()->SetGridSize (8,8); @@ -156,6 +162,8 @@ int main (int argc, char* argv[]) { Engine::RunCommand ("exec asteroids.rc"); Engine::RunCommand ("exec config.rc"); + Engine::PlayMusic (Engine::GetResourceFullPath("/data/sounds/intro_music.ogg")); + engine.MainLoop (); // save the configuration diff --git a/data/levels/level1.txt b/data/levels/level1.txt index 86e84ac..4137942 100644 --- a/data/levels/level1.txt +++ b/data/levels/level1.txt @@ -1,5 +1,7 @@ # Format # +Title Let's get started... +Author Martin Felis GameEntityTypeShip 1 0 0 0 0 90 0 0 0 0 0 GameEntityTypeAsteroid 0 5 0 -2 0 0 0 -0.2 0 -0.1 -10 GameEntityTypeAsteroid 0 -2 0 2 0 0 0 0.3 0 -0.4 5 diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index d75232b..daf86d0 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -37,6 +37,7 @@ SET ( ENGINE_SRCS Engine.cc Logging.cc EnumStrings.cc + Utils.cc ) INCLUDE_DIRECTORIES ( diff --git a/engine/ControllerBase.cc b/engine/ControllerBase.cc index 4a12165..39036d8 100644 --- a/engine/ControllerBase.cc +++ b/engine/ControllerBase.cc @@ -188,6 +188,13 @@ bool ControllerBase::OnKeyDown (const SDL_keysym &keysym) { /** \brief Keyboard processing */ bool ControllerBase::OnKeyUp (const SDL_keysym &keysym) { SetButtonState (keysym.sym, false); + + if (keysym.sym == SDLK_F12) { + if (mView->GetIsFullscreen()) + mView->SetFullscreen(false); + else + mView->SetFullscreen(true); + } uistate.keypressed_set.insert (keysym.sym); diff --git a/engine/Engine.h b/engine/Engine.h index 0c89624..1854074 100644 --- a/engine/Engine.h +++ b/engine/Engine.h @@ -20,6 +20,7 @@ #include "Module.h" #include "EngineEnums.h" +#include "Utils.h" // Some ugly #defines diff --git a/engine/IMGUIControls.cc b/engine/IMGUIControls.cc index 9461495..32b5396 100644 --- a/engine/IMGUIControls.cc +++ b/engine/IMGUIControls.cc @@ -257,6 +257,133 @@ bool Button (int id, const char* caption, int x, int y, int w, int h) { controller->uistate.hotitem = controller->uistate.lastwidget; controller->uistate.last_keysym = SDLK_CLEAR; break; + case SDLK_TAB: + controller->uistate.last_keysym = SDLK_CLEAR; + controller->uistate.last_unicode = 0; + controller->uistate.hotitem = 0; + controller->uistate.kbditem = 0; + break; + case SDLK_RETURN: + controller->uistate.last_keysym = SDLK_CLEAR; + // As we (probably) exit the current set of widgets, we have to clear + // the uistate.kbditem value. + controller->uistate.kbditem = 0; + return true; + break; + } + } + + controller->uistate.lastwidget = id; + + return false; +} + +bool CheckButton (int id, const char* caption, bool checked, int x, int y, int w, int h) { + controller = EngineGetController(); + assert (controller); + +// LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem); + + // Check for hotness + if (regionhit (x, y, w, h)) { + controller->uistate.hotitem = id; + if (controller->uistate.activeitem == 0 + && controller->GetButtonState(MouseButtonLeft)) + controller->uistate.activeitem = id; + } + + // If nothing is selected + if (controller->uistate.hotitem != 0) { + controller->uistate.kbditem = controller->uistate.hotitem; + } + + if (controller->uistate.kbditem == 0) { + controller->uistate.hotitem = id; + controller->uistate.kbditem = id; + } + + // Render + if (checked) { + glColor3f (0.91, 0.84, 0.); + DrawRoundedBlock (x - 2, y - 2, w + 4, h + 4); + } + + glColor3f (0.2, 0.2, 0.2); + + if (controller->uistate.hotitem == id + || controller->uistate.kbditem == id) { + if (controller->uistate.activeitem == id) { + glColor3f (0.8, 0.8, 0.8); + DrawRoundedBlock (x, y, w, h); + } else { + glColor3f (0.7, 0.7, 0.7); + DrawRoundedBlock (x, y, w, h); + } + } else { + glColor3f (0.4, 0.4, 0.4); + DrawRoundedBlock (x, y, w, h); + } + + // Caption + if (caption != NULL) { + float width, height; + view = EngineGetView (); + assert (view); + + // we have to load the font prior to measuring it + SelectFont("console.ttf size=23"); + view->DrawGLStringMeasure(caption, &width, &height); + + float xpos = x + w * 0.5 - width * 0.5; + float ypos = y + h * 0.5 - height * 0.5; + // LogDebug ("measure '%s' width = %f height = %f", caption, width, height); + + if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { + SelectFont("console.ttf size=23 color=#666666"); + view->DrawGLString( xpos - 2., ypos + 2., caption); + SelectFont("console.ttf size=23 color=#ffffff"); + view->DrawGLString( xpos, ypos, caption); + } else { + SelectFont("console.ttf size=23 color=#4d4d4d"); + view->DrawGLString( xpos - 2., ypos + 2., caption); + SelectFont("console.ttf size=23 color=#b3b3b3"); + view->DrawGLString( xpos, ypos, caption); + } + } + + // Mouse Logic + if (controller->GetButtonState(MouseButtonLeft) == false + && controller->uistate.hotitem == id + && controller->uistate.activeitem == id) { + controller->uistate.lastwidget = id; + + return true; + } + + // Keyboard Logic + if (controller->uistate.kbditem == id) { + // Any unicode keys that were sent get cleared + controller->uistate.last_unicode = 0; + + // We have to make sure, that we always clear the uistate.last_keysym + // value, otherwise the same action might be repeated over and over. + switch (controller->uistate.last_keysym) { + case SDLK_DOWN: + controller->uistate.kbditem = 0; + controller->uistate.hotitem = 0; + controller->uistate.last_keysym = SDLK_CLEAR; + break; + case SDLK_UP: + controller->uistate.kbditem = controller->uistate.lastwidget; + controller->uistate.hotitem = controller->uistate.lastwidget; + controller->uistate.last_keysym = SDLK_CLEAR; + break; + case SDLK_TAB: + controller->uistate.last_keysym = SDLK_CLEAR; + controller->uistate.last_unicode = 0; + controller->uistate.hotitem = 0; + controller->uistate.kbditem = 0; + break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; // As we (probably) exit the current set of widgets, we have to clear @@ -363,6 +490,13 @@ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxleng controller->uistate.kbditem = 0; return false; break; + case SDLK_TAB: + controller->uistate.last_keysym = SDLK_CLEAR; + controller->uistate.last_unicode = 0; + controller->uistate.hotitem = 0; + controller->uistate.kbditem = 0; + return false; + break; case SDLK_RETURN: controller->uistate.last_keysym = SDLK_CLEAR; controller->uistate.last_unicode = 0; diff --git a/engine/IMGUIControls.h b/engine/IMGUIControls.h index 7f8ef10..c27b523 100644 --- a/engine/IMGUIControls.h +++ b/engine/IMGUIControls.h @@ -34,6 +34,8 @@ void LabelCentered (int id, const char* caption, int x, int y); */ bool Button (int id, const char* caption, int x, int y, int w, int h); +bool CheckButton (int id, const char* caption, bool state, int x, int y, int w, int h); + bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxlength); float VerticalSlider (int id, int x, int y, int w, int h, float min_value, float max_value, float &value); diff --git a/engine/PhysicsBase.cc b/engine/PhysicsBase.cc index 4da6627..6c3e7c3 100644 --- a/engine/PhysicsBase.cc +++ b/engine/PhysicsBase.cc @@ -99,20 +99,20 @@ int PhysicsBase::Simulate (float msec, ModelBase* model) { // if the timestep is too small we simply resolve the collision and // do not move beforehang LogDebug ("Time step too small ==> Resolving Step immediately"); - ResolveCollision (0., entity_a_id, entity_b_id, info); // Send the collision event to the Model (if we have one) if (model) { model->SendEntityCollisionEvent (entity_a_id, entity_b_id, info.time, info.point, info.normal); } + ResolveCollision (0., entity_a_id, entity_b_id, info); // collision_remaining_step -= 1.0e-4; } else { Move (info.time * (1 - alpha)); - LogDebug ("Resolving Step between %u and %u t = %f alpha = %f", entity_a_id, entity_b_id, info.time, alpha); - ResolveCollision (info.time * (1 - alpha), entity_a_id, entity_b_id, info); // Send the collision event to the Model (if we have one) if (model) { model->SendEntityCollisionEvent (entity_a_id, entity_b_id, info.time, info.point, info.normal); } + LogDebug ("Resolving Step between %u and %u t = %f alpha = %f", entity_a_id, entity_b_id, info.time, alpha); + ResolveCollision (info.time * (1 - alpha), entity_a_id, entity_b_id, info); collision_remaining_step -= info.time * (1 - alpha); } diff --git a/engine/Utils.cc b/engine/Utils.cc new file mode 100644 index 0000000..3b922ae --- /dev/null +++ b/engine/Utils.cc @@ -0,0 +1,38 @@ +#include "Utils.h" + +#include +#include +#include + +#include +#include + +std::string strip_whitespaces (const std::string input_str) { + std::string result = input_str.substr(input_str.find_first_not_of (" \t\n\r"), input_str.size()); + return result.substr (0, result.find_last_not_of(" \t\n\r") + 1); +} + +bool create_dir (const std::string &dir_str) { + boost::filesystem::path dir_str_path(dir_str); + if(!boost::filesystem::is_directory (dir_str_path)) { + if (!boost::filesystem::create_directory(dir_str_path)) { + std::cerr << "Warning: could not create directory " << dir_str<< std::endl; + return false; + } + } + + std::string test_file_path = dir_str; + test_file_path += "/fysxasteroids_write_test_delete_me.txt"; + std::ofstream test_file (test_file_path.c_str(), std::ios_base::app); + if (!test_file) { + test_file.close(); + std::cerr << "Warning: directory not writable! " << dir_str << std::endl; + return false; + } else { + test_file.close(); + boost::filesystem::remove (test_file_path); + return true; + } + + return false; +} diff --git a/engine/Utils.h b/engine/Utils.h new file mode 100644 index 0000000..a986bca --- /dev/null +++ b/engine/Utils.h @@ -0,0 +1,9 @@ +#ifndef _UTILS_H +#define _UTILS_H + +#include + +std::string strip_whitespaces (const std::string input_str); +bool create_dir (const std::string &dir_str); + +#endif /* _UTILS_H */ diff --git a/engine/ViewBase.cc b/engine/ViewBase.cc index dedf2be..fc69333 100644 --- a/engine/ViewBase.cc +++ b/engine/ViewBase.cc @@ -43,13 +43,21 @@ int ViewBase::OnInit (int argc, char* argv[]) { mWindowHeight = VIEW_DEFAULT_HEIGHT; mWindowWidth = VIEW_DEFAULT_WIDTH; + // get and save the screen resolution + mScreenHeight = SDL_GetVideoInfo()->current_h; + mScreenWidth = SDL_GetVideoInfo()->current_w; + + mDrawFullscreen = false; + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); - if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL | SDL_RESIZABLE ) == 0 ) { + if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL ) == 0 ) { LogError ("Video mode set failed: %s", SDL_GetError ()); exit (-1); } + LogMessage ("Resolution is %dx%d", SDL_GetVideoInfo()->current_w, SDL_GetVideoInfo()->current_h); + InitGL (); Resize (mWindowWidth, mWindowHeight); @@ -187,6 +195,28 @@ void ViewBase::PostDraw() { SDL_GL_SwapBuffers (); } +void ViewBase::SetFullscreen (bool fullscreen) { + if (fullscreen && mDrawFullscreen) + return; + + if (!fullscreen && !mDrawFullscreen) + return; + + if (mDrawFullscreen) { + mWindowHeight = VIEW_DEFAULT_HEIGHT; + mWindowWidth = VIEW_DEFAULT_WIDTH; + + mDrawFullscreen = false; + } else { + mWindowWidth = mScreenWidth; + mWindowHeight = mScreenHeight; + + mDrawFullscreen = true; + } + + Resize (mWindowWidth, mWindowHeight); +} + /* Fonts */ /** \brief Parses font specifications strings into its values @@ -401,7 +431,12 @@ void ViewBase::Resize (int width, int height) { * workaround, however since I do not yet run SDL 1.3 I hold on to this. * See http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-November/067306.html */ - if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL | SDL_RESIZABLE ) == 0 ) { + Uint32 video_flags = SDL_OPENGL; + + if (mDrawFullscreen) + video_flags = video_flags | SDL_FULLSCREEN; + + if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, video_flags) == 0 ) { LogError ("Video mode set failed: %s", SDL_GetError ()); exit (-1); } diff --git a/engine/ViewBase.h b/engine/ViewBase.h index 36c21f5..1f27cc5 100644 --- a/engine/ViewBase.h +++ b/engine/ViewBase.h @@ -26,6 +26,9 @@ class ViewBase : public Module{ /** \brief Resizes the View */ void Resize (int width, int height); + /** \brief Switches to fullscreen */ + void SetFullscreen (bool fullscreen); + bool GetIsFullscreen () { return mDrawFullscreen; }; /** \brief Performs actions before drawing (e.g. timer stuff) */ void PreDraw(); @@ -96,6 +99,8 @@ class ViewBase : public Module{ /** \brief Draws orthographic overlay*/ void DrawOverlay2D (); + bool mDrawFullscreen; + /** \brief Draws a grid of 16 x 16 tiles */ void DrawGrid (); bool mDrawGrid; @@ -114,6 +119,13 @@ class ViewBase : public Module{ unsigned int mWindowHeight; /** \brief The width of the canvas we're drawing on */ unsigned int mWindowWidth; + + /** \brief The height of the screen in pixels */ + unsigned int mScreenHeight; + /** \brief The width of the screen in pixels */ + unsigned int mScreenWidth; + + /** \brief Stores the current frame rate */ int mFrameRate; diff --git a/engine/libraries/oglft/liboglft/CMakeLists.txt b/engine/libraries/oglft/liboglft/CMakeLists.txt index e2c3cf0..5db7064 100644 --- a/engine/libraries/oglft/liboglft/CMakeLists.txt +++ b/engine/libraries/oglft/liboglft/CMakeLists.txt @@ -13,7 +13,7 @@ ENDIF( DESIRED_QT_VERSION EQUAL 3) INCLUDE_DIRECTORIES( ${FREETYPE2_INCLUDE_DIR} ) -INCLUDE_DIRECTORIES( ${PROJECT_BINARY_DIR} ) +INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR} ) CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/OGLFT.h.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/OGLFT.h"