initial commit

main
Martin Felis (berta) 2010-04-05 23:38:59 +02:00
commit e7e0b39e82
150 changed files with 28636 additions and 0 deletions

20
.hgignore Normal file
View File

@ -0,0 +1,20 @@
syntax:glob
CMakeCache.txt
CMakeFiles
cmake_install.cmake
install_manifest.txt
debug
tags
Makefile
start
runtests
hasteroids
./doc/html/*
*.log
*.a
*.o
*.so
*.swp

45
CMake/FindCEGUI.cmake Normal file
View File

@ -0,0 +1,45 @@
# Tries to find CEGUI (http://CEGUI.bespin.org) a simple and fast
# network library by Lee Salzman
#
SET (CEGUI_FOUND FALSE)
FIND_PATH (CEGUI_INCLUDE_DIR CEGUI.h /usr/include/CEGUI
/usr/local/include/CEGUI $ENV{CEGUI_PATH}/include/CEGUI
$ENV{CEGUI_INCLUDE_PATH}/CEGUI)
FIND_LIBRARY (CEGUI_BASE_LIBRARIES NAMES CEGUIBase PATHS /usr/lib /usr/local/lib $ENV{CEGUI_PATH} $ENV{CEGUI_PATH}/lib ENV{CEGUI_LIBRARY_PATH})
FIND_LIBRARY (CEGUI_RENDERER_LIBRARIES NAMES CEGUIOpenGLRenderer PATHS /usr/lib /usr/local/lib $ENV{CEGUI_PATH} $ENV{CEGUI_PATH}/lib ENV{CEGUI_LIBRARY_PATH})
SET ( CEGUI_LIBRARIES FALSE )
IF ( CEGUI_BASE_LIBRARIES AND CEGUI_RENDERER_LIBRARIES )
SET ( CEGUI_LIBRARIES
${CEGUI_BASE_LIBRARIES}
${CEGUI_RENDERER_LIBRARIES}
)
ENDIF ( CEGUI_BASE_LIBRARIES AND CEGUI_RENDERER_LIBRARIES )
MESSAGE (STATUS "CEGUI Libraries: ${CEGUI_LIBRARIES}")
IF (CEGUI_INCLUDE_DIR AND CEGUI_LIBRARIES)
SET (CEGUI_FOUND TRUE)
ENDIF (CEGUI_INCLUDE_DIR AND CEGUI_LIBRARIES)
IF (CEGUI_FOUND)
IF (NOT CEGUI_FIND_QUIETLY)
MESSAGE(STATUS "Found CEGUI: ${CEGUI_LIBRARIES}")
ENDIF (NOT CEGUI_FIND_QUIETLY)
ELSE (CEGUI_FOUND)
IF (CEGUI_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find CEGUI")
ENDIF (CEGUI_FIND_REQUIRED)
ENDIF (CEGUI_FOUND)
MARK_AS_ADVANCED (
CEGUI_INCLUDE_DIR
CEGUI_BASE_LIBRARIES
CEGUI_RENDERER_LIBRARIES
CEGUI_LIBRARIES
)

28
CMake/FindENET.cmake Normal file
View File

@ -0,0 +1,28 @@
# Tries to find ENET (http://enet.bespin.org) a simple and fast
# network library by Lee Salzman
#
SET (ENET_FOUND FALSE)
FIND_PATH (ENET_INCLUDE_DIR enet/enet.h /usr/include/ /usr/local/include/ $ENV{ENET_PATH}/include $ENV{ENET_INCLUDE_PATH})
FIND_LIBRARY (ENET_LIBRARIES NAMES enet PATHS /usr/lib /usr/local/lib $ENV{ENET_PATH} $ENV{ENET_PATH}/lib ENV{ENET_LIBRARY_PATH})
IF (ENET_INCLUDE_DIR AND ENET_LIBRARIES)
SET (ENET_FOUND TRUE)
ENDIF (ENET_INCLUDE_DIR AND ENET_LIBRARIES)
IF (ENET_FOUND)
IF (NOT ENET_FIND_QUIETLY)
MESSAGE(STATUS "Found ENET: ${ENET_LIBRARIES}")
ENDIF (NOT ENET_FIND_QUIETLY)
ELSE (ENET_FOUND)
IF (ENET_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find ENET")
ENDIF (ENET_FIND_REQUIRED)
ENDIF (ENET_FOUND)
MARK_AS_ADVANCED (
ENET_INCLUDE_DIR
ENET_LIBRARIES
)

28
CMake/FindFreeType2.cmake Normal file
View File

@ -0,0 +1,28 @@
# Tries to find FREETYPE2 (http://freetype2.bespin.org) a simple and fast
# network library by Lee Salzman
#
SET (FREETYPE2_FOUND FALSE)
FIND_PATH (FREETYPE2_INCLUDE_DIR freetype/freetype.h /usr/include/ /usr/local/include/ /usr/include/freetype2 /usr/local/include/freetype2 $ENV{FREETYPE2_PATH}/include $ENV{FREETYPE2_INCLUDE_PATH})
FIND_LIBRARY (FREETYPE2_LIBRARIES NAMES freetype PATHS /usr/lib /usr/local/lib $ENV{FREETYPE2_PATH} $ENV{FREETYPE2_PATH}/lib ENV{FREETYPE2_LIBRARY_PATH})
IF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES)
SET (FREETYPE2_FOUND TRUE)
ENDIF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES)
IF (FREETYPE2_FOUND)
IF (NOT FREETYPE2_FIND_QUIETLY)
MESSAGE(STATUS "Found FREETYPE2: ${FREETYPE2_LIBRARIES}")
ENDIF (NOT FREETYPE2_FIND_QUIETLY)
ELSE (FREETYPE2_FOUND)
IF (FREETYPE2_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find FREETYPE2")
ENDIF (FREETYPE2_FIND_REQUIRED)
ENDIF (FREETYPE2_FOUND)
MARK_AS_ADVANCED (
FREETYPE2_INCLUDE_DIR
FREETYPE2_LIBRARIES
)

View File

@ -0,0 +1,28 @@
# - Try to find UnitTest++
#
#
SET (UNITTEST++_FOUND FALSE)
FIND_PATH (UNITTEST++_INCLUDE_DIR UnitTest++.h /usr/include/unittest++ /usr/local/include/unittest++ $ENV{UNITTESTXX_PATH}/src $ENV{UNITTESTXX_INCLUDE_PATH})
FIND_LIBRARY (UNITTEST++_LIBRARY NAMES UnitTest++ PATHS /usr/lib /usr/local/lib $ENV{UNITTESTXX_PATH} ENV{UNITTESTXX_LIBRARY_PATH})
IF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY)
SET (UNITTEST++_FOUND TRUE)
ENDIF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY)
IF (UNITTEST++_FOUND)
IF (NOT UnitTest++_FIND_QUIETLY)
MESSAGE(STATUS "Found UnitTest++: ${UNITTEST++_LIBRARY}")
ENDIF (NOT UnitTest++_FIND_QUIETLY)
ELSE (UNITTEST++_FOUND)
IF (UnitTest++_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find UnitTest++")
ENDIF (UnitTest++_FIND_REQUIRED)
ENDIF (UNITTEST++_FOUND)
MARK_AS_ADVANCED (
UNITTEST++_INCLUDE_DIR
UNITTEST++_LIBRARY
)

28
CMake/Findfysx.cmake Normal file
View File

@ -0,0 +1,28 @@
# - Try to find FYSX
#
#
SET (FYSX_FOUND FALSE)
FIND_PATH (FYSX_INCLUDE_DIR fysx.h /usr/include/ /usr/local/include/ $ENV{FYSX_PATH}/include $ENV{FYSX_INCLUDE_PATH})
FIND_LIBRARY (FYSX_LIBRARIES NAMES fysx PATHS /usr/lib /usr/local/lib $ENV{FYSX_PATH} $ENV{FYSX_PATH}/lib ENV{FYSX_LIBRARY_PATH})
IF (FYSX_INCLUDE_DIR AND FYSX_LIBRARIES)
SET (FYSX_FOUND TRUE)
ENDIF (FYSX_INCLUDE_DIR AND FYSX_LIBRARIES)
IF (FYSX_FOUND)
IF (NOT FYSX_FIND_QUIETLY)
MESSAGE(STATUS "Found FYSX: ${FYSX_LIBRARIES}")
ENDIF (NOT FYSX_FIND_QUIETLY)
ELSE (FYSX_FOUND)
IF (FYSX_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find FYSX")
ENDIF (FYSX_FIND_REQUIRED)
ENDIF (FYSX_FOUND)
MARK_AS_ADVANCED (
FYSX_INCLUDE_DIR
FYSX_LIBRARIES
)

43
CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake )
# FIND_PACKAGE (Cal3D REQUIRED)
INCLUDE_DIRECTORIES (
src/
engine/
include/
engine/libraries/mathlib/
engine/libraries/coll2d/include
engine/libraries/oglft/
${FREETYPE2_INCLUDE_DIR}
)
SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES
LINKER_LANGUAGE CXX
)
ADD_SUBDIRECTORY ( engine )
SET ( ASTEROIDS_SOURCES
asteroids/AsteroidEntity.cc
asteroids/Controller.cc
asteroids/ControllerCommands.cc
asteroids/EntityFactory.cc
asteroids/EnumToString.cc
asteroids/main.cc
asteroids/Model.cc
asteroids/ModelCommands.cc
asteroids/Physics.cc
asteroids/RocketEntity.cc
asteroids/ShipEntity.cc
asteroids/View.cc
asteroids/MenuOverlay.cc
)
ADD_EXECUTABLE ( hasteroids ${ASTEROIDS_SOURCES} )
TARGET_LINK_LIBRARIES ( hasteroids
Engine
)

1563
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
#include "Engine.h"
#include "ModelBase.h"
#include "AsteroidEntity.h"
#include "Controller.h"
#include "Model.h"
#include "EntityBase.h"
#include "coll2d.h"
namespace asteroids {
bool AsteroidEntity::CollisionEvent (Engine::EntityBase* entity_state) {
GameEntityType other_type = (GameEntityType) entity_state->mType;
Engine::LogMessage ("CONTACT OF AN ASTEROID");
if (other_type == GameEntityTypeRocket) {
// First remove the rocket
Engine::KillEntity (entity_state->mId);
Engine::LogMessage ("You killed an ASTEROID!");
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->mSubAsteroidsCount = 0;
asteroid->mPhysicState->mRadius = 2. * mPhysicState->mRadius / mSubAsteroidsCount;
static_cast<coll2d::Sphere*>(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);
return true;
} else if (other_type == GameEntityTypeRocket) {
return false;
}
return false;
}
}

View File

@ -0,0 +1,33 @@
#ifndef _ASTEROIDENTITY_H
#define _ASTEROIDENTITY_H
#include "EntityBase.h"
#include "AsteroidsEnums.h"
#include "Sprite.h"
namespace asteroids {
struct AsteroidEntityPhysicState : public Engine::EntityPhysicState {
AsteroidEntityPhysicState () {
mBaseType = Engine::EntityBaseTypeBlock;
mType = GameEntityTypeAsteroid;
}
virtual ~AsteroidEntityPhysicState() {};
};
struct AsteroidEntity: public Engine::EntityBase {
AsteroidEntity () {
mBaseType = Engine::EntityBaseTypeBlock;
mType = GameEntityTypeAsteroid;
mSubAsteroidsCount = 4;
}
virtual bool CollisionEvent (Engine::EntityBase *entity);
int mSubAsteroidsCount;
};
}
#endif // _ASTEROIDENTITY_H

View File

@ -0,0 +1,37 @@
#if ( !defined(_ASTEROIDSENUMS_H) || defined(GENERATE_ENUM_STRINGS) )
#if ( !defined(GENERATE_ENUM_STRINGS))
#define _ASTEROIDSENUMS_H
#endif
#include "EnumToString.h"
namespace asteroids {
BEGIN_ENUM(GameEntityType)
{
DECL_ENUM_ELEMENT(GameEntityTypeUnknown),
DECL_ENUM_ELEMENT(GameEntityTypeShip),
DECL_ENUM_ELEMENT(GameEntityTypeRocket),
DECL_ENUM_ELEMENT(GameEntityTypeAsteroid),
DECL_ENUM_ELEMENT(GameEntityTypeShipPart),
DECL_ENUM_ELEMENT(GameEntityTypeLast)
}
END_ENUM(GameEntityType)
BEGIN_ENUM(GameState)
{
DECL_ENUM_ELEMENT(GameStateMainMenu),
DECL_ENUM_ELEMENT(GameStateRunning),
DECL_ENUM_ELEMENT(GameStatePaused),
DECL_ENUM_ELEMENT(GameStatePlayerDied),
DECL_ENUM_ELEMENT(GameStateLevelComplete),
DECL_ENUM_ELEMENT(GameStateGameOver)
}
END_ENUM(GameState)
#include "AsteroidsEvents.h"
}
#endif /* _ASTEROIDSENUMS_H */

View File

@ -0,0 +1,14 @@
#include "EnumToString.h"
namespace asteroids {
BEGIN_ENUM(Event)
{
DECL_ENUM_ELEMENT(EventAccelerateStart),
DECL_ENUM_ELEMENT(EventAccelerateStop),
DECL_ENUM_ELEMENT(EventShipExplode)
}
END_ENUM(Event)
}

26
asteroids/Controller.cc Normal file
View File

@ -0,0 +1,26 @@
#include "Controller.h"
namespace asteroids {
int Controller::OnInit (int argc, char *argv[]) {
Engine::ControllerBase::OnInit (argc, argv);
mBindings[SDLK_q] = "quit";
mBindings[SDLK_v] = "+forward";
mBindings[SDLK_h] = "+turnleft";
mBindings[SDLK_g] = "+turnright";
mBindings[SDLK_UP] = "+forward";
mBindings[SDLK_LEFT] = "+turnleft";
mBindings[SDLK_RIGHT] = "+turnright";
mBindings[SDLK_SPACE] = "attack";
mBindings[SDLK_F8] = "toggleconsole";
mBindings[SDLK_F9] = "set playerspeed 5.0";
return 0;
}
}

35
asteroids/Controller.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _CONTROLLER_H
#define _CONTROLLER_H
#include "Engine.h"
#include "ControllerBase.h"
namespace asteroids {
/** \brief All possible controller states for an Entity
*
* These are used by the controller to update the current state of an Entity
* (e.g. EntityPhysicState).
*/
enum EntityControllerKeyState {
EntityKeyStateForward = 0,
EntityKeyStateBack,
EntityKeyStateLeft,
EntityKeyStateRight,
EntityKeyStateTurnLeft,
EntityKeyStateTurnRight,
EntityKeyStateLast
};
class Controller : public Engine::ControllerBase {
public:
Controller () {};
protected:
/** \brief Set up basic keybindings */
virtual int OnInit (int argc, char* argv[]);
/** \brief Registers the commands of the cnotroller */
virtual void OnRegisterCommands ();
};
}
#endif // _CONTROLLER_H

View File

@ -0,0 +1,217 @@
// #include "Engine.h"
#include "Controller.h"
#include "Model.h"
#include "EntityBase.h"
#include "EventsBase.h"
#include "Controller.h"
#include "ShipEntity.h"
#include "AsteroidsEvents.h"
namespace asteroids {
static Controller *ControllerInstance = NULL;
/* +forward */
bool Cmd_ControllerForwardDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateForward);
Engine::EventBasePtr forward_event (new Engine::EventBase());
forward_event->mEventType = EventAccelerateStart;
QueueEvent (forward_event);
return true;
}
return false;
}
/* -forward */
bool Cmd_ControllerForwardUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateForward);
Engine::EventBasePtr forward_event (new Engine::EventBase());
forward_event->mEventType = EventAccelerateStop;
QueueEvent (forward_event);
return true;
}
return false;
}
/* +back */
bool Cmd_ControllerBackDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateBack);
return true;
}
return false;
}
/* -back */
bool Cmd_ControllerBackUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateBack);
return true;
}
return false;
}
/* +left */
bool Cmd_ControllerLeftDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateLeft);
return true;
}
return false;
}
/* -left */
bool Cmd_ControllerLeftUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateLeft);
return true;
}
return false;
}
/* +right */
bool Cmd_ControllerRightDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateRight);
return true;
}
return false;
}
/* -right */
bool Cmd_ControllerRightUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateRight);
return true;
}
return false;
}
/* +turnleft */
bool Cmd_ControllerTurnLeftDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateTurnLeft);
return true;
}
return false;
}
/* -turnleft */
bool Cmd_ControllerTurnLeftUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateTurnLeft);
return true;
}
return false;
}
/* +turnright */
bool Cmd_ControllerTurnRightDown (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->SetControllerKeyState (EntityKeyStateTurnRight);
return true;
}
return false;
}
/* -turnright */
bool Cmd_ControllerTurnRightUp (std::vector<std::string> args) {
assert (ControllerInstance);
Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId());
if (player_entity) {
player_entity->UnsetControllerKeyState (EntityKeyStateTurnRight);
return true;
}
return false;
}
/* attack */
bool Cmd_ControllerAttack (std::vector<std::string> args) {
assert (ControllerInstance);
ShipEntity* player_entity = (ShipEntity*) Engine::GetEntity (Engine::GetPlayerEntityId ());
assert (player_entity);
player_entity->Attack ();
return true;
}
void Controller::OnRegisterCommands () {
ControllerBase::OnRegisterCommands ();
ControllerInstance = this;
Engine::AddCommand ("+forward", Cmd_ControllerForwardDown);
Engine::AddCommand ("-forward", Cmd_ControllerForwardUp);
Engine::AddCommand ("+back", Cmd_ControllerBackDown);
Engine::AddCommand ("-back", Cmd_ControllerBackUp);
Engine::AddCommand ("+left", Cmd_ControllerLeftDown);
Engine::AddCommand ("-left", Cmd_ControllerLeftUp);
Engine::AddCommand ("+right", Cmd_ControllerRightDown);
Engine::AddCommand ("-right", Cmd_ControllerRightUp);
Engine::AddCommand ("+turnleft", Cmd_ControllerTurnLeftDown);
Engine::AddCommand ("-turnleft", Cmd_ControllerTurnLeftUp);
Engine::AddCommand ("+turnright", Cmd_ControllerTurnRightDown);
Engine::AddCommand ("-turnright", Cmd_ControllerTurnRightUp);
Engine::AddCommand ("attack", Cmd_ControllerAttack);
}
}

139
asteroids/EntityFactory.cc Normal file
View File

@ -0,0 +1,139 @@
#include <iostream>
#include "EntityBase.h"
#include "coll2d.h"
#include "EntityFactory.h"
#include "ShipEntity.h"
#include "RocketEntity.h"
#include "AsteroidEntity.h"
namespace asteroids {
int EntityFactory::OnInit (int argc, char* argv[]) {
Engine::LogMessage ("Initializing EntityFactory");
return 0;
}
Engine::EntityPhysicState* EntityFactory::CreateEntityPhysicState (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type > GameEntityTypeLast ) {
Engine::LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
// Create the Entity
Engine::EntityPhysicState* entity_physics = NULL;
// type specific initialization
if (type == GameEntityTypeShip) {
entity_physics = new ShipEntityPhysicState ();
if (!entity_physics) {
Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
entity_physics->mRadius = 0.5;
entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius);
assert (entity_physics->mShape);
} else if (type == GameEntityTypeAsteroid) {
entity_physics = new AsteroidEntityPhysicState ();
if (!entity_physics) {
Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
entity_physics->mRadius = 1.;
entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius);
assert (entity_physics->mShape);
} else if (type == GameEntityTypeRocket) {
entity_physics = new RocketEntityPhysicState();
if (!entity_physics) {
Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
entity_physics->mRadius = 0.1;
entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius);
} else if (type == GameEntityTypeShipPart) {
entity_physics = new RocketEntityPhysicState();
entity_physics->mBaseType = Engine::EntityBaseTypeBlock;
if (!entity_physics) {
Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
entity_physics->mRadius = 0.1;
entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius);
} else {
Engine::LogError ("No EntityPhysicState defined for GameEntity type '%d'", type);
assert (0);
}
entity_physics->mType = type;
return entity_physics;
}
Engine::EntityControllerState* EntityFactory::CreateEntityControllerState (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type >> Engine::EntityBaseTypeLast ) {
Engine::LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
// Create the Entity
Engine::EntityControllerState* entity_controller = NULL;
// specific values for each type
if (type == GameEntityTypeShip) {
entity_controller = new Engine::EntityControllerState ();
if (!entity_controller) {
Engine::LogError ("Could not allocate enough memory for EntityControllerState of type '%d'", type);
assert (0);
}
}
return entity_controller;
}
Engine::EntityBase* EntityFactory::CreateEntity (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type > GameEntityTypeLast ) {
Engine::LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
// Create the Entity
Engine::EntityBase *entity;
if (type == GameEntityTypeShip) {
entity = new ShipEntity;
} else if (type == GameEntityTypeAsteroid) {
entity = new AsteroidEntity;
} else if (type == GameEntityTypeRocket) {
entity = new RocketEntity;
} else if (type == GameEntityTypeShipPart) {
entity = new Engine::EntityBase;
entity->mBaseType = Engine::EntityBaseTypeBlock;
}
entity->mType = type;
if (!entity) {
Engine::LogError ("Could not allocate enough memory for EntityVisualState of type '%d'", type);
assert (0);
}
entity->mPhysicState = CreateEntityPhysicState (type);
entity->mControllerState = CreateEntityControllerState (type);
return entity;
}
}

29
asteroids/EntityFactory.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef _ENTITYFACTORY_H
#define _ENTITYFACTORY_H
#include "EntityFactoryBase.h"
#include "AsteroidsEnums.h"
namespace Engine {
struct EntityBase;
struct EntityPhysicState;
struct EntityControllerState;
}
namespace asteroids {
class EntityFactory: public Engine::EntityFactoryBase {
public:
EntityFactory () {};
virtual Engine::EntityPhysicState* CreateEntityPhysicState (int type);
virtual Engine::EntityControllerState* CreateEntityControllerState (int type);
virtual Engine::EntityBase* CreateEntity (int type);
protected:
virtual int OnInit (int argc, char *argv[]);
};
}
#endif // _ENTITYFACTORY_H

View File

@ -0,0 +1,7 @@
#include "AsteroidsEnums.h"
#define GENERATE_ENUM_STRINGS
#include "AsteroidsEnums.h"
#undef GENERATE_ENUM_STRINGS

176
asteroids/MenuOverlay.cc Normal file
View File

@ -0,0 +1,176 @@
#include "OGLFT.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include "DrawingsGL.h"
#include "OverlayBase.h"
#include "MenuOverlay.h"
#include "Model.h"
#include "Sprite.h"
#include "ShipEntity.h"
#include "Engine.h"
namespace asteroids {
// static float left = 0;
static float right = 0;
// static float top = 0;
static float bottom = 0;
void MenuOverlay::Init () {
if (!mShipSprite.LoadFromPNG("./data/textures/ship.png"))
Engine::LogError ("Could not load ship sprite!");
assert (mShipSprite.GetWidth() > 1);
mShipSprite.SetScale (0.1);
}
bool MenuOverlay::OnKeyDown (const SDL_keysym &keysym) {
if (mModel->GetGameState() == GameStateLevelComplete) {
switch (keysym.sym) {
case SDLK_RETURN:
mModel->SetGameState(GameStateRunning);
break;
default:
break;
}
return true;
} else if (mModel->GetGameState() == GameStateRunning) {
switch (keysym.sym) {
case SDLK_ESCAPE:
mModel->SetGameState(GameStatePaused);
return true;
default:
break;
}
return false;
} else if (mModel->GetGameState() == GameStateGameOver) {
switch (keysym.sym) {
case SDLK_ESCAPE:
mModel->SetGameState(GameStateMainMenu);
break;
case SDLK_RETURN:
mModel->SetGameState(GameStateMainMenu);
break;
default:
break;
}
return true;
}
else if (mModel->GetGameState() == GameStateMainMenu
|| mModel->GetGameState() == GameStatePaused) {
switch (keysym.sym) {
case SDLK_ESCAPE:
Engine::RunCommand ("quit");
return true;
case SDLK_RETURN:
mModel->SetGameState(GameStateRunning);
return true;
default:
return true;
}
} else if (mModel->GetGameState() == GameStatePlayerDied) {
switch (keysym.sym) {
case SDLK_RETURN:
mModel->SetGameState(GameStateRunning);
break;
default:
break;
}
return true;
}
return false;
}
void MenuOverlay::Draw () {
glClearColor (0.1, 0.1, 0.1, 1.);
right = static_cast<float> (Engine::GetWindowWidth());
bottom = static_cast<float> (Engine::GetWindowHeight());
// we switch to orthographic projection and draw the contents of the 2d
// overlay on top of the previous drawings
glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();
// first we have to get the size of the current viewport to set up the
// orthographic projection correctly
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
gluOrtho2D (viewport[0], viewport[2], viewport[3], viewport[1]);
glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
// then we do the drawings
if (mModel->GetGameState() == GameStateRunning) {
glClearColor (0., 0., 0., 1.);
DrawGameRunning();
} else if (mModel->GetGameState() == GameStateGameOver)
DrawGameOverScreen ();
else if (mModel->GetGameState() == GameStateMainMenu
|| mModel->GetGameState() == GameStatePaused)
DrawGameMenu ();
else if (mModel->GetGameState() == GameStateLevelComplete)
DrawGameLevelComplete ();
else if (mModel->GetGameState() == GameStatePlayerDied)
DrawPlayerDied();
glPopMatrix ();
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
};
void MenuOverlay::DrawGameRunning() {
right = static_cast<float> (Engine::GetWindowWidth());
bottom = static_cast<float> (Engine::GetWindowHeight());
int i;
for (i = 0; i < mModel->GetPlayerLives(); i++) {
mShipSprite.DrawAt2D (right - 32 - i*20, bottom - 16);
}
}
void MenuOverlay::DrawPlayerDied () {
std::ostringstream topbar_stream;
topbar_stream << "You died ...";
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, topbar_stream.str().c_str ());
}
void MenuOverlay::DrawGameOverScreen() {
std::ostringstream topbar_stream;
topbar_stream << "That was pathetic! ";
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, topbar_stream.str().c_str ());
}
void MenuOverlay::DrawGameMenu() {
Engine::DrawGLString ( right * 0.5 - 100, bottom * 0.5 - 8 - 64, "A s t e r o i d s");
Engine::DrawGLString ( right * 0.5 - 100, bottom * 0.5 - 8 - 32, "Main Menu");
if (mModel->GetGameState() == GameStatePaused)
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "[Return] - Resume Game");
else
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "[Return] - Start Game");
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, "[Escape] - Quit");
}
void MenuOverlay::DrawGameLevelComplete() {
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "Congratulations - You rock!");
Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, "[Return] - Next level ...");
}
}

41
asteroids/MenuOverlay.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef MENUOVERLAY
#define MENUOVERLAY
namespace Engine {
class OverlayBase;
}
#include "OverlayBase.h"
#include "Sprite.h"
namespace asteroids {
class Model;
class MenuOverlay : public Engine::OverlayBase {
public:
MenuOverlay () {
};
void Init ();
virtual ~MenuOverlay() {};
virtual bool OnKeyDown (const SDL_keysym &keysym);
virtual void Draw ();
void DrawGameOverScreen ();
void DrawGameMenu();
void DrawGameLevelComplete ();
void DrawGamePaused ();
void DrawGameRunning ();
void DrawPlayerDied ();
void SetModel (Model *model) { mModel = model; };
private:
Model *mModel;
Engine::Sprite mShipSprite;
};
}
#endif /* MENUOVERLAY */

213
asteroids/Model.cc Normal file
View File

@ -0,0 +1,213 @@
#include <iostream>
#include <fstream>
#include "Model.h"
#include "Physics.h"
#include "PhysicsBase.h"
#include "EntityFactory.h"
#include <assert.h>
namespace asteroids {
static Model* ModelInstance = NULL;
/*
* Inherited Module functions
*/
int Model::OnInit (int argc, char* argv[]) {
int result = Engine::ModelBase::OnInit (argc, argv);
ModelInstance = this;
mGameState = GameStateMainMenu;
mLastGameState = GameStateMainMenu;
Engine::LogMessage ("Model Initialization!");
return result;
}
void Model::Process () {
if (mLastGameState == mGameState) {
if (mGameState == GameStateRunning) {
Engine::ModelBase::Process();
}
return;
}
// when we are here we know that something has changed so we need to take
// some action.
Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState));
if (mLastGameState == GameStateMainMenu && mGameState == GameStateRunning) {
mPlayerLives = 3;
mLevel = 1;
DoLoadLevel ("./data/levels/default.txt");
}
else if (mLastGameState == GameStateRunning && mGameState == GameStatePlayerDied) {
mPlayerLives --;
ClearEntities();
if (mPlayerLives == 0)
mGameState = GameStateGameOver;
}
else if (mLastGameState == GameStateLevelComplete && mGameState == GameStateRunning)
DoLoadLevel ("./data/levels/default.txt");
else if (mLastGameState == GameStatePlayerDied && mGameState == GameStateRunning)
DoLoadLevel ("./data/levels/default.txt");
else if (mLastGameState == GameStateRunning && mGameState == GameStateGameOver)
ClearEntities();
// ... and we have to set the last game state to the current gamestate
// otherwise we end up in an infinit loop of performing the switching
// action.
mLastGameState = mGameState;
}
int Model::DoLoadLevel (const char* filename) {
Engine::LogMessage ("Loading level from %s", filename);
std::fstream level_file (filename, std::ios::in);
if (!level_file) {
Engine::LogError ("Unable to open file %s for writing!", filename);
exit (-1);
}
ClearEntities();
mAsteroids.clear();
std::string entity_type_str;
int entity_count = 0;
while (level_file >> entity_type_str) {
if (entity_type_str[0] == '#') {
std::cout << "Read Comment: " << entity_type_str;
getline (level_file, entity_type_str);
std::cout << entity_type_str << std::endl;
continue;
}
GameEntityType entity_type = GameEntityTypeUnknown;
if (entity_type_str == "GameEntityTypeShip")
entity_type = GameEntityTypeShip;
else if (entity_type_str == "GameEntityTypeAsteroid")
entity_type = GameEntityTypeAsteroid;
else {
Engine::LogError ("Unknown Entity type: %s", entity_type_str.c_str());
exit (-1);
}
Engine::EntityBase* entity = CreateEntity (entity_type);
bool is_player;
level_file >> is_player;
if (is_player)
mPlayerEntityId = entity->mId;
level_file >> entity->mPhysicState->mPosition[0];
level_file >> entity->mPhysicState->mPosition[1];
level_file >> entity->mPhysicState->mPosition[2];
level_file >> entity->mPhysicState->mOrientation[0];
level_file >> entity->mPhysicState->mOrientation[1];
level_file >> entity->mPhysicState->mOrientation[2];
level_file >> entity->mPhysicState->mVelocity[0];
level_file >> entity->mPhysicState->mVelocity[1];
level_file >> entity->mPhysicState->mVelocity[2];
level_file >> entity->mPhysicState->mAngleVelocity;
entity_count ++;
}
level_file.close();
Engine::LogDebug ("%d Entities loaded!", mEntities.size());
return 0;
}
int Model::DoSaveLevel (const char* filename) {
Engine::LogMessage ("Saving level to %s", filename);
std::fstream level_file (filename, std::ios::out);
if (!level_file) {
Engine::LogError ("Unable to open file %s for writing!", filename);
exit (-1);
}
level_file << "# Format" << std::endl;
level_file << "# <Type> <player?> <xpos> <ypos> <zpos> <zrot> <yrot> <xrot> <xvel> <yvel> <zvel> <rotvel>" << std::endl;
std::map<unsigned int, Engine::EntityBase*>::iterator iter = mEntities.begin();
unsigned int player_id = GetPlayerEntityId();
for (iter = mEntities.begin(); iter != mEntities.end(); iter++) {
Engine::EntityBase* game_entity = iter->second;
level_file << GetStringGameEntityType((GameEntityType)game_entity->mType) << "\t"
// this stores the player id
<< (game_entity->mId == player_id) << "\t"
<< game_entity->mPhysicState->mPosition[0] << "\t"
<< game_entity->mPhysicState->mPosition[1] << "\t"
<< game_entity->mPhysicState->mPosition[2] << "\t"
<< game_entity->mPhysicState->mOrientation[0] << "\t"
<< game_entity->mPhysicState->mOrientation[1] << "\t"
<< game_entity->mPhysicState->mOrientation[2] << "\t"
<< game_entity->mPhysicState->mVelocity[0] << "\t"
<< game_entity->mPhysicState->mVelocity[1] << "\t"
<< game_entity->mPhysicState->mVelocity[2] << "\t"
<< game_entity->mPhysicState->mAngleVelocity << "\t"
<< std::endl;
}
level_file.close();
return 0;
}
void Model::OnCreateEntity (const int type, const unsigned int id) {
GameEntityType entity_type = (GameEntityType) type;
if (entity_type == GameEntityTypeAsteroid) {
mAsteroids.push_back (id);
}
}
void Model::OnKillEntity (const Engine::EntityBase *entity) {
GameEntityType entity_type = (GameEntityType) entity->mType;
if (entity_type == GameEntityTypeAsteroid) {
unsigned int i;
for (i = 0; i < mAsteroids.size(); i++) {
if (mAsteroids.at(i) == entity->mId) {
std::vector<unsigned int>::iterator entity_iter = mAsteroids.begin() + i;
mAsteroids.erase (entity_iter);
break;
}
}
if (mAsteroids.size() == 0) {
SetGameState (GameStateLevelComplete);
}
}
}
float Model::GetWorldWidth () {
return static_cast<Physics*>(mPhysics)->GetWorldWidth();
}
float Model::GetWorldHeight () {
return static_cast<Physics*>(mPhysics)->GetWorldHeight();
}
}

49
asteroids/Model.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef _MODEL_H
#define _MODEL_H
#include "ModelBase.h"
#include "AsteroidsEnums.h"
namespace asteroids {
class Model : public Engine::ModelBase {
public:
virtual void Process();
int DoLoadLevel (const char* filename);
int DoSaveLevel (const char* filename);
void SetGameState (const GameState &state) {
mLastGameState = mGameState;
mGameState = state;
};
GameState GetGameState () { return mGameState; };
int GetPlayerLives () { return mPlayerLives; };
float GetWorldWidth ();
float GetWorldHeight ();
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
virtual void OnRegisterCommands ();
virtual void OnCreateEntity (const int type, const unsigned int id);
virtual void OnKillEntity (const Engine::EntityBase *entity);
private:
/** \brief Keeps a list of all asteroids */
std::vector<unsigned int> mAsteroids;
GameState mGameState;
GameState mLastGameState;
int mPlayerLives;
int mLevel;
friend class View;
};
}
#endif // _MODEL_H

View File

@ -0,0 +1,48 @@
#include "Model.h"
namespace asteroids {
static Model *ModelInstance = NULL;
bool Cmd_SaveLevel (const std::vector<std::string> args) {
assert (ModelInstance);
if (args.size() != 1) {
Engine::CommandSetErrorString ("usage: savelevel <path_to_level>");
return false;
}
if (ModelInstance->DoSaveLevel (args[0].c_str()) > 0)
return true;
// ToDo: Maybe some error output?
return false;
}
bool Cmd_LoadLevel (const std::vector<std::string> args) {
assert (ModelInstance);
if (args.size() != 1) {
Engine::CommandSetErrorString ("usage: loadlevel <path_to_level>");
return false;
}
if (ModelInstance->DoLoadLevel (args[0].c_str()) > 0)
return true;
// ToDo: Maybe some error output?
return false;
}
void Model::OnRegisterCommands () {
ModelInstance = this;
Engine::ModelBase::OnRegisterCommands ();
Engine::AddCommand ("savelevel", Cmd_SaveLevel);
Engine::AddCommand ("loadlevel", Cmd_LoadLevel);
}
}

58
asteroids/Physics.cc Normal file
View File

@ -0,0 +1,58 @@
#include <iostream>
#include <fstream>
#include "Model.h"
#include "Physics.h"
#include "EntityFactory.h"
#include <assert.h>
namespace asteroids {
static Physics* PhysicsInstance = NULL;
/*
* Inherited Module functions
*/
int Physics::OnInit (int argc, char* argv[]) {
Engine::PhysicsBase::OnInit (argc, argv);
Engine::LogMessage ("Physics Initialization!");
PhysicsInstance = this;
mWorldWidth = 16;
mWorldHeight = 16;
return 0;
}
int Physics::Simulate (float msec, Engine::ModelBase *model) {
int result = Engine::PhysicsBase::Simulate (msec, model);
Engine::EntityPhysicState* entity = NULL;
std::map<unsigned int, Engine::EntityPhysicState*>::iterator entity_iter;
for (entity_iter = mEntities.begin ();
entity_iter != mEntities.end();
entity_iter++) {
entity = entity_iter->second;
if (entity->mPosition[0] > mWorldWidth * 0.5)
entity->mPosition[0] -= mWorldWidth;
if (entity->mPosition[0] < - mWorldWidth * 0.5)
entity->mPosition[0] += mWorldWidth;
if (entity->mPosition[2] > mWorldHeight * 0.5)
entity->mPosition[2] -= mWorldHeight;
if (entity->mPosition[2] < - mWorldHeight * 0.5)
entity->mPosition[2] += mWorldHeight;
}
return result;
}
}

34
asteroids/Physics.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef _PHYSICS_H
#define _PHYSICS_H
#include "PhysicsBase.h"
namespace Engine {
class Model;
}
namespace asteroids {
class Physics : public Engine::PhysicsBase {
public:
virtual int Simulate (float msec, Engine::ModelBase* model = NULL);
void SetWorldSize (float width, float height) {
mWorldWidth = width;
mWorldHeight = height;
}
float GetWorldWidth () { return mWorldWidth; }
float GetWorldHeight () { return mWorldHeight; }
protected:
virtual int OnInit (int argc, char* argv[]);
private:
float mWorldWidth;
float mWorldHeight;
};
}
#endif // _PHYSICS_H

17
asteroids/RocketEntity.cc Normal file
View File

@ -0,0 +1,17 @@
#include "Model.h"
#include "RocketEntity.h"
#include "Controller.h"
#include <GL/gl.h>
namespace asteroids {
void RocketEntity::Update (float delta_sec) {
mSecToLive -= delta_sec;
if (mSecToLive <= 0.)
Engine::KillEntity (mId);
}
}

39
asteroids/RocketEntity.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef _ROCKETENTITY_H
#define _ROCKETENTITY_H
#include "EntityBase.h"
#include "AsteroidsEnums.h"
#include "Sprite.h"
namespace asteroids {
struct RocketEntityPhysicState : public Engine::EntityPhysicState {
RocketEntityPhysicState () {
mBaseType = Engine::EntityBaseTypeParticle;
mType = GameEntityTypeRocket;
}
virtual ~RocketEntityPhysicState() {};
};
struct RocketEntity: public Engine::EntityBase {
RocketEntity () {
mBaseType = Engine::EntityBaseTypeParticle;
mType = GameEntityTypeRocket;
mSecToLive = 3.;
}
virtual ~RocketEntity() {};
virtual void Update (float delta_sec);
virtual bool CollisionEvent (Engine::EntityBase *entity) {
Engine::LogMessage ("Rocket BOOM");
return false;
}
float mSecToLive;
};
}
#endif // _ROCKETENTITY_H

122
asteroids/ShipEntity.cc Normal file
View File

@ -0,0 +1,122 @@
#include "Engine.h"
#include "Model.h"
#include "ShipEntity.h"
#include "RocketEntity.h"
#include "Controller.h"
#include "AsteroidsEvents.h"
#include "coll2d.h"
namespace asteroids {
static Engine::Variable var_ship_acceleration ("ship_acceleration", "10");
static Engine::Variable var_ship_maxspeed ("ship_maxspeed", "10");
static Engine::Variable var_ship_rotationspeed ("ship_rotationspeed", "180");
void ShipEntity::Update (float delta_sec) {
if (!mPhysicState || !mControllerState)
return;
// If we die, we have to decrease the fade timer
if (!mAlive) {
mFadeTimer -= delta_sec;
if (mFadeTimer <= 0.) {
Model *model = (Model*) Engine::EngineGetModel();
model->SetGameState (GameStatePlayerDied);
}
return;
}
mState = Idle;
// the local velocity
vector3d local_velocity = mPhysicState->mVelocity;
mPhysicState->LocalizeRotation (local_velocity);
// set the local velocity as the current state of the keys are
if (mControllerState->GetKey (EntityKeyStateForward)) {
local_velocity[0] += delta_sec * var_ship_acceleration.GetFloatValue();
mState = Accelerating;
}
// now transform these to global velocities
mPhysicState->GlobalizeRotation (local_velocity);
// now we can update the new global velocity
mPhysicState->SetVelocity(local_velocity);
if (mControllerState->GetKey (EntityKeyStateTurnLeft)) {
mPhysicState->mOrientation[1] += delta_sec * var_ship_rotationspeed.GetFloatValue();
}
if (mControllerState->GetKey (EntityKeyStateTurnRight)) {
mPhysicState->mOrientation[1] -= delta_sec * var_ship_rotationspeed.GetFloatValue();
}
// Check for the maximum speed
float speed = mPhysicState->mVelocity.length();
if (speed > var_ship_maxspeed.GetFloatValue()) {
mPhysicState->mVelocity *= var_ship_maxspeed.GetFloatValue() / speed;
}
}
bool ShipEntity::CollisionEvent (Engine::EntityBase* entity) {
GameEntityType other_type = (GameEntityType) entity->mType;
if (other_type == GameEntityTypeAsteroid) {
Engine::LogMessage ("You died!");
mPhysicState->mStatic = true;
mAlive = false;
mFadeTimer = 3.;
mState = Dying;
Engine::EventBasePtr explode_event (new Engine::EventBase());
explode_event->mEventType = EventShipExplode;
explode_event->mEventUnsignedInt = mId;
QueueEvent (explode_event);
return true;
} else if (other_type == GameEntityTypeRocket) {
Engine::LogMessage ("You just killed yourself!");
mPhysicState->mStatic = true;
mAlive = false;
mFadeTimer = 1.;
mState = Dying;
return true;
}
return false;
}
void ShipEntity::Attack () {
if (!mAlive)
return;
Engine::LogMessage ("ATTACK");
Engine::EntityPhysicState* entity_physic = Engine::GetEntityPhysicState (mId);
vector3d attack_dir (1., 0., 0.);
entity_physic->GlobalizeRotation (attack_dir);
RocketEntity *rocket_entity = (RocketEntity*) Engine::CreateEntity (GameEntityTypeRocket);
rocket_entity->mSecToLive = 1.75;
RocketEntityPhysicState *rocket_physics = (RocketEntityPhysicState*) rocket_entity->mPhysicState;
rocket_physics->mPosition = attack_dir;
rocket_physics->mPosition *= mPhysicState->mRadius;
rocket_physics->mPosition += entity_physic->mPosition;
rocket_physics->mOrientation = entity_physic->mOrientation;
rocket_physics->mVelocity = attack_dir.normalize();
rocket_physics->mVelocity *= var_ship_maxspeed.GetFloatValue() + 0.1;
}
}

58
asteroids/ShipEntity.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef _SHIPENTITY_H
#define _SHIPENTITY_H
#include "EntityBase.h"
#include "AsteroidsEnums.h"
#include "Sprite.h"
namespace asteroids {
struct ShipEntityPhysicState : public Engine::EntityPhysicState {
ShipEntityPhysicState () {
mType = GameEntityTypeShip;
mBaseType = Engine::EntityBaseTypeActor;
mAcceleration = 10.;
mMaxSpeed = 10.;
mRotationSpeed = 180.;
}
virtual ~ShipEntityPhysicState() {};
float mAcceleration;
float mMaxSpeed;
float mRotationSpeed;
};
struct ShipEntity: public Engine::EntityBase {
enum State {
Idle = 0,
Accelerating,
Rotating,
Shooting,
Dying
};
ShipEntity () {
mType = GameEntityTypeShip;
mBaseType = Engine::EntityBaseTypeActor;
mAlive = true;
mFadeTimer = 0.;
mState = Idle;
}
virtual ~ShipEntity() {};
virtual void Attack ();
virtual void Update (float delta_sec);
virtual bool CollisionEvent (Engine::EntityBase *entity);
bool mAlive;
float mFadeTimer;
State mState;
};
}
#endif // _SHIPENTITY_H

359
asteroids/View.cc Normal file
View File

@ -0,0 +1,359 @@
#include "View.h"
#include "CameraBase.h"
#include "MenuOverlay.h"
#include "SimpleConsoleOverlay.h"
#include "Engine.h"
#include "Physics.h"
#include "Model.h"
#include "EventsBase.h"
#include "ShipEntity.h"
#include "AsteroidEntity.h"
#include "AsteroidsEvents.h"
#include "RocketEntity.h"
#include <GL/gl.h>
// #define DRAW_BOUNDARIES
#ifdef DRAW_BOUNDARIES
#include "coll2d.h"
#include "DrawingsGL.h"
#endif
using namespace std;
namespace asteroids {
int View::OnInit (int argc, char* argv[]) {
ViewBase::OnInit (argc, argv);
// We want menu
mMenuOverlay = new MenuOverlay;
mMenuOverlay->SetModel ((Model*) mModel);
mMenuOverlay->Init();
AddOverlay (mMenuOverlay);
// We want the console
Engine::SimpleConsoleOverlay *console = new Engine::SimpleConsoleOverlay;
// We also want to display the log bar
console->SetDrawLogBar (true);
AddOverlay (console);
// This is a simple star field that makes the game so spacy
int i;
for (i = 0; i < 200; i++) {
BackgroundStar star;
star.position[0] = rand() / float(RAND_MAX);
star.position[1] = rand() / float(RAND_MAX);
star.position[2] = rand() / float(RAND_MAX);
mBackgroundStars.push_back (star);
}
mAsteroidSprite.LoadFromPNG ("./data/textures/asteroid.png");
mShipSprite.LoadFromPNG ("./data/textures/ship.png");
mShipThrustSprite.LoadFromPNG ("./data/textures/ship_thrust.png");
mShipThrustSprite.SetAnimation (4, 8);
mShipPartsSprite.LoadFromPNG ("./data/textures/ship_parts.png");
mShipPartsSprite.SetSubSpriteCount (10);
mAccelerateEventHandler = new AccelerateEventHandler (this);
Engine::RegisterListener (mAccelerateEventHandler, EventAccelerateStart);
Engine::RegisterListener (mAccelerateEventHandler, EventAccelerateStop);
mShipExplodeEventHandler = new ShipExplodeEventHandler (this);
Engine::RegisterListener (mShipExplodeEventHandler, EventShipExplode);
return 0;
}
void View::OnDestroy() {
delete mAccelerateEventHandler;
}
/*
* Event Handlers
*/
bool View::AccelerateEventHandler::HandleEvent (const Engine::EventBasePtr &event) const {
if (event->mEventType == EventAccelerateStart)
mView->mShipThrustSprite.ResetAnimation();
Engine::LogMessage ("Received Acceleration Event: %d", event->mEventType);
return true;
}
bool View::ShipExplodeEventHandler::HandleEvent (const Engine::EventBasePtr &event) const {
if (event->mEventType == EventShipExplode) {
Engine::EntityBase *ship_entity = Engine::GetEntity (event->mEventUnsignedInt);
vector3d position = ship_entity->mPhysicState->mPosition;
vector3d orientation = ship_entity->mPhysicState->mOrientation;
vector3d velocity = ship_entity->mPhysicState->mVelocity;
unsigned int i;
mView->mShipPartsEntityIds.clear();
for (i = 0; i < mView->mShipPartsSprite.GetSubSpriteCount(); i++) {
Engine::EntityBase* part_sprite_particle = Engine::CreateEntity (GameEntityTypeShipPart);
part_sprite_particle->mPhysicState->mPosition = position;
part_sprite_particle->mPhysicState->mOrientation = orientation;
part_sprite_particle->mPhysicState->mVelocity = velocity;
part_sprite_particle->mPhysicState->mVelocity = vector3d (velocity[0] * (rand()/float(RAND_MAX)) * 1.7, 0., velocity[2] * (rand()/float(RAND_MAX)) * 1.5);
part_sprite_particle->mPhysicState->mAngleVelocity = (rand()/float(RAND_MAX) - 0.5 ) * 100.;
mView->mShipPartsEntityIds.push_back(part_sprite_particle->mId);
}
}
Engine::LogMessage ("Received Ship Explode Event: %d", event->mEventType);
return true;
}
/*
* Module specific functions
*/
void View::UpdateCamera () {
mCamera->SetEye (
0.,
9.5,
0.
);
mCamera->SetPointOfIntrest (
0.,
0.,
0.
);
mCamera->SetUp (
0.,
0.,
-1.
);
mCamera->Update ();
}
void View::DrawStars() {
unsigned int i;
float world_width, world_height;
world_width = static_cast<Model*>(mModel)->GetWorldWidth();
world_height = static_cast<Model*>(mModel)->GetWorldHeight();
vector3d velocity (1., 0., 0.);
glPushMatrix();
glTranslatef(-world_width * 0.5, 0, -world_height * 0.5);
glColor3f (1., 1., 1.);
glPointSize(2.);
glBegin(GL_POINTS);
float z_value;
for (i = 0; i < mBackgroundStars.size(); i++) {
// glPointSize (2. + 300. *mBackgroundStars.at(i).position[1]);
z_value = mBackgroundStars.at(i).position[1] + 0.1;
glColor3f (z_value, z_value, z_value);
glVertex3f (mBackgroundStars.at(i).position[0] * world_width,
-1.,
mBackgroundStars.at(i).position[2] * world_height);
mBackgroundStars.at(i).position -= vector3d(Engine::GetFrameDuration() * 0.7 * mBackgroundStars.at(i).position[1] / world_width, 0., 0.);
if (mBackgroundStars.at(i).position[0] < 0.)
mBackgroundStars.at(i).position[0] += 1.;
if (mBackgroundStars.at(i).position[0] >= 1.)
mBackgroundStars.at(i).position[0] -= 1.;
}
glEnd();
glPopMatrix();
}
void View::DrawWorld() {
std::map<unsigned int, Engine::EntityBase*>::iterator entity_iterator;
Model *game_model = static_cast<Model*> (mModel);
DrawStars ();
if ( game_model->GetGameState() != GameStateRunning) {
return;
}
ViewBase::DrawWorld();
for (entity_iterator = game_model->mEntities.begin ();
entity_iterator != game_model->mEntities.end();
entity_iterator++) {
Engine::EntityBase* entity = entity_iterator->second;
// Perform multiple drawing if the entity is at the border
Physics* game_physics = (Physics*) game_model->mPhysics;
float world_width = game_physics->GetWorldWidth();
float world_height = game_physics->GetWorldHeight();
// Drawing at the original position:
glPushMatrix ();
glTranslatef (entity->mPhysicState->mPosition[0],
entity->mPhysicState->mPosition[1],
entity->mPhysicState->mPosition[2]);
glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.);
glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.);
glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.);
glColor3f (1., 1., 1.);
DrawEntity (entity);
glPopMatrix ();
// If we move out the right side
if (entity->mPhysicState->mPosition[0] + entity->mPhysicState->mRadius * 2
>= world_width * 0.5) {
glPushMatrix ();
glTranslatef (entity->mPhysicState->mPosition[0] - world_width,
entity->mPhysicState->mPosition[1],
entity->mPhysicState->mPosition[2]);
glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.);
glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.);
glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.);
glColor3f (1., 1., 1.);
DrawEntity (entity);
glPopMatrix ();
}
// if we move out the left side
if (entity->mPhysicState->mPosition[0] - entity->mPhysicState->mRadius * 2
< - world_width * 0.5) {
glPushMatrix ();
glTranslatef (entity->mPhysicState->mPosition[0] + world_width,
entity->mPhysicState->mPosition[1],
entity->mPhysicState->mPosition[2]);
glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.);
glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.);
glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.);
glColor3f (1., 1., 1.);
DrawEntity (entity);
glPopMatrix ();
}
// If we move out the bottom side
if (entity->mPhysicState->mPosition[2] + entity->mPhysicState->mRadius * 2
>= world_height * 0.5) {
glPushMatrix ();
glTranslatef (entity->mPhysicState->mPosition[0],
entity->mPhysicState->mPosition[1],
entity->mPhysicState->mPosition[2] - world_height);
glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.);
glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.);
glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.);
glColor3f (1., 1., 1.);
DrawEntity (entity);
glPopMatrix ();
}
// if we move out the left side
if (entity->mPhysicState->mPosition[2] - entity->mPhysicState->mRadius * 2
< - world_height* 0.5) {
glPushMatrix ();
glTranslatef (entity->mPhysicState->mPosition[0],
entity->mPhysicState->mPosition[1],
entity->mPhysicState->mPosition[2] + world_height);
glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.);
glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.);
glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.);
glColor3f (1., 1., 1.);
DrawEntity (entity);
glPopMatrix ();
}
}
}
void View::DrawEntity (Engine::EntityBase *entity) {
if (entity->mType == GameEntityTypeAsteroid)
DrawAsteroid ((AsteroidEntity*) entity);
else if (entity->mType == GameEntityTypeShip)
DrawShip ((ShipEntity*) entity);
else if (entity->mType == GameEntityTypeRocket)
DrawRocket ((RocketEntity*) entity);
else if (entity->mType == GameEntityTypeShipPart)
DrawShipPart (entity);
else {
Engine::LogError ("Cannot draw entity: unknown type '%d'", entity->mType);
}
}
/// \todo: Update of the animation ??
void View::DrawShip (ShipEntity *ship) {
if (!ship->mAlive)
return;
mShipSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight());
mShipThrustSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight());
if (ship->mState == ShipEntity::Accelerating) {
mShipThrustSprite.UpdateAnimation (Engine::GetFrameDuration());
mShipThrustSprite.DrawAt(-0.5, 0., 0.);
}
mShipSprite.DrawAt(0., 0., 0.);
#ifdef DRAW_BOUNDARIES
glColor3f (1., 1., 1.);
DrawCircle (ship->mPhysicState->mRadius, 20);
#endif
}
void View::DrawAsteroid (AsteroidEntity *asteroid) {
mAsteroidSprite.SetScale (2. * asteroid->mPhysicState->mRadius / mAsteroidSprite.GetWidth());
mAsteroidSprite.DrawAt(0., 0., 0.);
#ifdef DRAW_BOUNDARIES
glColor3f (1., 1., 1.);
DrawCircle (asteroid->mPhysicState->mRadius, 20);
#endif
}
void View::DrawRocket (RocketEntity *rocket) {
glColor3f (1., 1., 1.);
glBegin (GL_QUADS);
glVertex3f (-0.25, 0., 0.05);
glVertex3f (0.05, 0., 0.05);
glVertex3f (0.05, 0., -0.05);
glVertex3f (-0.25, 0., -0.05);
glEnd ();
}
void View::DrawShipPart (Engine::EntityBase *entity) {
unsigned int i;
mShipPartsSprite.SetScale (1. / mShipSprite.GetHeight());
for (i = 0; i < mShipPartsEntityIds.size(); i++) {
if (mShipPartsEntityIds.at(i) == entity->mId) {
mShipPartsSprite.DrawSubAt (i, 0., 0., 0.);
}
}
#ifdef DRAW_BOUNDARIES
glColor3f (1., 1., 1.);
DrawCircle (entity->mPhysicState->mRadius, 20);
#endif
}
}

75
asteroids/View.h Normal file
View File

@ -0,0 +1,75 @@
#ifndef _VIEW_H
#define _VIEW_H
#include "ViewBase.h"
#include "mathlib.h"
#include "Sprite.h"
#include "EntityBase.h"
namespace asteroids {
class MenuOverlay;
struct ShipEntity;
struct AsteroidEntity;
struct RocketEntity;
struct BackgroundStar {
vector3d position;
};
/** \brief Performs the actual drawing based on Camera and Model
*/
class View : public Engine::ViewBase {
protected:
/** \brief Initializes the system */
int OnInit (int argc, char* argv[]);
void OnDestroy ();
/** \brief Updates the camera for further drawing */
virtual void UpdateCamera ();
private:
virtual void DrawWorld ();
void DrawStars ();
void DrawEntity (Engine::EntityBase *entity);
void DrawShip (ShipEntity *ship);
void DrawAsteroid (AsteroidEntity *asteroid);
void DrawRocket (RocketEntity *asteroid);
void DrawShipPart (Engine::EntityBase *entity);
MenuOverlay *mMenuOverlay;
std::vector<BackgroundStar> mBackgroundStars;
std::vector<unsigned int> mShipPartsEntityIds;
Engine::Sprite mAsteroidSprite;
Engine::Sprite mShipSprite;
Engine::Sprite mShipThrustSprite;
Engine::Sprite mShipPartsSprite;
class AccelerateEventHandler : public Engine::EventListenerBase {
public:
explicit AccelerateEventHandler (View *view) : mView (view) {};
virtual bool HandleEvent (const Engine::EventBasePtr &event) const;
private:
View *mView;
};
class ShipExplodeEventHandler : public Engine::EventListenerBase {
public:
explicit ShipExplodeEventHandler (View *view) : mView (view) {};
virtual bool HandleEvent (const Engine::EventBasePtr &event) const;
private:
View *mView;
};
AccelerateEventHandler *mAccelerateEventHandler;
ShipExplodeEventHandler *mShipExplodeEventHandler;
friend class AccelerateEventHandler;
};
}
#endif // _VIEW_H

44
asteroids/main.cc Normal file
View File

@ -0,0 +1,44 @@
#include <cstdlib>
#include <iostream>
#include "Engine.h"
#include "Controller.h"
#include "View.h"
#include "Model.h"
#include "Physics.h"
#include "EntityFactory.h"
using namespace std;
int main (int argc, char* argv[]) {
cout << "Game Start" << endl;
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::LogLevelDebug);
if (engine.Init (argc, argv) != 0) {
cout << "Could not start engine!" << endl;
exit (-1);
}
engine.GetView()->SetGridSize (8,8);
dynamic_cast<asteroids::Physics*>(engine.GetPhysics())->SetWorldSize (28, 20);
SetLogPrintLevel (Engine::LogLevelDebug);
engine.MainLoop ();
engine.Destroy ();
cout << "Game Quit" << endl;
return 0;
}

12
codingstyle.txt Normal file
View File

@ -0,0 +1,12 @@
Indent: Tabs!
class Uppercase {
int OnInit ()
void OnDestroy ();
void SetValue ();
int GetValue ();
void SomeCoolFunction ();
};

BIN
data/fonts/console.ttf Normal file

Binary file not shown.

5
data/levels/default.txt Normal file
View File

@ -0,0 +1,5 @@
# Format
# <Type> <player?> <xpos> <ypos> <zpos> <zrot> <yrot> <xrot> <xvel> <yvel> <zvel> <rotvel>
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.1 5

BIN
data/textures/asteroid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
data/textures/ship.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,28 @@
# Tries to find FREETYPE2 (http://freetype2.bespin.org) a simple and fast
# network library by Lee Salzman
#
SET (FREETYPE2_FOUND FALSE)
FIND_PATH (FREETYPE2_INCLUDE_DIR freetype/freetype.h /usr/include/ /usr/local/include/ /usr/include/freetype2 /usr/local/include/freetype2 $ENV{FREETYPE2_PATH}/include $ENV{FREETYPE2_INCLUDE_PATH})
FIND_LIBRARY (FREETYPE2_LIBRARIES NAMES freetype PATHS /usr/lib /usr/local/lib $ENV{FREETYPE2_PATH} $ENV{FREETYPE2_PATH}/lib ENV{FREETYPE2_LIBRARY_PATH})
IF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES)
SET (FREETYPE2_FOUND TRUE)
ENDIF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES)
IF (FREETYPE2_FOUND)
IF (NOT FREETYPE2_FIND_QUIETLY)
MESSAGE(STATUS "Found FREETYPE2: ${FREETYPE2_LIBRARIES}")
ENDIF (NOT FREETYPE2_FIND_QUIETLY)
ELSE (FREETYPE2_FOUND)
IF (FREETYPE2_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find FREETYPE2")
ENDIF (FREETYPE2_FIND_REQUIRED)
ENDIF (FREETYPE2_FOUND)
MARK_AS_ADVANCED (
FREETYPE2_INCLUDE_DIR
FREETYPE2_LIBRARIES
)

60
engine/CMakeLists.txt Normal file
View File

@ -0,0 +1,60 @@
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake )
FIND_PACKAGE (SDL REQUIRED)
FIND_PACKAGE (OpenGL REQUIRED)
FIND_PACKAGE (PNG REQUIRED)
FIND_PACKAGE (FreeType2 REQUIRED)
ADD_SUBDIRECTORY ( libraries )
SET ( ENGINE_SRCS
CameraBase.cc
ControllerBase.cc
EntityBase.cc
EntityFactoryBase.cc
GameEntityBase.cc
ModelBase.cc
PhysicsBase.cc
PhysicsEntityBase.cc
ViewBase.cc
EventsBase.cc
Commands.cc
DrawingsGL.cc
EngineCommands.cc
Variables.cc
VariablesCommands.cc
SimpleConsoleOverlay.cc
Sprite.cc
Engine.cc
Logging.cc
)
INCLUDE_DIRECTORIES (
${PROJECT_SOURCE_DIR}
libraries/mathlib/
libraries/coll2d/include
libraries/oglft/
${FREETYPE2_INCLUDE_DIR}
)
IF ( WIN32 )
ADD_LIBRARY ( Engine STATIC ${ENGINE_SRCS} )
ELSE ( WIN32 )
ADD_LIBRARY ( Engine SHARED ${ENGINE_SRCS} )
ENDIF ( WIN32 )
TARGET_LINK_LIBRARIES ( Engine
${SDL_LIBRARY}
${OPENGL_LIBRARIES}
${PNG_LIBRARIES}
mathlib
oglft
coll2d
)
ADD_SUBDIRECTORY ( tests )

49
engine/CameraBase.cc Normal file
View File

@ -0,0 +1,49 @@
#include "CameraBase.h"
#include <GL/gl.h>
#include <GL/glu.h>
namespace Engine {
/*
* Inherited Module functions
*/
int CameraBase::OnInit (int argc, char* argv[]) {
LogDebug ("Camera Init");
mEye[0] = 0.;
mEye[1] = 1.;
mEye[2] = 1.;
mPointOfIntrest[0] = 0.;
mPointOfIntrest[1] = 0.;
mPointOfIntrest[2] = 0.;
mUp[0] = 0.;
mUp[1] = 1.;
mUp[2] = 0.;
mFOVY = 90.;
return 0;
}
void CameraBase::OnDestroy () {
LogDebug ("Camera Destroy");
}
/*
* Module specific functions
*/
void CameraBase::Update () {
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
gluLookAt(mEye[0], mEye[1], mEye[2],
mPointOfIntrest[0], mPointOfIntrest[1], mPointOfIntrest[2],
mUp[0], mUp[1], mUp[2]);
}
}

61
engine/CameraBase.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef _CAMERABASE_H
#define _CAMERABASE_H
#include "Engine.h"
namespace Engine {
class Module;
/** \brief Controls from where the View is looking to
*/
class CameraBase : public Module {
public:
/** updates the projection and modelview matrices for the camera */
void Update ();
float GetFOVY () {
return mFOVY;
}
/** sets the point where the camera is looking to */
void SetPointOfIntrest (float poi_x, float poi_y, float poi_z) {
mPointOfIntrest[0] = poi_x;
mPointOfIntrest[1] = poi_y;
mPointOfIntrest[2] = poi_z;
}
/** sets the position where the camera is located */
void SetEye (float eye_x, float eye_y, float eye_z) {
mEye[0] = eye_x;
mEye[1] = eye_y;
mEye[2] = eye_z;
}
/** returns the position of the eye */
void GetEye (float *eye_out) {
eye_out[0] = mEye[0];
eye_out[1] = mEye[1];
eye_out[2] = mEye[2];
}
/** sets the up direction of the camera */
void SetUp (float up_x, float up_y, float up_z) {
mUp[0] = up_x;
mUp[1] = up_y;
mUp[2] = up_z;
}
protected:
/** \brief Initializes the system */
int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
void OnDestroy ();
float mPointOfIntrest[3];
float mEye[3];
float mUp[3];
float mFOVY;
};
}
#endif // _CAMERABASE_H

245
engine/Commands.cc Normal file
View File

@ -0,0 +1,245 @@
#include "Commands.h"
namespace Engine {
static Commands *CommandsInstance = NULL;
void trim_command_str (std::string &command_str) {
std::string::size_type start = command_str.find_first_not_of (" \t");
if (start != std::string::npos)
command_str = command_str.substr (start);
std::string::size_type end = command_str.find_first_of ("#;\n\r\0");
if (end != std::string::npos)
command_str = command_str.substr (0, end);
}
/*
* Inherited Module functions
*/
int Commands::OnInit (int argc, char* argv[]) {
LogDebug ("Commands Init");
mErrorString = "";
if (CommandsInstance) {
LogError ("Commands module already initialized!");
return -1;
}
CommandsInstance = this;
return 0;
}
void Commands::OnDestroy () {
LogDebug ("Commands Destroy");
if (CommandsInstance)
CommandsInstance = NULL;
}
/*
* Module specific functions
*/
std::string Commands::GetCommandName (const std::string &command) {
return command.substr (0, command.find_first_of (" ;\n\0"));
}
std::vector<std::string> Commands::ParseArgs (std::string &argument_str) {
std::vector<std::string> args;
std::string::size_type pos = argument_str.find_first_not_of (" ");
std::string::size_type next_token = 0;
while ( argument_str.length ()> pos && argument_str.find_first_of (" \"", pos) != std::string::npos) {
next_token = argument_str.find_first_of (" \"", pos);
if (next_token != std::string::npos && argument_str[next_token] =='"') {
pos = next_token;
next_token = argument_str.find_first_of ('"', pos + 1);
if (next_token != std::string::npos) {
args.push_back (argument_str.substr (pos + 1, next_token - pos - 1));
}
pos = argument_str.find_first_not_of (" ", next_token + 1);
} else {
args.push_back (argument_str.substr (pos, next_token - pos));
pos = argument_str.find_first_not_of (" ", next_token + 1);
}
}
if (pos != std::string::npos)
args.push_back (argument_str.substr (pos, argument_str.length ()));
return args;
}
void Commands::AddCommand (const std::string &name, command_cb callback){
LogDebug ("Adding Command '%s' at %x", name.c_str(), (void *) callback);
mCommandsCallbacks[name] = callback;
return;
}
bool Commands::RunCommand (const std::string &command){
LogDebug ("Running Command: %s", command.c_str());
std::string cmd_name = GetCommandName (command);
std::string args;
if (command.length () > cmd_name.length () + 1)
args = command.substr (cmd_name.length () + 1, command.length ());
if (mCommandsCallbacks.find (cmd_name) != mCommandsCallbacks.end()) {
std::vector<std::string> argv = ParseArgs (args);
if (mCommandsCallbacks[cmd_name] (argv)) {
mErrorString = "";
return true;
} else {
return false;
}
} else {
LogWarning ("Command '%s' not registered!", cmd_name.c_str ());
}
SetErrorString ("Command '" + cmd_name + "' does not exist!");
return false;
}
void Commands::QueueCommand (const std::string &command){
mCommandQueue.push (command);
}
bool Commands::QueueExecute (){
bool result = true;
while (mCommandQueue.size() > 0) {
result = RunCommand (mCommandQueue.front());
if (!result) {
while (!mCommandQueue.empty())
mCommandQueue.pop();
return false;
}
mCommandQueue.pop();
}
return true;
}
void Commands::SetErrorString (const std::string &error_str){
mErrorString = error_str;
LogWarning ("Command Error: %s", error_str.c_str ());
}
std::string Commands::GetErrorString (){
return mErrorString;
}
/*
* Global functions
*/
bool CommandsInitialized () {
if (!CommandsInstance) {
LogError ("Commands System not yet initialized!");
return false;
}
return true;
}
void AddCommand (const std::string &name, command_cb callback){
if (!CommandsInitialized ())
return;
CommandsInstance->AddCommand (name, callback);
}
bool RunCommand (const std::string &command){
if (!CommandsInitialized ())
return false;
return CommandsInstance->RunCommand (command);
}
void QueueCommand (const std::string &command){
if (!CommandsInitialized ())
return;
CommandsInstance->QueueCommand (command);
}
bool CommandQueueExecute (){
if (!CommandsInitialized ())
return false;
return CommandsInstance->QueueExecute ();
}
void CommandSetErrorString (const std::string &error_str){
if (!CommandsInitialized ())
return;
CommandsInstance->SetErrorString (error_str);
}
std::string CommandGetErrorString (){
if (!CommandsInitialized ())
return false;
return CommandsInstance->GetErrorString();
}
/*
* Commands of the Command system
*/
bool Cmd_Exec (const std::vector<std::string> args) {
if (!CommandsInitialized())
return false;
if (args.size() != 1) {
CommandsInstance->SetErrorString("usage: exec <path_to_file>");
return false;
}
std::ifstream exec_file;
exec_file.open(args[0].c_str(), std::ios_base::in);
if (!exec_file) {
std::ostringstream error_msg;
error_msg << "exec failed: could not open file '"
<< args[0] << "'";
CommandsInstance->SetErrorString(error_msg.str());
return false;
}
int linecounter = 0;
while (!exec_file.eof()) {
std::string line;
getline (exec_file, line);
linecounter++;
trim_command_str (line);
if (line.size() == 0)
continue;
if (!CommandsInstance->RunCommand (line)) {
std::ostringstream error_msg;
error_msg << "exec failed running command '" << line
<< "' in file " << args[0] << ":" << linecounter << ".";
CommandsInstance->SetErrorString (error_msg.str());
return false;
}
}
exec_file.close();
return true;
}
void Commands::OnRegisterCommands () {
AddCommand ("exec", Cmd_Exec);
}
}

70
engine/Commands.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef _COMMANDS_H
#define _COMMANDS_H
#include "Engine.h"
namespace Engine {
class Module;
/** \brief The callback signature for commands */
typedef bool (*command_cb) (std::vector<std::string>);
/** \brief Contains all the facilities to parse/add/execute/... commands
*
* \todo make the command system case insensitive
*
* This system is mainly used for passing instructions to the Model and
* modifying it in this way. Commands can be added with a name (string) and a
* function (callback). It also contains a CommandQueue to faciliate delayed
* execution of commands and also does the parsing of functions passed to it.
*
* Functions that should be callable by the Commands module must have the
* following signature:
* \code
* bool MyFunc_cmd (std::vector<std::string> args);
* \endcode
* To keep things readable one is advised to add \c _cmd at the end of the
* function so that it is clear that this function is meant to be called by
* the Commands module.
*
* Scope: Globally visible (since commands must be added)
*
*/
class Commands : public Module {
public:
/** \brief Adds the function callback as command for the given name*/
void AddCommand (const std::string &name, command_cb callback);
/** \brief Executes the given command immediately */
bool RunCommand (const std::string &command);
/** \brief Adds the given command to the command queue */
void QueueCommand (const std::string &command);
/** \brief Executes the command queue */
bool QueueExecute ();
/** \brief When a command fails it sets the error with this function */
void SetErrorString (const std::string &error_str);
/** \brief Returns the error string */
std::string GetErrorString ();
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
virtual void OnDestroy ();
/** \brief Registers commands relevant to the Command system */
virtual void OnRegisterCommands ();
/** \brief All registered commands are in this map */
std::map<std::string, command_cb> mCommandsCallbacks;
/** \brief Queue for the commands */
std::queue<std::string> mCommandQueue;
/** \brief Holds the error message of a failed command */
std::string mErrorString;
private:
std::string GetCommandName (const std::string &command);
std::vector<std::string> ParseArgs (std::string &argument_str);
};
}
#endif // _COMMANDS_H

24
engine/CommandsGlobal.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _COMMANDSGLOBAL_H
#define _COMMANDSGLOBAL_H
namespace Engine {
typedef bool (*command_cb) (std::vector<std::string>);
/** \brief Adds the function callback as command for the given name*/
void AddCommand (const std::string &name, command_cb callback);
/** \brief Executes the given command immediately */
bool RunCommand (const std::string &command);
/** \brief Adds the given command to the command queue */
void QueueCommand (const std::string &command);
/** \brief Executes the command queue */
bool CommandQueueExecute ();
/** \brief When a command fails it sets the error with this function */
void CommandSetErrorString (const std::string &error_str);
/** \brief Returns the error string */
std::string CommandGetErrorString ();
}
#endif /* _COMMANDSGLOBAL_H */

264
engine/ControllerBase.cc Normal file
View File

@ -0,0 +1,264 @@
#include "ControllerBase.h"
#include "ModelBase.h"
#include "ViewBase.h"
#include "CommandsGlobal.h"
#include "keytable.h"
namespace Engine {
static ControllerBase *ControllerInstance = NULL;
/*
* Helper functions
*/
/** \brief Converts the SDL_BUTTON_* to a value we know */
MouseButton convert_sdl_button (Uint8 button) {
MouseButton mouse_button;
switch (button) {
case SDL_BUTTON_LEFT:
mouse_button = MouseButtonLeft;
break;
case SDL_BUTTON_MIDDLE:
mouse_button = MouseButtonMiddle;
break;
case SDL_BUTTON_RIGHT:
mouse_button = MouseButtonRight;
break;
case SDL_BUTTON_WHEELUP:
mouse_button = MouseButtonWheelUp;
break;
case SDL_BUTTON_WHEELDOWN:
mouse_button = MouseButtonWheelDown;
break;
default:
mouse_button = MouseButtonUnknown;
break;
}
return mouse_button;
}
/*
* Inherited Module functions
*/
int ControllerBase::OnInit (int argc, char* argv[]) {
LogDebug ("Controller Init");
// clear all bindings
int i;
for (i = 0; i < BINDING_KEYS_LAST; i++)
mBindings[i] = "";
ControllerInstance = this;
return 0;
}
void ControllerBase::OnDestroy () {
ControllerInstance = NULL;
LogDebug ("Controller Destroy");
}
/*
* Module specific functions
*/
bool ControllerBase::BindKey (int key, const char *command) {
if (key <= 0 || key >= BINDING_KEYS_LAST) {
LogError ("Could not bind to key with index '%d': invalid index!", key);
return false;
}
mBindings[key] = command;
return true;
}
void ControllerBase::Process () {
ProcessEvents ();
mView->CalcWorldCoordinates (mMouseScreenPosition[0], mMouseScreenPosition[1],
0., mMouseWorldPosition);
/*
LogMessage ("Screenpos = %2d,%2d Worldpos = %f,%f,%f",
mMouseScreenPosition[0], mMouseScreenPosition[1],
mMouseWorldPosition[0], mMouseWorldPosition[1], mMouseWorldPosition[2]);
*/
}
void ControllerBase::ProcessEvents () {
SDL_Event event;
while (SDL_PollEvent(&event)) {
/* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
switch (event.type) {
case SDL_KEYDOWN:
OnKeyDown (event.key.keysym);
break;
case SDL_KEYUP:
OnKeyUp (event.key.keysym);
break;
case SDL_MOUSEMOTION:
OnMouseMotion(event.motion.x, event.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
OnMouseButtonDown (event.button.button, event.button.x, event.button.y);
break;
case SDL_MOUSEBUTTONUP:
OnMouseButtonUp (event.button.button, event.button.x, event.button.y);
break;
case SDL_VIDEORESIZE:
OnVideoResize (event.resize.w, event.resize.h);
break;
case SDL_QUIT:
EngineSetStatus (EngineStatusStopping);
break;
default:
break;
}
}
}
/** \brief Keyboard processing */
bool ControllerBase::OnKeyDown (const SDL_keysym &keysym) {
if (mView->SendKeyDown (keysym))
return true;
if (mBindings[keysym.sym].size () != 0) {
QueueCommand (mBindings[keysym.sym]);
return true;
}
return false;
}
/** \brief Keyboard processing */
bool ControllerBase::OnKeyUp (const SDL_keysym &keysym) {
if (mView->SendKeyUp (keysym))
return true;
if (mBindings[keysym.sym].size () != 0) {
if (mBindings[keysym.sym][0] == '+') {
std::string upcommand = mBindings[keysym.sym];
upcommand[0] = '-';
QueueCommand (upcommand);
return true;
}
}
return false;
}
/** \brief Mouse processing */
bool ControllerBase::OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) {
MouseButton mouse_button = convert_sdl_button (button);
if (mView->SendMouseButtonDown (button, xpos, ypos))
return true;
if (mBindings[mouse_button].size () != 0) {
QueueCommand (mBindings[mouse_button]);
return true;
}
return false;
}
/** \brief Mouse processing */
bool ControllerBase::OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) {
MouseButton mouse_button = convert_sdl_button (button);
if (mView->SendMouseButtonUp (button, xpos, ypos))
return true;
if (mBindings[mouse_button].size () != 0) {
if (mBindings[mouse_button][0] == '+') {
std::string upcommand = mBindings[mouse_button];
upcommand[0] = '-';
QueueCommand (upcommand);
return true;
}
}
return false;
}
/** \brief Mouse processing */
bool ControllerBase::OnMouseMotion (const int xnew, const int ynew) {
mMouseScreenPosition[0] = xnew;
mMouseScreenPosition[1] = ynew;
return false;
}
/** \brief Video */
bool ControllerBase::OnVideoResize (int width, int height) {
mView->Resize (width, height);
return true;
}
int convert_keystring (const char *key_val) {
std::string keystr (key_val);
// convert the keystr to lowercase
for (std::string::iterator iter = keystr.begin(); iter != keystr.end(); iter++)
(*iter) = tolower(*iter);
// now we search through the table for he value we were given
int i = 0;
while (key_table[i].keynum != keytable_last) {
if (keystr.compare(key_table[i].keystr) == 0)
return key_table[i].keynum;
i++;
}
return 0;
}
/*
* Commands for the ControllerBase
*/
bool Cmd_Bind (const std::vector<std::string> args) {
if (ControllerInstance == NULL) {
CommandSetErrorString("Could not bind key: Controller not yet initialized!");
return false;
}
if (args.size() != 2) {
CommandSetErrorString("usage: bind <key> <command>");
return false;
}
int key = convert_keystring (args[0].c_str());
if (key == 0) {
std::ostringstream error_msg;
error_msg << "bind failed: invalid key '" << args[0] << '"';
CommandSetErrorString(error_msg.str());
return false;
}
if (!ControllerInstance->BindKey (key, args[1].c_str()))
return false;
return true;
}
void ControllerBase::OnRegisterCommands () {
AddCommand ("bind", Cmd_Bind);
}
}

92
engine/ControllerBase.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef _CONTROLLERBASE_H
#define _CONTROLLERBASE_H
#include "Engine.h"
namespace Engine {
class ModelBase;
class Console;
class Module;
/** \brief Defines the number of keys (keyboard + mous) that we can bind to.
*
* As the keysym enum of SDL has about 320 keys defined and we might have some
* more we set this define to 400 which should suffice. See also the file
* keytable.h
*/
#define BINDING_KEYS_LAST 400
/** \brief Converts a string into the corresponding keycode */
int convert_keystring (const char *key_val);
/** \brief All input is sent here and distributed from here
*
* Distributes and modifies the Model and indirectly the view by modifying the
* camera. It also holds the configuration of the keybindings and sends
* commands to the CommandQueue.
*/
class ControllerBase : public Module {
public:
/** \brief Processes all inputs and performs all the controlling for the
* current frame. */
void Process ();
/** \brief Returns the current mouse position in screen coordinates */
void GetMouseScreenPosition (int *pos_out) {
pos_out[0] = mMouseScreenPosition[0];
pos_out[1] = mMouseScreenPosition[1];
}
void GetMouseWorldPosition (float *pos_out) {
pos_out[0] = mMouseWorldPosition[0];
pos_out[1] = mMouseWorldPosition[1];
pos_out[2] = mMouseWorldPosition[2];
}
bool BindKey (int key, const char *command);
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
virtual void OnDestroy ();
/** \brief Registering of the commands of the ControllerBase */
virtual void OnRegisterCommands ();
/** \brief Processes all Events reported by SDL_PollEvent */
virtual void ProcessEvents ();
/** \brief Keyboard processing */
bool OnKeyDown (const SDL_keysym &keysym);
/** \brief Keyboard processing */
bool OnKeyUp (const SDL_keysym &keysym);
/** \brief Mouse processing */
bool OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos);
/** \brief Mouse processing */
bool OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos);
/** \brief Mouse processing */
bool OnMouseMotion (const int xnew, const int ynew);
/** \brief Resizes the size of the View */
bool OnVideoResize (int width, int height);
/** \brief Needs the Model to modify it */
ModelBase * mModel;
/** \brief Input might be sent to the Console, hence it is here */
Console * mConsole;
/** \brief The View which can get modified by Controller */
ViewBase *mView;
/** \brief Stores the current mouse position in screen coordinates */
int mMouseScreenPosition[2];
/** \brief Stores the current mouse position on the y=0 plane in wolrd * coordinates */
float mMouseWorldPosition[3];
/** \brief Contains all the bindings for the keyboard */
std::string mBindings[BINDING_KEYS_LAST];
friend class Engine;
};
}
#endif // _CONTROLLERBASE_H

247
engine/DrawingsGL.cc Normal file
View File

@ -0,0 +1,247 @@
#include <math.h>
#include "mathlib.h"
#include "DrawingsGL.h"
#include <GL/gl.h>
static void CubeVertices () {
// front
glVertex3f (0.5, -0.5, 0.5);
glVertex3f (0.5, -0.5, -0.5);
glVertex3f (0.5, 0.5, -0.5);
glVertex3f (0.5, 0.5, 0.5);
// back
glVertex3f (-0.5, 0.5, 0.5);
glVertex3f (-0.5, 0.5, -0.5);
glVertex3f (-0.5, -0.5, -0.5);
glVertex3f (-0.5, -0.5, 0.5);
// left
glVertex3f (-0.5, -0.5, 0.5);
glVertex3f (0.5, -0.5, 0.5);
glVertex3f (0.5, 0.5, 0.5);
glVertex3f (-0.5, 0.5, 0.5);
// right
glVertex3f (-0.5, 0.5, -0.5);
glVertex3f (0.5, 0.5, -0.5);
glVertex3f (0.5, -0.5, -0.5);
glVertex3f (-0.5, -0.5, -0.5);
// bottom
glVertex3f (-0.5, -0.5, -0.5);
glVertex3f (0.5, -0.5, -0.5);
glVertex3f (0.5, -0.5, 0.5);
glVertex3f (-0.5, -0.5, 0.5);
// top
glVertex3f (-0.5, 0.5, 0.5);
glVertex3f (0.5, 0.5, 0.5);
glVertex3f (0.5, 0.5, -0.5);
glVertex3f (-0.5, 0.5, -0.5);
}
void DrawWireCube () {
glBegin (GL_LINE_STRIP);
CubeVertices ();
glEnd ();
}
void DrawSolidCube () {
glBegin (GL_QUADS);
CubeVertices ();
glEnd ();
}
void DrawCircle () {
int i, segments;
segments = 20;
double x, z, rad, drad;
drad = (M_PI * 2) / segments;
// Top
glBegin (GL_TRIANGLE_FAN);
glVertex3f (0., 0.5, 0.);
for (i = 0; i <= segments; i++) {
rad = drad * i;
sincos (rad, &z, &x);
glVertex3f (x * 0.5, 0., -z * 0.5);
}
glEnd ();
}
void DrawTorus () {
int i, segments;
segments = 20;
double x, z, rad, drad;
drad = (M_PI * 2) / segments;
// Top
glBegin (GL_TRIANGLE_FAN);
glVertex3f (0., 0.5, 0.);
for (i = 0; i <= segments; i++) {
rad = drad * i;
sincos (rad, &z, &x);
glVertex3f (x * 0.5, 0.5, -z * 0.5);
}
glEnd ();
// Bottom
glBegin (GL_TRIANGLE_FAN);
glVertex3f (0., -0.5, 0.);
for (i = 0; i <= segments; i++) {
rad = -drad * i;
sincos (rad, &z, &x);
glVertex3f (x * 0.5, -0.5, -z * 0.5);
}
glEnd ();
// Sides
glBegin (GL_QUAD_STRIP);
for (i = 0; i <= segments; i++) {
rad = drad * i;
sincos (rad, &z, &x);
glVertex3f (x * 0.5, 0.5, -z * 0.5);
glVertex3f (x * 0.5, -0.5, -z * 0.5);
}
glEnd ();
}
void DrawPoint (float r, float x, float y, float z) {
glPointSize (r);
glBegin (GL_POINTS);
glVertex3f (x, y, z);
glEnd ();
glPointSize (1.);
}
void DrawAxis() {
glColor4f(1., 1., 1., 1.);
glBegin(GL_LINES);
glVertex3f(0., 0., 0.);
glVertex3f(1., 0., 0.);
glVertex3f(0., 0., 0.);
glVertex3f(0., 1., 0.);
glVertex3f(0., 0., 0.);
glVertex3f(0., 0., 1.);
glEnd();
// X
glPushMatrix();
glTranslatef(1., 0., 0.);
glColor3f(1., 0., 0.);
glScalef(0.2, 0.1, 0.1);
DrawCone(10);
glTranslatef(0.4, 0., 0.);
// glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'x');
glPopMatrix();
// Y
glPushMatrix();
glTranslatef(0., 1., 0.);
glRotatef(90, 0., 0., 1.);
glColor3f(0., 1., 0.);
glScalef(0.2, 0.1, 0.1);
DrawCone(10);
glTranslatef(0.4, 0., 0.);
// glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'y');
glPopMatrix();
// Z
glPushMatrix();
glTranslatef(0., 0., 1.);
glRotatef(90, 0., -1., 0.);
glColor3f(0., 0., 1.);
glScalef(0.2, 0.1, 0.1);
DrawCone(10);
glPopMatrix();
}
void DrawDisc(float radius, int segments) {
int i;
float radiant;
glBegin(GL_TRIANGLE_FAN);
glVertex3f(0., 0., 0.);
for (i = 0; i <= segments; i++) {
radiant = (float) i * ((2 * M_PI) / (float) segments);
glVertex3f(radius * cos(radiant), 0., radius * sin(radiant));
}
glEnd();
}
void DrawCircle(float radius, int segments) {
int i;
float radiant;
glBegin(GL_LINE_STRIP);
glVertex3f(0., 0., 0.);
for (i = 0; i <= segments; i++) {
radiant = (float) i * ((2 * M_PI) / (float) segments);
glVertex3f(radius * cos(radiant), 0., radius * sin(radiant));
}
glEnd();
}
void DrawCone(int segments) {
int i;
float radiant;
glBegin(GL_TRIANGLE_FAN);
glVertex3f(1., 0., 0.);
for (i = 0; i <= segments; i++) {
radiant = (float) (i * (2. * M_PI) / (float) segments);
glVertex3f(0., cos(radiant), sin(radiant));
}
glEnd();
}
void DrawVector(vector3d start, vector3d end) {
vector3d direction (end - start);
glColor3f(1., 1., 1.);
glLineWidth(2.);
glBegin(GL_LINES);
glVertex3f(start[0], start[1], start[2]);
glVertex3f(end[0], end[1], end[2]);
glEnd();
glLineWidth(1.);
vector3d right;
vector3d up;
vector3d direction_norm = direction / direction.length();
float cone_radius = 0.15;
vector3d conebottom = end - direction_norm * 0.3;
// Draw the tip
if (fabs(direction[0]) > fabs(direction[1]) && fabs(direction[2]) > fabs(direction[1])) {
up.setValues (0., 1., 0.);
vector3d right = direction.cross (up);
right = right / right.length();
int i;
int segments = 20.;
float rad_delta = 2. * M_PI / (float) segments;
double s,c;
glColor3f(0.2, 0.2, 0.5);
glPointSize (10.);
glBegin (GL_TRIANGLE_FAN);
glVertex3f (end[0], end[1], end[2]);
for (i = 0; i <= segments; i ++) {
sincos (i * rad_delta, &s, &c);
glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c);
}
glEnd ();
glBegin (GL_TRIANGLE_FAN);
glVertex3f (conebottom[0], conebottom[1], conebottom[2]);
for (i = 0; i <= segments; i ++) {
sincos (i * rad_delta, &s, &c);
glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c);
}
glEnd ();
}
}

21
engine/DrawingsGL.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _DRAWINGSGL_H
#define _DRAWINGSGL_H
#ifdef WIN32
#include <windows.h>
#endif
struct vector3d;
void DrawWireCube ();
void DrawSolidCube ();
void DrawCircle ();
void DrawTorus ();
void DrawPoint (float r, float x, float y, float z);
void DrawAxis();
void DrawDisc(float radius, int segments);
void DrawCircle(float radius, int segments);
void DrawCone(int segments);
void DrawVector(vector3d start, vector3d end);
#endif /* _DRAWINGSGL_H */

353
engine/Engine.cc Normal file
View File

@ -0,0 +1,353 @@
#include "Engine.h"
#include "Logging.h"
#include "Variables.h"
#include "Commands.h"
#include "PhysicsBase.h"
#include "CameraBase.h"
#include "ViewBase.h"
#include "ModelBase.h"
#include "ControllerBase.h"
#include "EntityFactoryBase.h"
#include "EventsBase.h"
#include <iostream>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>
#include <assert.h>
int vasprintf (char **result, const char *format, va_list *string) {
return 0;
}
namespace Engine {
/* Globaly visible classes */
Engine* EngineInstance = NULL;
/*
* Inherited Module functions
*/
int Engine::OnInit (int argc, char* argv[]) {
EngineInstance = this;
mStatus = EngineStatusUndefined;
/* Initialization of the base modules */
if (mLogging == NULL)
mLogging = new Logging ();
if (! mLogging ) {
SetError ("Could not allocate memory for Logging!");
exit (-1);
}
mLogging->Init (argc, argv);
SetStatus (EngineStatusInitializing);
if (mEventManager == NULL)
mEventManager = new EventManager();
mEventManager->Init (argc, argv);
if (mVariables == NULL)
mVariables = new Variables ();
if (! mVariables ) {
SetError ("Could not allocate memory for Variables!");
exit (-1);
}
mVariables->Init (argc, argv);
if (mCommands == NULL)
mCommands = new Commands ();
if (! mCommands ) {
SetError ("Could not allocate memory for Commands!");
exit (-1);
}
mCommands->Init (argc, argv);
// Initialize the SDL
LogDebug ("Initializing SDL");
if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) {
LogError ("Error initializing SDL: %s", SDL_GetError ());
exit (-1);
}
/* Model */
if (mModel == NULL)
mModel = new ModelBase ();
if (! mModel ) {
SetError ("Could not allocate memory for Model!");
exit (-1);
}
/* Initialization of the modules for the model */
if (mEntityFactory == NULL)
mEntityFactory = new EntityFactoryBase;
mModel->mEntityFactory = mEntityFactory;
/* Physics */
if (mPhysics == NULL) {
mPhysics = new PhysicsBase ();
if (! mPhysics ) {
SetError ("Could not allocate memory for Physics!");
exit (-1);
}
}
mModel->mPhysics = mPhysics;
mEntityFactory->Init (argc, argv);
mModel->mPhysics->Init (argc, argv);
mModel->Init (argc, argv);
/* View */
if (mView == NULL)
mView = new ViewBase ();
if (! mView ) {
SetError ("Could not allocate memory for View!");
exit (-1);
}
mView->mCamera = new CameraBase ();
mView->mCamera->Init (argc, argv);
mView->mModel = mModel;
mView->Init (argc, argv);
/* Controller */
if (mController == NULL)
mController = new ControllerBase ();
if (! mController ) {
SetError ("Could not allocate memory for Controller!");
exit (-1);
}
mController->mModel = mModel;
mController->mView = mView;
mController->Init (argc, argv);
/* Now register the commands */
mLogging->RegisterCommands ();
mVariables->RegisterCommands ();
mCommands->RegisterCommands ();
mModel->mPhysics->RegisterCommands ();
mModel->RegisterCommands ();
mView->mCamera->RegisterCommands ();
mView->RegisterCommands ();
mController->RegisterCommands ();
RegisterCommands ();
/* Now we are done */
SetStatus (EngineStatusInitialized);
return 0;
}
void Engine::OnDestroy () {
SetStatus (EngineStatusDestroying);
// Quit the SDL
SDL_Quit ();
if (mController) {
mController->Destroy ();
delete mController;
mController = NULL;
}
if (mView) {
if (mView->mCamera ) {
mView->mCamera->Destroy ();
delete mView->mCamera;
}
mView->Destroy ();
delete mView;
mView = NULL;
}
if (mModel) {
mModel->Destroy ();
if (mModel->mPhysics ) {
mModel->mPhysics->Destroy ();
delete mModel->mPhysics;
}
delete mModel;
mModel = NULL;
}
if (mCommands) {
mCommands->Destroy ();
delete mCommands;
mCommands = NULL;
}
if (mVariables) {
mVariables->Destroy ();
delete mVariables;
mVariables = NULL;
}
SetStatus (EngineStatusStopped);
if (mEventManager) {
mEventManager->Destroy();
delete mEventManager;
mEventManager = NULL;
}
if (mLogging) {
mLogging->Destroy ();
delete mLogging;
mLogging = NULL;
}
mStatus = EngineStatusUndefined;
EngineInstance = NULL;
}
/*
* Module specific functions
*/
void Engine::SetStatus (EngineStatus new_status) {
LogDebug ("EngineStatus Change: '%d' -> '%d'", mStatus, new_status);
mStatus = new_status;
}
EngineStatus Engine::GetStatus () {
return mStatus;
}
void Engine::OnMainLoop () {
SetStatus (EngineStatusRunning);
while (mStatus == EngineStatusRunning) {
mController->Process ();
mCommands->QueueExecute ();
mEventManager->Process();
mModel->UpdateTimer ();
mModel->Process ();
mView->Draw ();
}
}
void Engine::SetError (const char* str, ...) {
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
mErrorString = msg;
last_length = strlen (msg);
}
const char* Engine::GetError () {
return mErrorString.c_str();
}
/*
* Global functions
*/
void EngineSetError (const char* str, ...) {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
EngineInstance->SetError (msg);
last_length = strlen (msg);
}
const char* EngineGetError () {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
return EngineInstance->GetError ();
}
void EngineSetStatus (const EngineStatus status) {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
EngineInstance->SetStatus (status);
}
EngineStatus EngineGetStatus () {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
return EngineInstance->GetStatus ();
}
ModelBase* EngineGetModel () {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
return EngineInstance->GetModel();
}
ViewBase* EngineGetView () {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
return EngineInstance->GetView();
}
ControllerBase* EngineGetController () {
if (EngineInstance == NULL) {
std::cerr << "Error: Engine Instance not yet initialized!" << std::endl;
assert (0);
}
return EngineInstance->GetController();
}
}

167
engine/Engine.h Normal file
View File

@ -0,0 +1,167 @@
#ifndef _ENGINE_H
#define _ENGINE_H
#include <globals.h>
#include <assert.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <bitset>
#include "Module.h"
// Some ugly #defines
/** \brief Defines the number of keys that can be defined for an EntityController
*
* The current state of the controls for an entity is stored in an std::bitset
* and each bit tells us whether the key is pressed or not. Unfortunately we
* have to know the number of bits that are to be reserved in advance so this
* is hard coded into the library for now.
*/
#define ENTITY_CONTROLLER_MAX_KEY_STATES 64
namespace Engine {
class ModelBase;
class ViewBase;
class ControllerBase;
class PhysicsBase;
class EntityFactoryBase;
class EventManager;
class Logging;
class Commands;
class Variables;
class Variable;
enum EngineStatus {
EngineStatusUndefined = 0,
EngineStatusInitializing,
EngineStatusInitialized,
EngineStatusRunning,
EngineStatusStopping,
EngineStatusStopped,
EngineStatusDestroying
};
/** \brief The outermost class which contains just everything!
*
* Engine::Engine takes care of initializing, running and destroying the whole
* Engine.
*
* When Initialize called, the submodules are created by Engine and
* initializes them. There are two initialization phases:
* - Basemodules initialization:
* The first phase creates the base modules such as Engine::Logging,
* Engine::Variables and Engine::Commands and calls Initialize () on them.
* - ModelViewController initialization:
* In the second phase at first the Submodules for each Engine::ModelBase, Engine::ViewBase and
* Engine::ControllerBase are allocated an initialized. Then the Engine::ModelBase,
* Engine::View and Engine::ControllerBase Modules are initialized.
*/
class Engine : public Module {
public:
Engine () {
mModel = NULL;
mView = NULL;
mController = NULL;
mLogging = NULL;
mCommands = NULL;
mVariables = NULL;
mEntityFactory = NULL;
mEventManager = NULL;
}
virtual void MainLoop () {
OnMainLoop ();
}
void SetModel (ModelBase *model) { mModel = model; };
ModelBase* GetModel () { return mModel; };
void SetView (ViewBase *view) { mView = view; };
ViewBase* GetView () { return mView; };
void SetPhysics (PhysicsBase *physics) { mPhysics = physics; };
PhysicsBase* GetPhysics () { return mPhysics; };
void SetEntityFactory (EntityFactoryBase *entityfactory) { mEntityFactory = entityfactory; };
void SetController (ControllerBase *controller) { mController = controller; };
ControllerBase* GetController () { return mController; };
void SetLogging (Logging *logging) { mLogging = logging; };
void SetCommands (Commands *commands) { mCommands = commands; };
void SetVariables (Variables *variables) { mVariables = variables; };
void SetError (const char* str, ...);
const char* GetError ();
void SetStatus (const EngineStatus new_status);
EngineStatus GetStatus ();
private:
// Engine must not be created with the standard constructor!
// It must be ensured, that the correct EntityFactory is used!
protected:
virtual int OnInit (int argc, char* argv[]);
virtual void OnDestroy ();
virtual void OnRegisterCommands ();
void OnMainLoop ();
ModelBase *mModel;
ViewBase *mView;
ControllerBase *mController;
PhysicsBase *mPhysics;
EntityFactoryBase *mEntityFactory;
EventManager *mEventManager;
Logging *mLogging;
Commands *mCommands;
Variables *mVariables;
std::string mErrorString;
EngineStatus mStatus;
};
}
/*
* Global visible functions and classes
*/
namespace Engine {
/* Globaly visible classes */
extern Engine* EngineInstance;
/** \brief Sets the Engine::mErrorString to the given message */
void EngineSetError (const char* str, ...);
/** \brief Returns Engine::mErrorString */
const char* EngineGetError ();
/** \brief Sets the current state of the Engine */
void EngineSetStatus (const EngineStatus status);
/** \brief Returns the current state of the Engine */
EngineStatus EngineGetStatus ();
/** \brief Global access functions for the Model */
ModelBase* EngineGetModel ();
/** \brief Global access functions for the View */
ViewBase* EngineGetView ();
/** \brief Global access functions for the Controller */
ControllerBase* EngineGetController ();
}
/* Include the globally visible declarations of the other modules */
#include "LoggingGlobal.h"
#include "VariablesGlobal.h"
#include "CommandsGlobal.h"
#include "ModelBaseGlobal.h"
#include "ViewBaseGlobal.h"
#include "EventsBaseGlobal.h"
#endif // _ENGINE_H

15
engine/EngineCommands.cc Normal file
View File

@ -0,0 +1,15 @@
#include "Engine.h"
namespace Engine {
bool Cmd_EngineQuit (std::vector<std::string> args) {
EngineSetStatus (EngineStatusStopping);
return true;
}
void Engine::OnRegisterCommands () {
AddCommand ("quit", Cmd_EngineQuit);
}
}

45
engine/EntityBase.cc Normal file
View File

@ -0,0 +1,45 @@
#include "EntityBase.h"
#include "coll2d.h"
namespace Engine {
bool EntityControllerState::GetKey (int state) {
assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0);
return mKeyState.test (state);
}
void EntityControllerState::SetKey (int state) {
assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0);
LogDebug ("Setting Entity Key State %d", state);
mKeyState.set (state);
}
void EntityControllerState::UnsetKey (int state) {
assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0);
LogDebug ("Unsetting Entity Key State %d", state);
mKeyState.reset (state);
}
void EntityBase::SetControllerKeyState (int state) {
if (!mControllerState) {
LogError ("Error when trying to send a KeyState to an Entity that has no EntityControllerState!");
assert (0);
}
mControllerState->SetKey (state);
}
void EntityBase::UnsetControllerKeyState (int state) {
if (!mControllerState) {
LogError ("Error when trying to send a KeyState to an Entity that has no EntityControllerState!");
assert (0);
}
mControllerState->UnsetKey (state);
}
}

154
engine/EntityBase.h Normal file
View File

@ -0,0 +1,154 @@
#ifndef _ENTITYBASE_H
#define _ENTITYBASE_H
#include "Engine.h"
#include "mathlib.h"
#include "EntityBaseTypes.h"
namespace coll2d {
class Shape;
}
namespace Engine {
/** \brief Contains all the information for the physical simulation of the
* Entity
*
* Every Engine::Entity that should be simulated in the Engine::Physics module
* must have a Engine::EntityPhysicState counterpart. It represents the state
* of the Entity in the physical world of the engine.
*
* The Copy-Constructor and Assignment Operator is defined in EntityPhysics.cc
* as they require a call to coll2d::Shape::getCopy() which again requires the
* inclusion of coll2d.h which should stay out of Entity.h for faster
* compilation.
*/
struct EntityPhysicState {
/** \brief Standard constructor */
EntityPhysicState ():
mId (0),
mBaseType (EntityBaseTypeNone),
mPosition (0., 0., 0.),
mOrientation (0., 0., 0.),
mVelocity (0., 0., 0.),
mAngleVelocity (0.),
mRadius (0.),
mShape (NULL),
mStatic (false),
mAlive (true),
mContactNormals() {};
virtual ~EntityPhysicState() {};
/** \brief Copy constructor */
EntityPhysicState (const EntityPhysicState& state);
/** \brief Assignment operator */
EntityPhysicState& operator= (const EntityPhysicState& state);
unsigned int mId;
/** \brief The type from the game's perspective */
int mType;
/** \brief The type from the engine's perspective */
EntityBaseType mBaseType;
vector3d mPosition;
vector3d mOrientation;
vector3d mVelocity;
float mAngleVelocity;
float mRadius;
coll2d::Shape* mShape;
bool mStatic;
/** \brief If false, the entity will be removed at the end of the frame. */
bool mAlive;
vector3d &GetPosition ();
vector3d &GetVelocity ();
vector3d &GetOrientation ();
float &GetAngleVelocity ();
void SetPosition (const vector3d &position);
void SetVelocity (const vector3d &velocity);
void SetOrientation (const vector3d &orientation);
void SetAngleVelocity (const float &angle_velocity);
/** \brief Transforms the given vector in local space to world coordinates */
void Globalize (vector3d &vec);
/** \brief Transforms the given vector from world coordinates to local
* coordinates */
void Localize (vector3d &vec);
/** \brief Performs only the rotational part of Globalize() */
void GlobalizeRotation (vector3d &vec);
/** \brief Performs only the rotational part of Localize() */
void LocalizeRotation (vector3d &vec);
/** \brief Updates the shape for collision detection */
void UpdateShape ();
/** \brief Contains all the normal vectors of the planes with which we are in contact. */
std::map<unsigned int, vector3d> mContactNormals;
};
/** \brief Represents the current state of the Entity controller
*
* This struct stores the current state for all EntityControllerKeyStates
* (e.g. whether the keys are currently pressed or not).
*
* \todo [Low] The current design is very unflexible. Is there a better way?
*/
struct EntityControllerState {
std::bitset<ENTITY_CONTROLLER_MAX_KEY_STATES> mKeyState;
bool GetKey (int state);
void SetKey (int state);
void UnsetKey (int state);
};
/** \brief Represents the base of everything that exists in the engine
*
* An Engine::Entity has different representations in the different
* submodules. Engine::EntityPhysicState stores the physical representation of
* the entity like its position, velocity, orientation, and shape.
* Engine::EntityVisualState is used for drawing an Entity by Engine::View.
*/
struct EntityBase {
unsigned int mId;
/** \brief The type from the game's perspective */
int mType;
/** \brief The type from the engine's perspective */
EntityBaseType mBaseType;
EntityPhysicState *mPhysicState;
EntityControllerState *mControllerState;
/** \brief Applies the state of the EntityControllerState to the EntityPhysicState etc. */
virtual void Update (float delta_sec) {};
/** \brief Adds the given state to the current movement state of the Entity */
virtual void SetControllerKeyState (int state);
/** \brief Removes the given state to the current movement state of the Entity */
virtual void UnsetControllerKeyState (int state);
/** \brief Helper function to set the id to all substates */
void SetId (unsigned int id) {
mId = id;
if (mPhysicState)
mPhysicState->mId = id;
}
/** Executes game logic for the collision event
* \returns true if it was able to react for this type of entity, false
* false otherwise
*/
virtual bool CollisionEvent (EntityBase* entity_state) {
return false;
}
};
}
#endif // _ENTITYBASE_H

59
engine/EntityBaseTypes.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _ENTITYBASETYPES_H
#define _ENTITYBASETYPES_H
namespace Engine {
struct EntityBase;
struct EntityPhysicState;
struct EntityControllerState;
/** \brief Represents the different types of an entity from the Engines' perspective
*
* The different EntityBaseTypes define their basic behaviour when it comes to
* events such as collisions between two Entities.
*
* Here is an overview how collisions are handled:
*
* <table>
* <tr>
* <td></td>
* <td>EntityBaseTypeParticle</td>
* <td>EntityBaseTypeBlock</td>
* <td>EntityBaseTypeActor</td>
* </tr>
* <tr>
* <td>EntityBaseTypeParticle</td>
* <td>-</td>
* <td>C</td>
* <td>C</td>
* </tr>
* <tr>
* <td>EntityBaseTypeBlock</td>
* <td>C</td>
* <td>C</td>
* <td>C</td>
* </tr>
* <tr>
* <td>EntityBaseTypeActor</td>
* <td>C</td>
* <td>C</td>
* <td>C</td>
* </tr>
* </table>
*
* A 'C' means that Engine::Physics::CheckPossibleCollisionPair() will return
* true (this results that the two types cannot penetrate each other).
*/
enum EntityBaseType {
EntityBaseTypeNone = 0, /**< is the default type and does not collide with anything */
EntityBaseTypeBlock, /**< represents walls etc. that block Particles, Actors and other Blocks */
EntityBaseTypeParticle, /**< is used for projectiles or Entities that just move
but are "dumb" apart from that */
EntityBaseTypeActor, /**< is the type for objects that interact with each other */
EntityBaseTypeLast /**< marks the maximum number of EntityBaseType */
};
}
#endif // _ENTITYBASETYPES_H

117
engine/EntityFactoryBase.cc Normal file
View File

@ -0,0 +1,117 @@
#include <iostream>
#include "EntityFactoryBase.h"
#include "EntityBase.h"
#include "coll2d.h"
namespace Engine {
int EntityFactoryBase::OnInit (int argc, char* argv[]) {
LogMessage ("Initializing EntityFactory");
return 0;
}
EntityPhysicState* EntityFactoryBase::CreateEntityPhysicState (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type >> EntityBaseTypeLast ) {
LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
EntityBaseType base_type = (EntityBaseType) type;
// Create the Entity
EntityPhysicState* entity_physics = new EntityPhysicState ();
if (!entity_physics) {
LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
// default values for all entities
entity_physics->mBaseType = base_type;
// specific values for each Entity base_type
if (base_type == EntityBaseTypeNone) {
entity_physics->mShape = new coll2d::Sphere (0.01);
assert (entity_physics->mShape);
} else if (base_type == EntityBaseTypeActor) {
entity_physics->mShape = new coll2d::Sphere (0.4);
assert (entity_physics->mShape);
} else if (base_type == EntityBaseTypeBlock) {
entity_physics->mShape = new coll2d::Polygon (4);
assert (entity_physics->mShape);
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (0, vector3d (-0.5, 0., 0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (1, vector3d (0.5, 0., 0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (2, vector3d (0.5, 0., -0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (3, vector3d (-0.5, 0., -0.5));
} else if (base_type == EntityBaseTypeParticle) {
entity_physics->mShape = new coll2d::Sphere (0.05);
assert (entity_physics->mShape);
} else {
LogError ("No EntityPhysicState defined for Entity base_type '%d'", base_type);
assert (0);
}
return entity_physics;
}
EntityControllerState* EntityFactoryBase::CreateEntityControllerState (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type >> EntityBaseTypeLast ) {
LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
EntityBaseType base_type = (EntityBaseType) type;
// Create the Entity
EntityControllerState* entity_controller = NULL;
// specific values for each Entity base_type
if (base_type == EntityBaseTypeNone) {
} else if (base_type == EntityBaseTypeActor) {
entity_controller = new EntityControllerState ();
if (!entity_controller) {
LogError ("Could not allocate enough memory for EntityControllerState of type '%d'", type);
assert (0);
}
} else if (base_type == EntityBaseTypeBlock) {
} else if (base_type == EntityBaseTypeParticle) {
} else {
LogError ("No EntityPhysicState defined for Entity base_type '%d'", base_type);
assert (0);
}
return entity_controller;
}
EntityBase* EntityFactoryBase::CreateEntity (int type) {
// In this simple factory the type is simply seen as the EntityBaseType.
// However to prevent errors we do a simple check vor validity.
if (type < 0 || type >> EntityBaseTypeLast ) {
LogError ("Cannot create Entity with type %d: invalid type!", type);
assert (0);
}
EntityBaseType base_type = (EntityBaseType) type;
// Create the Entity
EntityBase *entity = new EntityBase;
entity->mBaseType = base_type;
if (!entity) {
LogError ("Could not allocate enough memory for EntityVisualState of type '%d'", type);
assert (0);
}
entity->mPhysicState = CreateEntityPhysicState (type);
entity->mControllerState = CreateEntityControllerState (type);
return entity;
}
}

View File

@ -0,0 +1,35 @@
#ifndef _ENTITYFACTORYBASE_H
#define _ENTITYFACTORYASE_H
#include "Module.h"
namespace Engine {
struct EntityBase;
struct EntityPhysicState;
struct EntityControllerState;
/** \brief Takes care of the creation of user-defined Entities
*
* To be able to define custom types of Entities we use a Object Factory for
* which the actual CreateEntityXYZState () function can be overloaded with
* the creation of custom types.
*
* The method EntityFactory::CreateEntity() will be called by the
* Model::CreateEntity() function which also takes care of registering the
* Entity to required submodules.
*/
class EntityFactoryBase : public Module {
public: EntityFactoryBase () {};
virtual EntityPhysicState* CreateEntityPhysicState (int type);
virtual EntityControllerState* CreateEntityControllerState (int type);
virtual EntityBase* CreateEntity (int type);
protected:
virtual int OnInit (int argc, char* argv[]);
};
}
#endif // _ENTITYFACTORYBASE_H

21
engine/EnumToString.h Normal file
View File

@ -0,0 +1,21 @@
// File name: "EnumToString.h"
//
// From: http://www.codeproject.com/KB/cpp/C___enums_to_strings.aspx
//
// Thanks to Marcos F. Cardoso
#undef DECL_ENUM_ELEMENT
#undef BEGIN_ENUM
#undef END_ENUM
#ifndef GENERATE_ENUM_STRINGS
#define DECL_ENUM_ELEMENT( element ) element
#define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
#define END_ENUM( ENUM_NAME ) ENUM_NAME; \
const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
#define DECL_ENUM_ELEMENT( element ) #element
#define BEGIN_ENUM( ENUM_NAME ) const char* gs_##ENUM_NAME [] =
#define END_ENUM( ENUM_NAME ) ; const char* GetString##ENUM_NAME(enum \
tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif

88
engine/EventsBase.cc Normal file
View File

@ -0,0 +1,88 @@
#include "EventsBase.h"
#include "Logging.h"
namespace Engine {
EventManager* EventManagerInstance = NULL;
int EventManager::OnInit (int argc, char* argv[]) {
EventManagerInstance = this;
return 0;
}
void EventManager::OnDestroy() {
EventManagerInstance = NULL;
}
bool EventManager::RegisterListener (const EventListenerBase *listener, const int event_type) {
LogDebug ("Registering Event listener %x for event type %d", listener, event_type);
mEventTypeListeners[event_type].push_back(listener);
return true;
}
void EventManager::Process () {
while (mQueuedEvents.size() > 0) {
TriggerEvent (mQueuedEvents.front());
mQueuedEvents.pop();
}
}
bool EventManager::QueueEvent (const EventBasePtr &event) {
if (!HasEventTypeListener(event->mEventType))
return false;
mQueuedEvents.push (event);
return true;
}
bool EventManager::TriggerEvent (const EventBasePtr &event) {
if (!HasEventTypeListener (event->mEventType))
return false;
std::vector<const EventListenerBase*>::iterator listener_iter = mEventTypeListeners[event->mEventType].begin();
for ( ; listener_iter != mEventTypeListeners[event->mEventType].end(); listener_iter++) {
if ((*listener_iter)->HandleEvent (event))
return true;
}
return false;
}
/*
* Global Functions
*/
/** \brief Registers a listener to a given event type */
bool RegisterListener (const EventListenerBase *listener, const int event_type) {
if (!EventManagerInstance) {
LogError ("Could not register EventListenerBase: EventManager not initialized!");
return false;
}
return EventManagerInstance->RegisterListener (listener, event_type);
}
/** \brief Calls all event listeners to handle the events */
bool QueueEvent (const EventBasePtr &event) {
if (!EventManagerInstance) {
LogError ("Could not queue event: EventManager not initialized!");
return false;
}
return EventManagerInstance->QueueEvent (event);
}
/** \brief Calls the listener handlers immediately */
bool TriggerEvent (const EventBasePtr &event) {
if (!EventManagerInstance) {
LogError ("Could not trigger event: EventManager not initialized!");
return false;
}
return EventManagerInstance->TriggerEvent (event);
}
};

88
engine/EventsBase.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef EVENTSBASE_H
#define EVENTSBASE_H
#include "Module.h"
#include <boost/shared_ptr.hpp>
#include <string>
#include <map>
#include <vector>
#include <queue>
namespace Engine {
/** \brief Contains all information relevant to the Event */
struct EventBase {
int mEventType;
/** \brief This might later be used for de-/serializing of the event data */
std::string mEventData;
float mEventFloat;
int mEventInt;
unsigned int mEventUnsignedInt;
};
typedef boost::shared_ptr<EventBase> EventBasePtr;
/** \brief Takes care of the handling of the Event
*
* Processing of the Event is done in the EventListenerBase::HandleEvent()
* function.
*/
class EventListenerBase {
public:
virtual ~EventListenerBase() {};
/** \brief Handles the Event */
virtual bool HandleEvent (const EventBasePtr &event) const = 0;
void just_for_the_vtable() {};
/** \brief For debugging */
std::string mName;
};
typedef boost::shared_ptr<EventListenerBase> EventListenerPtr;
/** \brief Keeps track of all the EventListenerBase objects and receives Event notifications.
*/
class EventManager : public Module {
public:
virtual ~EventManager() {};
/** \brief Registers a listener to a given event type */
bool RegisterListener (const EventListenerBase *listener, const int event_type);
/** \brief Calls all event listeners to handle the events */
void Process ();
/** \brief Queues the until Process() gets called */
bool QueueEvent (const EventBasePtr &event);
/** \brief Calls the listener handlers immediately */
bool TriggerEvent (const EventBasePtr &event);
/** \brief Returns true if there is a listener for a given event type */
bool HasEventTypeListener (const int event_type) {
return mEventTypeListeners.find(event_type) != mEventTypeListeners.end();
}
/** \brief Returns the number of listeners for a given event type */
unsigned int GetEventTypeListenerCount (const int event_type) {
if (!HasEventTypeListener (event_type))
return 0;
return (mEventTypeListeners.find(event_type))->second.size();
}
unsigned int GetQueuedEventCount () {
return mQueuedEvents.size();
}
protected:
virtual int OnInit (int argc, char* argv[]);
virtual void OnDestroy();
private:
/** \brief Contains for each event type the list of listeners */
std::map <int, std::vector<const EventListenerBase*> > mEventTypeListeners;
std::queue <EventBasePtr> mQueuedEvents;
};
}
#endif /* EVENTSBASE_H */

16
engine/EventsBaseGlobal.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef EVENTSBASEGLOBAL_H
#define EVENTSBASEGLOBAL_H
#include "EventsBase.h"
namespace Engine {
/** \brief Registers a listener to a given event type */
bool RegisterListener (const EventListenerBase *listener, const int event_type);
/** \brief Calls all event listeners to handle the events */
bool QueueEvent (const EventBasePtr &event);
/** \brief Calls the listener handlers immediately */
bool TriggerEvent (const EventBasePtr &event);
}
#endif /* EVENTSBASEGLOBAL_H */

7
engine/GameEntityBase.cc Normal file
View File

@ -0,0 +1,7 @@
#include "EntityBase.h"
#include "ModelBase.h"
namespace Engine {
}

173
engine/Logging.cc Normal file
View File

@ -0,0 +1,173 @@
#include "Logging.h"
#include <iostream>
#include <cstdarg>
#include <cstdlib>
#include <assert.h>
namespace Engine {
static Logging *LoggingInstance = NULL;
static LogLevel requested_level = LOG_DEFAULT_LEVEL;
/*
* Inherited Module functions
*/
int Logging::OnInit (int argc, char* argv[]) {
mPrintLevel = requested_level;
Log (LogLevelDebug, "Logging Init");
LoggingInstance = this;
return 0;
}
void Logging::OnDestroy () {
Log (LogLevelDebug, "Logging Destroy");
LoggingInstance = NULL;
}
/*
* Module specific functions
*/
void Logging::Log (LogLevel level, const char *str, ...) {
if (level < mPrintLevel)
return;
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
static int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
last_length = strlen (msg);
assert (last_length < LOG_MAX_MESSAGE_LENGTH);
std::cout << msg << std::endl;
LogEntry log_entry (level, msg);
mLogEntries.push_back (log_entry);
}
void Logging::SetLogPrintLevel (LogLevel print_level) {
mPrintLevel = print_level;
}
const LogEntry &Logging::GetLastEntry () {
static LogEntry null_message (LogLevelMessage, "");
if (mLogEntries.size() > 0) {
return mLogEntries[mLogEntries.size() - 1];
}
return null_message;
}
/*
* Globally visible functions
*/
void SetLogPrintLevel (LogLevel print_level) {
if (!LoggingInstance) {
requested_level = print_level;
return;
}
LoggingInstance->SetLogPrintLevel (print_level);
}
void LogError (const char* str, ...) {
assert (LoggingInstance);
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
static int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
last_length = strlen (msg);
assert (last_length < LOG_MAX_MESSAGE_LENGTH);
LoggingInstance->Log (LogLevelError, msg);
}
void LogWarning (const char* str, ...) {
assert (LoggingInstance);
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
static int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
last_length = strlen (msg);
assert (last_length < LOG_MAX_MESSAGE_LENGTH);
LoggingInstance->Log (LogLevelWarning, msg);
}
void LogMessage (const char* str, ...) {
assert (LoggingInstance);
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
static int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
last_length = strlen (msg);
assert (last_length < LOG_MAX_MESSAGE_LENGTH);
LoggingInstance->Log (LogLevelMessage, msg);
}
void LogDebug (const char* str, ...) {
assert (LoggingInstance);
static char msg[LOG_MAX_MESSAGE_LENGTH];
static int last_length = LOG_MAX_MESSAGE_LENGTH;
static int i;
for (i = 0; i < last_length; i++)
msg[i] = 0;
va_list ap;
va_start(ap, str);
vsprintf(msg, str, ap);
va_end(ap);
last_length = strlen (msg);
assert (last_length < LOG_MAX_MESSAGE_LENGTH);
LoggingInstance->Log (LogLevelDebug, msg);
}
const LogEntry& GetLastLogEntry () {
assert (LoggingInstance);
return LoggingInstance->GetLastEntry ();
}
}

34
engine/Logging.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef _LOGGING_H
#define _LOGGING_H
#include "Engine.h"
namespace Engine {
class Module;
/** \brief All logging goes through this class
*/
class Logging : public Module {
public:
void Log (LogLevel level, const char *str, ...);
void SetLogPrintLevel (LogLevel print_level);
/** \brief Returns the last LogEntry that was sent to the Logging module
*/
const LogEntry& GetLastEntry ();
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
virtual void OnDestroy ();
private:
LogLevel mPrintLevel;
/** \brief Stores all log messages that were sent to the Logging module
* \todo Restrict the number of entries to be stored!
*/
std::vector<LogEntry> mLogEntries;
};
}
#endif // _LOGGING_H

45
engine/LoggingGlobal.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef _LOGGINGLOBAL_H
#define _LOGGINGLOBAL_H
namespace Engine {
enum LogLevel {
LogLevelDebug = 0,
LogLevelWarning,
LogLevelMessage,
LogLevelError
};
/** \brief Represents a log message along with its level
*/
struct LogEntry {
LogEntry (LogLevel level, const char* message) {
mLevel = level;
mMessage = message;
}
/** \brief the level of the message */
LogLevel mLevel;
/** \brief the message itself */
const char *mMessage;
};
/* Global visible functions */
/** \brief Sets the level for which messages should be printed out */
void SetLogPrintLevel (LogLevel print_level);
/** \brief Sends the Message to the Logging system */
void LogError (const char* str, ...);
/** \brief Sends the Message to the Logging system */
void LogWarning (const char* str, ...);
/** \brief Sends the Message to the Logging system */
void LogMessage (const char* str, ...);
/** \brief Sends the Message to the Logging system */
void LogDebug (const char* str, ...);
/** \brief Returns the last LogEntry sent to the Logging system */
const LogEntry &GetLastLogEntry ();
}
#endif // _LOGGINGLOBAL_H

295
engine/ModelBase.cc Normal file
View File

@ -0,0 +1,295 @@
#include "ModelBase.h"
#include "PhysicsBase.h"
#include "EntityBase.h"
#include "EntityFactoryBase.h"
#include <coll2d.h>
#include <assert.h>
namespace Engine {
static ModelBase* ModelInstance = NULL;
/*
* Inherited Module functions
*/
int ModelBase::OnInit (int argc, char* argv[]) {
LogDebug ("Model Init");
ModelInstance = this;
mKilledEntities.clear ();
mEntityIdCounter = 0;
mDeltaSec = 0.;
return 0;
}
void ModelBase::OnDestroy () {
LogDebug ("Model Destroy");
std::map<unsigned int, EntityBase*>::iterator iter = mEntities.begin ();
std::map<unsigned int, EntityBase*>::iterator next = iter;
// Since DestroyEntity () also erases the entry in mEntities iter gets
// invalid. Therefore we have to store the iterator that follows iter
while (iter != mEntities.end ()) {
next = iter;
next ++;
DestroyEntity (iter->first);
iter = next;
}
ModelInstance = NULL;
}
/*
* Model specific functions
*/
void ModelBase::Process () {
// Process the controllers and state of all entities
std::map<unsigned int, EntityBase*>::iterator entity_iter = mEntities.begin();
do {
if (entity_iter->second == NULL) {
LogError ("Entity with id %d does not exist!", entity_iter->first);
assert (0);
}
entity_iter->second->Update(mDeltaSec);
entity_iter++;
} while (entity_iter != mEntities.end());
// simulate the world
mPhysics->Simulate (mDeltaSec, this);
// remove killed entities
unsigned int i;
for (i = 0; i < mKilledEntities.size(); i++)
UnregisterEntity (mKilledEntities[i]);
mKilledEntities.clear();
}
EntityBase* ModelBase::CreateEntity (int type) {
// Create a new entity id
unsigned int entity_id = CreateEntityId();
// Call the event
OnCreateEntity (type, entity_id);
// Create the entity
EntityBase* result = mEntityFactory->CreateEntity (type);
result->SetId (entity_id);
// And register it
RegisterEntity (result);
return result;
}
void ModelBase::RegisterEntity (EntityBase* entity) {
unsigned int id = entity->mId;
LogDebug ("Registering Entity with id '%d'", id);
if (mEntities.find(id) != mEntities.end ()) {
LogError ("Replacing Entity with id '%d'", id);
assert (0);
}
if (entity->mPhysicState)
mPhysics->RegisterEntity (entity->mPhysicState);
mEntities[id] = entity;
}
void ModelBase::KillEntity (const unsigned int id) {
std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);
if (iter == mEntities.end ()) {
LogError ("Could not kill Entity with id '%d': Entity not found!", id);
assert (0);
return;
} else {
EntityBase *entity = iter->second;
// call the event handler
OnKillEntity (entity);
if (entity->mPhysicState) {
entity->mPhysicState->mAlive = false;
}
mKilledEntities.push_back (iter->first);
}
}
void ModelBase::UnregisterEntity (const unsigned int id) {
std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);
if (iter == mEntities.end ()) {
LogError ("Could not unregister Entity with id '%d': Entity not found!", id);
assert (0);
return;
} else {
EntityBase *entity = iter->second;
if (entity->mPhysicState) {
mPhysics->UnregisterEntity (id);
entity->mPhysicState = NULL;
}
delete entity;
mEntities.erase (iter);
}
}
EntityBase* ModelBase::GetEntity (const unsigned int id) {
std::map<unsigned int, EntityBase*>::iterator iter = mEntities.find (id);
if (iter != mEntities.end ()) {
return iter->second;
}
return NULL;
}
unsigned int ModelBase::CreateEntityId () {
return ++mEntityIdCounter;
}
void ModelBase::ClearEntities () {
LogDebug ("Clearing all %d entities.", mEntities.size());
std::map<unsigned int, EntityBase*>::iterator iter = mEntities.begin ();
std::map<unsigned int, EntityBase*>::iterator next = iter;
// Since DestroyEntity () also erases the entry in mEntities iter gets
// invalid. Therefore we have to store the iterator that follows iter
while (iter != mEntities.end ()) {
next = iter;
next ++;
DestroyEntity (iter->first);
iter = next;
}
mEntityIdCounter = 0;
}
unsigned int ModelBase::GetPlayerEntityId () {
return mPlayerEntityId;
}
/**
* \param collision_time The time when the collision occured relative to the start of the simulation frame
*/
void ModelBase::SendEntityCollisionEvent (const unsigned int reference_entity_id,
const unsigned int incidence_entity_id, float collision_time, vector3d normal) {
// Check whether we report the same contact over and over
static unsigned int last_reference_entity_id = 0;
static unsigned int last_incidence_entity_id = 0;
static float last_collision_time = -1.;
if (!reference_entity_id || !incidence_entity_id)
return;
if (last_reference_entity_id != 0) {
if (last_reference_entity_id == reference_entity_id
&& last_incidence_entity_id == incidence_entity_id
&& last_collision_time == collision_time) {
return;
}
}
// update the last_* entries
last_reference_entity_id = reference_entity_id;
last_incidence_entity_id = incidence_entity_id;
last_collision_time = collision_time;
// now we need to get the Entities and check whether we should
// call EntityBase::CollisionEvent()
EntityBase* reference_entity = GetEntity (reference_entity_id);
EntityBase* incidence_entity = GetEntity (incidence_entity_id);
LogDebug ("Received collision type ref = %d type inc = %d",
reference_entity->mType,
incidence_entity->mType);
// If the incidence entity accepted the collision we can return, otherwise
// we perform the symmetric collision event.
if (incidence_entity->CollisionEvent (reference_entity))
return;
reference_entity->CollisionEvent (incidence_entity);
}
/*
* Global functions
*/
unsigned int GetPlayerEntityId () {
if (!ModelInstance) {
LogError ("Couldn't create Entity: Model not initialized!");
assert (0);
}
return ModelInstance->GetPlayerEntityId ();
}
float GetFrameDuration () {
if (!ModelInstance) {
LogError ("Couldn't create Entity: Model not initialized!");
assert (0);
}
return ModelInstance->GetFrameDuration ();
}
EntityBase * CreateEntity (int type) {
if (!ModelInstance) {
LogError ("Couldn't create Entity: Model not initialized!");
assert (0);
}
EntityBase *result = ModelInstance->CreateEntity (type);
return result;
}
void DestroyEntity (unsigned int id) {
if (!ModelInstance) {
LogError ("Couldn't destroy Entity: Model not initialized!");
assert (0);
}
ModelInstance->UnregisterEntity (id);
}
void KillEntity (unsigned int id) {
if (!ModelInstance) {
LogError ("Couldn't kill Entity: Model not initialized!");
assert (0);
}
ModelInstance->KillEntity (id);
}
EntityBase * GetEntity (unsigned int id) {
if (!ModelInstance) {
LogError ("Couldn't execute GetEntity(): Model not initialized!");
assert (0);
}
return ModelInstance->GetEntity (id);
}
EntityPhysicState * GetEntityPhysicState (unsigned int id) {
EntityBase *entity = GetEntity (id);
if (entity)
return entity->mPhysicState;
return NULL;
}
}

140
engine/ModelBase.h Normal file
View File

@ -0,0 +1,140 @@
#ifndef _MODELBASE_H
#define _MODELBASE_H
#include "Engine.h"
#include "EntityBase.h"
namespace Engine {
class Module;
class PhysicsBase;
class Events;
class EntityFactoryBase;
struct EntityBase;
/** \brief Represents the current state of the Engine
*
* Represents the State of the Engine and is unaware of anything except itself.
* It manages all Entities, Physics, etc.
*
* It has the following subsystems:
* - Events
* - Physics
* - Variables
*
* \note
* To create an Entity one must call Engine::CreateEntity() which will
* allocate the Entity and performs all the required registrations by calling
* Model::RegisterEntity().
*/
class ModelBase : public Module {
public:
/** Performs simulation, gamelogic, etc */
virtual void Process ();
/** Updates the timer */
void UpdateTimer () {
static float last_frame = 0;
float current_frame = static_cast<float> (SDL_GetTicks ()) * 1.0e-3;
mDeltaSec = current_frame - last_frame;
last_frame = current_frame;
}
float GetFrameDuration () {
return mDeltaSec;
}
/** Adds the given Entity to the Model */
void RegisterEntity (EntityBase *entity);
/** Removes the Entity with the given id */
void UnregisterEntity (const unsigned int id);
/** Marks an Engine::Entity as killed so that it gets removed after the
* simulation */
void KillEntity (const unsigned int id);
/** Creates an Entity of the given type
*
* This calls the CreateEntity() function on the Model::mEntityFactory to
* create the proper Entity and registers it to the required submodules
* (so far only Model::mPhysics).
*/
EntityBase* CreateEntity (int type);
/** Returns the Entity with the given id */
EntityBase* GetEntity (const unsigned int id);
/** Returns a unused id for an Entity */
unsigned int CreateEntityId ();
/** Removes all Entities */
void ClearEntities ();
/** Returns the id of the entity the player is currently controlling */
unsigned int GetPlayerEntityId ();
/** Notifies the gamelogic of a collision event */
void SendEntityCollisionEvent (const unsigned int reference_entity_id,
const unsigned int incidence_entity_id, float collision_time, vector3d normal);
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
virtual void OnDestroy ();
/** \brief Gets called when an entity of the given type is being created */
virtual void OnCreateEntity (const int type, const unsigned int id) {};
/** \brief Gets called when an entity is marked to be killed */
virtual void OnKillEntity (const EntityBase *entity) {};
PhysicsBase *mPhysics;
Events *mEvents;
/** \brief Creates Entities */
EntityFactoryBase *mEntityFactory;
/** \brief contains all Engine::Entities */
std::map<unsigned int, EntityBase *> mEntities;
/** \brief contains all Engine::Entities that ceased to exist */
std::vector<unsigned int> mKilledEntities;
unsigned int mEntityIdCounter;
unsigned int mPlayerEntityId;
float mDeltaSec;
friend class ViewBase;
friend class Engine;
friend class Controller;
};
/** \brief Creates an Entity of the given type and registers at the required modules *
*
* It allocates the memory needed to represent that specific type of Entity.
* At also performs the required registrations at the various Modules.
*
* All Entities that are created with this function get also destroyed in
* Model::Destroy () by calling DestroyEntity () for this Entity.
*
* \note All Entities must be created with this function if you intend to keep
* the different Modules in sync!
*/
EntityBase * CreateEntity (int type);
/** \brief Frees the Memory required by the Entity and unregisters it
*
* This function also frees the memory the Entity is using in its
* different representitions such as EntityVisualState and EntityPhysicState.
*
* It also unregisters the Entity from the different Modules where it was
* registered by CreateEntity.
*/
void DestroyEntity (unsigned int id);
/** \brief Tells the model that the entity is no more existant in the game
* world */
void KillEntity (unsigned int id);
/** \brief Returns the Entity with the given id, NULL otherwise */
EntityBase * GetEntity (unsigned int id);
/** \brief Returns the physical state of the Entity with the given id, NULL otherwise */
EntityPhysicState * GetEntityPhysicState (unsigned int id);
}
#endif // _MODEL_H

15
engine/ModelBaseGlobal.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _MODELGLOBAL_H
#define _MODELGLOBAL_H
namespace Engine {
/** \brief Adds the function callback as command for the given name*/
unsigned int GetPlayerEntityId ();
/** \brief Returns the duration of the frame in seconds */
float GetFrameDuration ();
}
#endif /* _MODELGLOBAL_H */

51
engine/Module.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef MODULE_H
#define MODULE_H
namespace Engine {
/** \brief Base class for the separate modules
*
* All Modules that are managed by the Engine::Engine base Module. Everything
* else is a Submodule of that or a Submodule of a Submodule.
*/
class Module {
public:
/** \brief Initializes the Module
*
* \note This function must only return if it was able to initialize
* successfully, otherwise it should log an error with LogError and exit
* the program itself!
*
* In general at first the Submodules get initialized and then the Module
* that has pointers to the Submodules. Exception: The main Module
* Engine::Engine.
*
* \note
* The function Engine::OnInit () performs the allocation
* and Initialization of all its Submodules.
*/
virtual int Init (int argc, char* argv[]) { return OnInit (argc, argv); }
/** \brief Frees the used memory of the Module
*
* The Destroy function is always called at first for the Module itself
* and then for its submodules so that a Module is still completely
* working. Again for the main Module Engine::Engine the reverse is the
* case (such as in Module::Init).
*/
virtual void Destroy () { OnDestroy (); }
/** \brief Calls the function that registers its commands to the
* Commandsystem */
virtual void RegisterCommands () { OnRegisterCommands (); }
protected:
/** \brief The actual function being called when Init () is called */
virtual int OnInit (int argc, char* argv[]) = 0;
/** \brief Frees the memory */
virtual void OnDestroy () { };
/** \brief Registers the commands of the Module */
virtual void OnRegisterCommands () { };
};
}
#endif /* MODULE_H */

25
engine/OverlayBase.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef OVERLAY
#define OVERLAY
#include <SDL/SDL.h>
namespace Engine {
class OverlayBase {
public:
OverlayBase () {};
virtual ~OverlayBase() {};
virtual bool OnKeyDown (const SDL_keysym &keysym) { return false; };
virtual bool OnKeyUp (const SDL_keysym &keysym) { return false; };
virtual bool OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) { return false; };
virtual bool OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) { return false; };
virtual void Draw () = 0;
void _strange_function_for_vtable () {};
};
}
#endif /* OVERLAY */

604
engine/PhysicsBase.cc Normal file
View File

@ -0,0 +1,604 @@
#include "PhysicsBase.h"
#include "EntityBase.h"
// needed for GetEntityGameState() in ProcessCollisionEvent()
#include "ModelBase.h"
#include "coll2d.h"
#define EPSILON 1.0e-4
namespace Engine {
/*
* Inherited Module functions
*/
int PhysicsBase::OnInit (int argc, char* argv[]) {
LogDebug ("Physics Init");
mEntities.clear ();
return 0;
}
void PhysicsBase::OnDestroy () {
LogDebug ("Physics Destroy");
}
/*
* Module specific functions
*/
void PhysicsBase::Move (float delta_msec) {
EntityPhysicState* entity = NULL;
std::map<unsigned int, EntityPhysicState*>::iterator entity_iter;
std::map<unsigned int, EntityPhysicState*>::iterator collision_iter;
vector3d velocity, orientation;
for (entity_iter = mEntities.begin (); entity_iter != mEntities.end(); entity_iter++){
entity = entity_iter->second;
velocity = entity->GetVelocity ();
if (velocity.length2() == 0.)
continue;
CheckContactCache (entity);
entity->SetPosition (entity->GetPosition () + velocity * delta_msec);
entity->SetOrientation (vector3d(0., entity->GetOrientation()[1] + entity->GetAngleVelocity() * delta_msec, 0.));
}
}
int PhysicsBase::Simulate (float msec, ModelBase* model) {
vector3d velocity, orientation;
unsigned int entity_a_id, entity_b_id;
int resolve_steps = 0;;
float current_time = 0, stepsize = msec / (float) 10;
while (current_time < msec) {
if (msec - current_time < stepsize)
stepsize = msec - current_time;
coll2d::CollisionInfo info;
bool collision_result = false;
collision_result = CalcNextCollision (stepsize, entity_a_id, entity_b_id, info);
if (collision_result == 0) {
// there is no collision, so we integrate to the end of
// the timestep
current_time += stepsize;
Move (stepsize);
} else {
LogDebug ("Collision between %u and %u", entity_a_id, entity_b_id);
float collision_remaining_step = stepsize;
resolve_steps = 0;
// This is the collision loop. It loops until there is no collision any
// more within the stepsize.
while (collision_remaining_step > 0.) {
// with this we count how often we iterate through and can
// detect certain false behaviour
resolve_steps ++;
// info.time tells us in what time the contact will happen and
// theoretically we can move to that point, however due to round-off
// errors we might end up in a situation where the Entities overlap
// which we must prevent to keep the collision detection working.
//
// Therefore we only move to a portion of the time and resolve the
// collision then. This alpha variable controls how much of the time
// we skip.
float alpha = 0.001;
// If the timestep is already very small, we try to resolve it
// immediately.
if (info.time < EPSILON ) {
// 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.normal);
}
// 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.normal);
}
collision_remaining_step -= info.time * (1 - alpha);
}
// At this point we resolved the collision we had found. Now we need
// to check whether there is a new one
collision_result = CalcNextCollision (collision_remaining_step, entity_a_id, entity_b_id, info);
// If there was none, we happily move on to the end of the frame and
// set collision_remaining_step to 0 so that we jump out of the
// collision loop.
if (!collision_result) {
Move (collision_remaining_step);
collision_remaining_step = 0.;
break;
}
assert (resolve_steps <= 50);
}
}
}
/* Postcondition: we simulated exactly msec milliseconds, no less, no more! */
assert (current_time == msec);
return 0;
}
void PhysicsBase::RegisterEntity (EntityPhysicState* entity) {
unsigned int id = entity->mId;
LogDebug ("Registering EntityPhysicState with id '%d'", id);
if (mEntities.find (id) == mEntities.end ())
mEntities[id] = entity;
else {
LogError ("Physics already has registered an Entity with id '%d'", id);
assert (0);
}
}
void PhysicsBase::UnregisterEntity (const unsigned int id) {
std::map<unsigned int, EntityPhysicState*>::iterator iter = mEntities.find(id);
LogDebug ("Unegistering EntityPhysicState with id '%d'", id);
if (iter != mEntities.end ()) {
// Remove all the references of existing contacts to the Entity that is
// going to be deleted.
std::map<unsigned int, vector3d>::iterator temp_iter, contact_iter = iter->second->mContactNormals.begin();
while (contact_iter != iter->second->mContactNormals.end()) {
temp_iter = contact_iter;
contact_iter ++;
ContactCacheRemove (id, temp_iter->first);
}
// Remove the Entity from mEntities and erase the Entity
EntityPhysicState* entity_physic_state = iter->second;
mEntities.erase (iter);
// We also have to delete the state!
if (entity_physic_state->mShape)
delete entity_physic_state->mShape;
delete entity_physic_state;
} else {
LogError ("Could not unegister EntityPhysicState with id '%d': Entity not found!", id);
assert (0);
}
}
/**
* This function also sorts out collisions that might be invalid such as
* Particle-Particle collision and the like.
*/
inline bool PhysicsBase::CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b) {
// no collision checks against itself
if (entity_a == entity_b)
return false;
// no checks if both are static
if (entity_a->mStatic && entity_a->mStatic)
return false;
// no checks if both are particles
if (entity_a->mBaseType == EntityBaseTypeParticle
&& entity_b->mBaseType == EntityBaseTypeParticle)
return false;
// no checks if both are particles
if (entity_a->mBaseType == EntityBaseTypeBlock
&& entity_b->mBaseType == EntityBaseTypeBlock)
return false;
return true;
}
/**
* \param stepsize The timestep for which we want to check whether a
* collision occurs.
* \param reference_entity_id If a collision occurs the Id of the entity that
* is the reference entity is stored in this variable.
* \param incidence_entity_id If a collision occurs the Id of the entity that
* crashes into the reference entity is stored in this variable.
* \param info Contains information about the collision (normal, time, point)
*
* This function calls PhysicsBase::CheckPossibleCollisionPair() to sort out
* invalid collision pairs.
*/
bool PhysicsBase::CalcNextCollision (
float stepsize,
unsigned int &reference_entity_id,
unsigned int &incidence_entity_id,
coll2d::CollisionInfo &info) {
std::map<unsigned int, EntityPhysicState*>::iterator collision_iter;
std::map<unsigned int, EntityPhysicState*>::iterator collision_ref;
// We have to find the next collision, so we check everything and
// take the first one that will happen.
coll2d::CollisionInfo temp_info;
info.time = 2.;
for (collision_ref = mEntities.begin (); collision_ref != mEntities.end (); collision_ref ++) {
if (collision_ref->second->mId != 0) {
collision_ref->second->UpdateShape();
}
}
for (collision_ref = mEntities.begin (); collision_ref != mEntities.end (); collision_ref ++) {
if (collision_ref->second->mAlive == false)
continue;
for (collision_iter = collision_ref; collision_iter != mEntities.end (); collision_iter ++) {
if (collision_iter->second->mAlive == false)
continue;
if (!CheckPossibleCollisionPair(collision_ref->second, collision_iter->second))
continue;
EntityPhysicState *entity_a, *entity_b;
entity_a = collision_ref->second;
entity_b = collision_iter->second;
int coll2d_result = coll2d::check_collision (stepsize, entity_a->mShape, entity_b->mShape, &temp_info);
if (!HandleColl2dError (coll2d_result, stepsize, entity_a, entity_b, temp_info)) {
LogError ("Could not handle coll2d error: %d\n", coll2d_result);
assert (0);
}
if (coll2d_result > 0 && temp_info.time < info.time ) {
info = temp_info;
assert (info.reference_shape >= 0);
if (info.reference_shape == 0) {
reference_entity_id = collision_ref->first;
incidence_entity_id = collision_iter->first;
} else {
reference_entity_id = collision_iter->first;
incidence_entity_id = collision_ref->first;
//assert (0);
}
}
}
}
if (info.time != 2.)
return true;
return false;
}
/**
* This function updates the velocity of the Entity with id of
* incidence_entity_id so that it no more is in collision with with Entity
* with id reference_entity_id. Additionally to that we also cache the
* contact normals and check against them so that the new velocity does not
* violate previous contacts.
*
* We store the cached contact normals both in the incidence and the reference
* entity and we have to make sure that these references get cleared once one
* of the two get deleted! This is done in PhysicsBase::UnregisterEntity().
*/
void PhysicsBase::ResolveCollision (float stepsize, unsigned int reference_entity_id, unsigned int incidence_entity_id, coll2d::CollisionInfo &info) {
EntityPhysicState* reference_entity = mEntities[reference_entity_id];
EntityPhysicState* incidence_entity = mEntities[incidence_entity_id];
if (!reference_entity || !incidence_entity) {
LogError ("Invalid entity IDs passed to %s: %u and %u", __FUNCTION__, reference_entity_id, incidence_entity_id);
}
assert (reference_entity && incidence_entity);
// So far only resolving of collision of sphere collisions is
// allowed
assert (dynamic_cast<coll2d::Sphere*> (incidence_entity->mShape));
// First we calculate the velocity along the normal and then we calculate
// the velocity that is tangential to the plane and set the new velocity to
// it.
vector3d new_velocity = incidence_entity->mVelocity;
new_velocity -= reference_entity->mVelocity;
// It should be greater zero otherwise there was an error in the collision
// detection.
assert (new_velocity.length2 ());
// The scalar proj tells us how far we went along the normal
float proj = new_velocity * info.normal;
// As there is a collision, and the incidence_entity is moving towards the
// plane, this value must be strictly smaller than zero, otherwise we would
// not penetrate the plane.
if (proj > 0.) {
LogError ("Projection invalid: %e", proj);
info.doPrint ("Collision Info:\n");
}
assert (proj < 0.);
// Collision handling start:
// This is the code that tells us how we deal with collisions and how to
// prevent them. To get real dynamics one has to adjust this section:
vector3d old_velocity = incidence_entity->mVelocity;
new_velocity = old_velocity + info.normal * proj * (-1.0);
incidence_entity->SetVelocity (new_velocity);
// Collision handling end
// And we also add the normal to the cached contacts for both incidence
// and reference entity
ContactCacheAdd (incidence_entity, reference_entity, info.normal);
// Now we check whether we are violating any of the cached contacts. For
// this we loop through all contacts and try to adjust the velocity of
// incidence_entity until we no more violate the cached contacts.
std::map<unsigned int, vector3d>::iterator iter = incidence_entity->mContactNormals.begin();
int readjust = 0;
while (iter != incidence_entity->mContactNormals.end()) {
vector3d contact_normal = iter->second;
float contact_velocity = new_velocity * contact_normal;
if (contact_velocity < 0.) {
if (reference_entity_id == iter->first) {
// In this case, the projection was not good enough. We simultaneously
// damp the velocity and push a little harder. If it was damped too
// much or we had to readjust too often, we set the velocity to zero.
LogDebug ("Resolved collision needs to be readjusted");
new_velocity = incidence_entity->GetVelocity();
new_velocity *= 0.5;
if (new_velocity.length2() < EPSILON)
new_velocity.setValues (0., 0., 0.);
else
new_velocity += info.normal * 0.001;
incidence_entity->SetVelocity (new_velocity);
readjust++;
// More than 10 readjusts? -> set velocity to zero
if (readjust > 10)
new_velocity.setValues (0., 0., 0.);
// and we redo all the checking:
iter = incidence_entity->mContactNormals.begin();
continue;
}
// In this case the proposed velocity is violating another contact
// and we set the velocity to zero.
LogDebug ("Cached collision: %e -> resetting velocity", contact_velocity);
// contact_normal.print ("contact normal: ");
new_velocity.setValues (0., 0., 0.);
incidence_entity->mVelocity = new_velocity;
} else if (contact_velocity > 0.01) {
// If we move sufficiently fast away from a contact we remove the
// contact from the cache.
// * Attention! * As the function ContactCacheRemove() deletes an entry
// of mContactNormals we have to increase the iterator *before* we
// remove the cache. Otherwise the iterator might become invalid!
unsigned int contact_id = iter->first;
iter++;
ContactCacheRemove (contact_id, incidence_entity_id);
continue;
}
iter++;
}
}
/*
* Contact Cache Functions
*/
void PhysicsBase::ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal) {
incidence_entity->mContactNormals[reference_entity->mId] = normal;
reference_entity->mContactNormals[incidence_entity->mId] = normal * -1.;
LogDebug ("Adding normal (%f,%f,%f) id=%d to entity %d",
normal[0], normal[1], normal[2],
reference_entity->mId, incidence_entity->mId);
LogDebug ("Adding normal (%f,%f,%f) id=%d to entity %d",
normal[0] * -1., normal[1] * -1., normal[2] * -1.,
incidence_entity->mId, reference_entity->mId);
}
void PhysicsBase::ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id) {
assert (entity_a_id != entity_b_id);
// LogDebug ("Removing start %d and %d", entity_a_id, entity_b_id);
EntityPhysicState *entity_a, *entity_b;
#ifdef WIN32
entity_a = mEntities[entity_a_id];
entity_b = mEntities[entity_b_id];
#else
entity_a = mEntities.at(entity_a_id);
entity_b = mEntities.at(entity_b_id);
#endif
// Check the entries exist
assert (entity_a->mContactNormals.find (entity_b_id) != entity_a->mContactNormals.end());
assert (entity_b->mContactNormals.find (entity_a_id) != entity_b->mContactNormals.end());
#ifdef WIN32
vector3d contact_normal = entity_a->mContactNormals[entity_b_id];
#else
vector3d contact_normal = entity_a->mContactNormals.at(entity_b_id);
#endif
LogDebug ("Removing normal (%f,%f,%f) id=%d from entity %d",
contact_normal[0], contact_normal[1], contact_normal[2],
entity_a_id, entity_b_id);
entity_a->mContactNormals.erase (entity_a->mContactNormals.find(entity_b_id));
#ifdef WIN32
contact_normal = entity_b->mContactNormals[entity_a_id];
#else
contact_normal = entity_b->mContactNormals.at(entity_a_id);
#endif
LogDebug ("Removing normal (%f,%f,%f) id=%d from entity %d",
contact_normal[0], contact_normal[1], contact_normal[2],
entity_b_id, entity_a_id);
entity_b->mContactNormals.erase (entity_b->mContactNormals.find(entity_a_id));
// LogDebug ("Removing done!");
}
/** \brief Checks whether we are still in contact with the entities stored in mContactNormals
*
* To check whether we still are in contact, we modify temporarily the
* velocity of the given Entity that it moves towards the contact point (i.e.
* we add the negative normal to the velocity) and re-check for a collision.
* If the collision has a time value of 0.0 and the reported normal stays the
* same, we know, that the two Entities are still in contact. If not, we lost
* contact.
*
* We can skip the test, if the scalar product of the normal and velocity are
* positive (in this case we move away from the plane)
*/
void PhysicsBase::CheckContactCache (EntityPhysicState* entity) {
std::map<unsigned int, vector3d>::iterator contacts_iter, current_iter;
contacts_iter = entity->mContactNormals.begin();
while (contacts_iter != entity->mContactNormals.end()) {
// * Attention! *
// current_iter can be used throughout this environment and contacts_iter
// can already now be increased as it *must not* be used! This is due to
// the nature of ContactCachRemove() which might make contacts_iter
// invalid if we were using that.
current_iter = contacts_iter;
contacts_iter ++;
unsigned int contact_entity_id = contacts_iter->first;
#ifdef WIN32
EntityPhysicState* contact_entity = mEntities[contact_entity_id];
#else
EntityPhysicState* contact_entity = mEntities.at (contact_entity_id);
#endif
vector3d normal = contacts_iter->second;
vector3d old_velocity = entity->GetVelocity();
// If we already move away from the normal, we delete the contact.
if (normal * old_velocity > 0.01) {
LogDebug ("Lost Contact with entity %d!", current_iter->first);
ContactCacheRemove (entity->mId, current_iter->first);
continue;
} else {
vector3d new_velocity (old_velocity);
new_velocity -= normal;
entity->SetVelocity (new_velocity);
entity->UpdateShape();
contact_entity->UpdateShape();
coll2d::CollisionInfo info;
int coll2d_result;
coll2d_result = coll2d::check_collision (1.0, entity->mShape, contact_entity->mShape, &info);
if (!HandleColl2dError (coll2d_result, 1.0, entity, contact_entity, info)) {
// error
LogError ("Error when performing collision check: %s\n", __FUNCTION__);
assert (0);
}
if (coll2d_result == 0) {
// no contact, so delete it:
LogDebug ("Lost Contact with entity %d!", current_iter->first);
ContactCacheRemove (entity->mId, current_iter->first);
entity->SetVelocity (old_velocity);
continue;
} else if ( coll2d_result > 0){
// contact
if (info.time != 0. || normal != info.normal) {
LogError ("Something strange happened when checking for contacts in %s\n", __FUNCTION__);
assert (0);
}
}
entity->SetVelocity (old_velocity);
}
}
}
/**
* So far we ignore overlapping if one entity is an EntityBaseTypeActor and
* the other a EntityBaseTypeParticle.
*
* If this function returns true everything is ok and we can safely continue
* otherwise it is recommended to quit the application.
*
* \returns true if there was no error at all or we were able to deal with it
*/
bool PhysicsBase::HandleColl2dError (int coll2d_result, float stepsize,
EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info)
{
if (coll2d_result < 0) {
if (coll2d_result == CHECK_ERROR_OVERLAP) {
// this can happen if an Actor is faster than its thrown Particle,
// we ignore this for now
/// \todo Handle overlaps of Actors and Particles better or define clear guidelines. Note this also in Entity.h
if ( (entity_a->mBaseType == EntityBaseTypeParticle && entity_b->mBaseType == EntityBaseTypeActor)
|| (entity_b->mBaseType == EntityBaseTypeParticle && entity_a->mBaseType == EntityBaseTypeActor) )
LogDebug ("Ignoring CHECK_ERROR_OVERLAP");
return true;
} else {
LogError ("coll2d Error: %d (stepsize = %f, id_a = %d, id_b = %d)", coll2d_result, stepsize, entity_a->mId, entity_b->mId);
entity_a->mShape->doPrint ("Debug: entity_a");
entity_b->mShape->doPrint ("Debug: entity_b");
return false;
}
}
return true;
}
EntityPhysicState* CreateEntityPhysicState (EntityBaseType type, unsigned int id) {
EntityPhysicState* entity_physics = new EntityPhysicState ();
if (!entity_physics) {
LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type);
assert (0);
}
// default values for all Entities
entity_physics->mId = id;
entity_physics->mBaseType = type;
// specific values for each Entity type
if (type == EntityBaseTypeNone) {
entity_physics->mShape = new coll2d::Sphere (0.01);
assert (entity_physics->mShape);
} else if (type == EntityBaseTypeActor) {
entity_physics->mShape = new coll2d::Sphere (0.4);
assert (entity_physics->mShape);
} else if (type == EntityBaseTypeBlock) {
entity_physics->mShape = new coll2d::Polygon (4);
assert (entity_physics->mShape);
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (0, vector3d (-0.5, 0., 0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (1, vector3d (0.5, 0., 0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (2, vector3d (0.5, 0., -0.5));
static_cast<coll2d::Polygon*> (entity_physics->mShape)->setVertice (3, vector3d (-0.5, 0., -0.5));
} else if (type == EntityBaseTypeParticle) {
entity_physics->mShape = new coll2d::Sphere (0.05);
assert (entity_physics->mShape);
} else {
LogError ("No EntityPhysicState defined for Entity type '%d'", type);
assert (0);
}
return entity_physics;
}
}

91
engine/PhysicsBase.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef _PHYSICSBASE_H
#define _PHYSICSBASE_H
#include "Engine.h"
#include "EntityBase.h"
namespace coll2d {
struct CollisionInfo;
}
/// \todo get rid of this forward declaration somehow
struct vector3d;
namespace Engine {
class Module;
class Events;
class EntityPhysicState;
/** \brief Performs the physical simulation of all Entities
*
* This class defines how the physical simulation is performed. Especially how
* collisions are treated is defined in the function
* Physics::ResolveCollision() . This function can be modified to simulate
* dynamics effects such as friction, bouncing, stacking etc..
*
* \todo When running along multiple boxes that are perfectly aligned one hangs from time to time at the last box
*/
class PhysicsBase : public Module {
public:
/** \brief Performs the simulation for the next msec milliseconds
* \param msec The amount of time that is to be simulated
* \param model A pointer to the model that will be used to pass on collision events.*/
virtual int Simulate (float msec, ModelBase* model = NULL);
/** \brief Registers the physical state of an Entity */
virtual void RegisterEntity (EntityPhysicState* entity);
/** \brief Unregisters the physical state of an Entity */
virtual void UnregisterEntity (const unsigned int id);
protected:
/** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
virtual void OnDestroy ();
/** \brief Moves all Entities for delta_msec milliseconds (can be
* negative!) */
void Move (float delta_msec);
/** \brief Checks whether two given Entities can collide */
bool CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b);
/** \brief Calculates the next pair of Entities that will collide */
bool CalcNextCollision (float stepsize,
unsigned int &reference_entity_id,
unsigned int &incidence_entity_id,
coll2d::CollisionInfo &info);
/** \brief Resolves the collision that CalcNextCollision has found
* It resolves a found collision and prevents interpenetration of cached contacts.
*/
void ResolveCollision (float stepsize,
unsigned int reference_entity_id,
unsigned int incidence_entity_id,
coll2d::CollisionInfo &info);
/** \brief Contains all Entities with a physical state */
std::map<unsigned int, EntityPhysicState*> mEntities;
private:
/** \brief Caches the contact information between two entities
*/
void ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal);
/** \brief Removes the contact cache information of two entities in contact
*/
void ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id);
/** \brief Checks whether the entries in the contact cache are still valid
*/
void CheckContactCache (EntityPhysicState* entity);
/** \brief Allows to ignore certain kinds of errors reported by coll2d
*/
bool HandleColl2dError (int coll2d_result, float stepsize,
EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info);
};
/** \brief Creates an EntityPhysicState with all the required information */
EntityPhysicState* CreateEntityPhysicState (EntityBaseType type, unsigned int id);
}
#endif // _PHYSICSBASE_H

149
engine/PhysicsEntityBase.cc Normal file
View File

@ -0,0 +1,149 @@
#include "EntityBase.h"
#include "coll2d.h"
namespace Engine {
/** \brief Copy constructor */
inline EntityPhysicState::EntityPhysicState (const EntityPhysicState& state):
mId (state.mId),
mBaseType (state.mBaseType),
mPosition (state.mPosition),
mOrientation (state.mOrientation),
mVelocity (state.mVelocity),
mAngleVelocity (state.mAngleVelocity),
mStatic (state.mStatic),
mAlive (state.mAlive),
mContactNormals(state.mContactNormals)
{
mShape = state.mShape->getCopy();
};
/** \brief Assignment operator */
inline EntityPhysicState& EntityPhysicState::operator= (const EntityPhysicState& state) {
if (this != &state) {
mId = state.mId;
mBaseType = state.mBaseType;
mPosition = state.mPosition;
mOrientation = state.mOrientation;
mVelocity = state.mVelocity;
mAngleVelocity = state.mAngleVelocity;
mShape = state.mShape->getCopy();
mStatic = state.mStatic;
mAlive = state.mAlive;
mContactNormals = state.mContactNormals;
}
return *this;
}
vector3d& EntityPhysicState::GetPosition () {
return mPosition;
}
vector3d& EntityPhysicState::GetVelocity () {
return mVelocity;
}
vector3d& EntityPhysicState::GetOrientation () {
return mOrientation;
}
float& EntityPhysicState::GetAngleVelocity () {
return mAngleVelocity;
}
void EntityPhysicState::SetPosition (const vector3d &position) {
mPosition = position;
}
void EntityPhysicState::SetVelocity (const vector3d &velocity) {
mVelocity = velocity;
}
void EntityPhysicState::SetOrientation (const vector3d &orientation) {
mOrientation = orientation;
}
void EntityPhysicState::SetAngleVelocity (const float &angle_velocity) {
mAngleVelocity = angle_velocity;
}
void EntityPhysicState::Globalize (vector3d &vec) {
// make a copy of the local coordinates
vector3d local (vec);
// calculate sin and cos for the current rotation
float sintheta, costheta;
sintheta = sin (- mOrientation[1] * M_PI / 180);
costheta = cos (- mOrientation[1] * M_PI / 180);
// rotation
vec[0] = - sintheta * local[2] + costheta * local[0];
vec[1] = local[1];
vec[2] = costheta * local[2] + sintheta * local[0] ;
// and translation
vec += mPosition;
}
void EntityPhysicState::Localize (vector3d &vec) {
// translation
vec -= mPosition;
vector3d global (vec);
// calculate sin and cos for the current rotation
float sintheta, costheta;
sintheta = sin (mOrientation[1] * M_PI / 180);
costheta = cos (mOrientation[1] * M_PI / 180);
// rotation
vec[0] = - sintheta * global[2] + costheta * global[0];
vec[1] = global[1];
vec[2] = costheta * global[2] + sintheta * global[0] ;
}
void EntityPhysicState::GlobalizeRotation (vector3d &vec) {
// make a copy of the local coordinates
vector3d local (vec);
// calculate sin and cos for the current rotation
float sintheta, costheta;
sintheta = sin (- mOrientation[1] * M_PI / 180);
costheta = cos (- mOrientation[1] * M_PI / 180);
// rotation
vec[0] = - sintheta * local[2] + costheta * local[0];
vec[1] = local[1];
vec[2] = costheta * local[2] + sintheta * local[0] ;
}
void EntityPhysicState::LocalizeRotation (vector3d &vec) {
vector3d global (vec);
// calculate sin and cos for the current rotation
float sintheta, costheta;
sintheta = sin (mOrientation[1] * M_PI / 180);
costheta = cos (mOrientation[1] * M_PI / 180);
// rotation
vec[0] = - sintheta * global[2] + costheta * global[0];
vec[1] = global[1];
vec[2] = costheta * global[2] + sintheta * global[0] ;
}
void EntityPhysicState::UpdateShape () {
assert (mShape);
mShape->setPosition (mPosition);
mShape->setAngle (mOrientation[1] * M_PI / 180);
mShape->setVelocity (mVelocity);
mShape->setAngleVelocity (mAngleVelocity);
}
}

View File

@ -0,0 +1,220 @@
#include "DrawingsGL.h"
#include "OverlayBase.h"
#include "SimpleConsoleOverlay.h"
#include "OGLFT.h"
#include <GL/gl.h>
#include <GL/glu.h>
namespace Engine {
static Variable Var_ConsoleTransparency ("consoletransparency", "0.2");
bool SimpleConsoleOverlay::OnKeyDown (const SDL_keysym &keysym) {
if (keysym.sym == SDLK_F8) {
if (mActive) {
// We have to call SetActive() to actually
// activate the unicode processing of SDL
SetActive (false);
}
else {
SetActive (true);
}
return true;
}
if (!mActive)
return false;
// check for input that requires actions
switch (keysym.sym) {
case SDLK_ESCAPE:
SetActive (false);
return true;
break;
case SDLK_BACKSPACE:
if (mCurrentInput.size() > 0)
mCurrentInput = mCurrentInput.substr (0, mCurrentInput.size() - 1 );
return true;
break;
case SDLK_RETURN:
if (mCurrentInput.size() == 0) {
mLastLines.push_back ("");
return true;
}
mLastLines.push_back (mCurrentInput);
// run the command and print out the error if there was one
if (!RunCommand (mCurrentInput)) {
mLastLines.push_back ("Error: " + CommandGetErrorString());
}
mCurrentInput = "";
return true;
break;
default:
break;
}
// if we got input of a character that we can write add it to the current
// input
if (keysym.unicode) {
if ((keysym.unicode & 0xFF80) == 0) {
mCurrentInput += keysym.unicode & 0x7F;
return true;
} else {
LogWarning ("Input key not supported!");
return false;
}
}
return true;
}
void SimpleConsoleOverlay::Draw () {
// we switch to orthographic projection and draw the contents of the 2d
// overlay on top of the previous drawings
glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();
// first we have to get the size of the current viewport to set up the
// orthographic projection correctly
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
gluOrtho2D (viewport[0], viewport[2], viewport[3], viewport[1]);
glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
// then we do the drawings
if (mDrawLogBar)
DrawLogBar ();
if (mActive)
DrawConsole ();
glPopMatrix ();
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
};
void SimpleConsoleOverlay::DrawLogBar () {
// first we have to get the size of the current viewport to set up the
// orthographic projection correctly
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
// we want to enable transparency
glDisable(GL_DEPTH_TEST);
glEnable (GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
float left = 0;
float right = static_cast<float> (GetWindowWidth());
float top = 0;
// float bottom = static_cast<float> (GetWindowHeight());
// draw the background
glColor4f (0.2, 0.2, 0.2, 0.3);
glBegin (GL_QUADS);
glVertex2f (left, top);
glVertex2f (left, top + 16);
glVertex2f (right, top + 16);
glVertex2f (right, top);
glEnd ();
glDisable (GL_BLEND);
// draw the log
std::ostringstream topbar_stream;
topbar_stream << "Log: " << GetLastLogEntry().mMessage;
DrawGLString ( 10, 10, topbar_stream.str().c_str ());
// draw the FPS counter
topbar_stream.str ("");
topbar_stream << "FPS: " << GetFrameRate();
DrawGLString (right - 64 , 10, topbar_stream.str().c_str ());
glEnable (GL_DEPTH_TEST);
}
void SimpleConsoleOverlay::DrawConsole () {
// first we have to get the size of the current viewport to set up the
// orthographic projection correctly
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
// we want to enable transparency
glDisable(GL_DEPTH_TEST);
// glEnable (GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
// calculate the screen coordinates which defines the size of the
// console
mLeft = (viewport[2] - viewport[0]) * 0.5 - (viewport[2] - viewport[0]) * 0.45 ;
mRight = (viewport[2] - viewport[0]) * 0.5 + (viewport[2] - viewport[0]) * 0.45;
mTop = -1; // Do not draw the mTop line
mBottom = (viewport[3] - viewport[1]) * 0.7;
// draw the background
glColor4f (0.2, 0.2, 0.2, Var_ConsoleTransparency.GetFloatValue());
glBegin (GL_QUADS);
glVertex2f (mLeft, mTop);
glVertex2f (mLeft, mBottom);
glVertex2f (mRight, mBottom);
glVertex2f (mRight, mTop);
glEnd ();
// draw borders
glColor3f (0.9, 0.9, 0.9);
glBegin (GL_LINE_STRIP);
glVertex2f (mLeft, mTop);
glVertex2f (mLeft, mBottom);
glVertex2f (mRight, mBottom);
glVertex2f (mRight, mTop);
glVertex2f (mLeft, mTop);
glEnd ();
glDisable (GL_BLEND);
// now draw the contents
DrawLastLines ();
DrawCurrentInput ();
glEnable (GL_DEPTH_TEST);
}
const std::vector<std::string> SimpleConsoleOverlay::GetLastLines (const unsigned int n) {
assert (0);
std::vector<std::string> result;
return result;
}
void SimpleConsoleOverlay::DrawLastLines () {
unsigned int i;
for (i = 0; i < mLastLines.size(); i++)
DrawGLString (mLeft + 8, mTop + 12 + 12 *i, mLastLines[i].c_str());
}
void SimpleConsoleOverlay::DrawCurrentInput () {
// We add a '_' to the current input as a simple cursor
static std::string current_input;
current_input = mCurrentInput + "_";
// We add a '>' at the beginning of the input line to highlight it
DrawGLString (mLeft, mTop + 12 + 12 * (mLastLines.size()), ">");
DrawGLString (mLeft + 8, mTop + 12 + 12 * (mLastLines.size()), current_input.c_str());
}
}

View File

@ -0,0 +1,69 @@
#ifndef SIMPLECONSOLEOVERLAY
#define SIMPLECONSOLEOVERLAY
#include "Variables.h"
#include <SDL/SDL.h>
#include <string>
namespace Engine {
class OverlayBase;
class SimpleConsoleOverlay : public OverlayBase {
public:
SimpleConsoleOverlay () {
mActive = false;
mDrawLogBar = false;
};
virtual ~SimpleConsoleOverlay() {};
virtual bool OnKeyDown (const SDL_keysym &keysym);
virtual bool OnKeyUp (const SDL_keysym &keysym) {
if(mActive)
return true;
return false;
};
virtual void Draw ();
void DrawLogBar ();
void DrawConsole ();
/** \brief Returns the last n lines */
const std::vector<std::string> GetLastLines (const unsigned int n);
/** \brief Returns true if the Console is active */
bool GetActive () { return mActive; };
/** \brief Activates or deactivates the the Console */
void SetActive (bool active) {
if (active) {
SDL_EnableUNICODE (1);
SDL_EnableKeyRepeat (500, 50);
}
else {
SDL_EnableUNICODE (-1);
SDL_EnableKeyRepeat (0, 100);
}
mActive = active;
};
void SetDrawLogBar (bool value) { mDrawLogBar = value; };
/** \brief Draws the console with the current content */
void DrawLastLines ();
void DrawCurrentInput ();
private:
std::vector<std::string> mLastLines;
std::string mCurrentInput;
bool mActive;
bool mDrawLogBar;
float mLeft;
float mTop;
float mRight;
float mBottom;
};
}
#endif /* SIMPLECONSOLEOVERLAY */

237
engine/Sprite.cc Normal file
View File

@ -0,0 +1,237 @@
#include "Engine.h"
#include "Sprite.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <png.h>
#include <cmath>
namespace Engine {
/*
* Code is taken from http://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures on
* Sunday, March 14 2010.
*/
bool Sprite::LoadFromPNG (const char *filename) {
LogDebug ("Loading png from %s", filename);
//header for testing if it is a png
png_byte header[8];
//open file as binary
FILE *fp = fopen(filename, "rb");
if (!fp) {
LogError ("Could not open file: %s", filename);
return false;
}
//read the header
fread(header, 1, 8, fp);
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png) {
LogError ("Error opening png file %s: file is not a png file!", filename);
fclose(fp);
return false;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr) {
LogError ("Error opening png file %s: unable to read png header", filename);
fclose(fp);
return (false);
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
LogError ("Error opening png file %s: unable to read png header", filename);
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
fclose(fp);
return (false);
}
//create png info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
LogError ("Error opening png file %s: unable to read png header", filename);
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
fclose(fp);
return (false);
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr))) {
LogError ("Error opening png file %s: unable to read png header", filename);
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return (false);
}
//init png reading
png_init_io(png_ptr, fp);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 tmWidth, tmHeight;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &tmWidth, &tmHeight, &bit_depth, &color_type,
NULL, NULL, NULL);
//update mWidth and mHeight based on png info
mWidth = tmWidth;
mHeight = tmHeight;
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// Allocate the image_data as a big block, to be given to opengl
png_byte *image_data = new png_byte[rowbytes * mHeight];
if (!image_data) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
fclose(fp);
return false;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = new png_bytep[mHeight];
if (!row_pointers) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
fclose(fp);
return false;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (unsigned int i = 0; i < mHeight; ++i)
row_pointers[mHeight - 1 - i] = image_data + i * rowbytes;
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
//Now generate the OpenGL texture object
glGenTextures(1, &mGlTextureName);
glBindTexture(GL_TEXTURE_2D, mGlTextureName);
glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, mWidth, mHeight, 0,
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] row_pointers;
delete[] image_data;
fclose(fp);
return true;
}
void Sprite::DrawAt (float xpos, float ypos, float zpos) {
float u_start = 0., u_end = 1.;
if (mAnimation) {
int frame_index = floor ( (mAnimationFrameRate) * mAnimationTimer);
u_start = static_cast<float>(frame_index) / mAnimationFrameCount;
u_end = (frame_index + 1.) / mAnimationFrameCount;
}
glPushMatrix();
glTranslatef (xpos -mScale * mWidth * 0.5, ypos, zpos -mScale * mHeight * 0.5);
glScalef (mScale, mScale, mScale);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture (GL_TEXTURE_2D, mGlTextureName);
glBegin(GL_QUADS);
glTexCoord2f (u_start, 0.); glVertex3f (0., 0., 0.);
glTexCoord2f (u_end, 0.); glVertex3f (0., 0., mHeight);
glTexCoord2f (u_end, 1.); glVertex3f (mWidth, 0., mHeight);
glTexCoord2f (u_start, 1.); glVertex3f (mWidth, 0.,0.);
glEnd();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
void Sprite::DrawAt2D (float xpos, float ypos) {
float u_start = 0., u_end = 1.;
if (mAnimation) {
int frame_index = floor ( (mAnimationFrameRate) * mAnimationTimer);
u_start = static_cast<float>(frame_index) / mAnimationFrameCount;
u_end = (frame_index + 1.) / mAnimationFrameCount;
}
glPushMatrix();
glTranslatef (xpos -mScale * mWidth * 0.5, ypos, 0.);
glScalef (mScale, mScale, mScale);
// \TODO Make 2d drawing of sprites a bit nicer, seems a bit overkill for such a simple function
glRotatef (-90, 0., 0., 1.);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture (GL_TEXTURE_2D, mGlTextureName);
glBegin(GL_QUADS);
glTexCoord2f (u_start, 0.); glVertex2f (0., 0.);
glTexCoord2f (u_end, 0.); glVertex2f (0., mHeight);
glTexCoord2f (u_end, 1.); glVertex2f (mWidth, mHeight);
glTexCoord2f (u_start, 1.); glVertex2f (mWidth,0.);
glEnd();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
void Sprite::DrawSubAt (unsigned int index, float xpos, float ypos, float zpos) {
assert (index < mSubSpriteCount);
float u_start = static_cast<float>(index) / mSubSpriteCount;
float u_end = (index + 1.) / mSubSpriteCount;
glPushMatrix();
glTranslatef (xpos -mScale * mWidth * 0.5, ypos, zpos -mScale * mHeight * 0.5);
glScalef (mScale, mScale, mScale);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture (GL_TEXTURE_2D, mGlTextureName);
glBegin(GL_QUADS);
glTexCoord2f (u_start, 0.); glVertex3f (0., 0., 0.);
glTexCoord2f (u_end, 0.); glVertex3f (0., 0., mHeight);
glTexCoord2f (u_end, 1.); glVertex3f (mWidth, 0., mHeight);
glTexCoord2f (u_start, 1.); glVertex3f (mWidth, 0.,0.);
glEnd();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
}
}

71
engine/Sprite.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef SPRITE_H
#define SPRITE_H
#include <cmath>
namespace Engine {
class Sprite {
public:
Sprite() {
mScale = 1.;
mWidth = 0;
mHeight = 0;
mGlTextureName = 0;
mAnimation = false;
mSubSpriteCount = 1;
}
bool LoadFromPNG (const char *filename);
void DrawAt (float xpos, float ypos, float zpos);
void DrawAt2D (float xpos, float ypos);
unsigned int GetWidth() { return mWidth; };
unsigned int GetHeight() { return mHeight; };
void SetScale (float scale) { mScale = scale; };
void SetAnimation (int frame_count, float frame_rate) {
mAnimation = true;
mAnimationFrameCount = frame_count;
mAnimationFrameRate = frame_rate;
mAnimationTimer = 0.;
mWidth = static_cast<unsigned int>(ceil (static_cast<float> (mWidth / mAnimationFrameCount)));
}
void ResetAnimation () {
mAnimationTimer = 0.;
}
void UpdateAnimation (float seconds) {
mAnimationTimer += seconds;
while (mAnimationTimer >= mAnimationFrameCount / mAnimationFrameRate) {
mAnimationTimer -= mAnimationFrameCount / mAnimationFrameRate;
}
}
void SetSubSpriteCount (const unsigned int count) {
mSubSpriteCount = count;
mWidth = static_cast<unsigned int>(ceil (static_cast<float> (mWidth / mSubSpriteCount)));
}
unsigned int GetSubSpriteCount () const {
return mSubSpriteCount;
}
void DrawSubAt (unsigned int index, float xpos, float ypos, float zpos);
private:
float mScale;
unsigned int mWidth;
unsigned int mHeight;
unsigned int mGlTextureName;
unsigned int mSubSpriteCount;
bool mAnimation;
int mAnimationFrameCount;
float mAnimationTimer;
float mAnimationFrameRate;
};
}
#endif /* SPRITE_H */

159
engine/Variables.cc Normal file
View File

@ -0,0 +1,159 @@
#include "Variables.h"
#include <cstdlib>
#include <iostream>
namespace Engine {
Variables* VariablesInstance = NULL;
/*
* Inherited Module functions
*/
int Variables::OnInit (int argc, char* argv[]) {
LogDebug ("Variables Init");
VariablesInstance = this;
/* This definition of a variable causes the delayed variables to be loaded,
* please keep it here! */
static Variable VariableSystemUp_var ("variablesystemup", "true");
return 0;
}
void Variables::OnDestroy () {
LogDebug ("Variables Destroy");
VariablesInstance = NULL;
}
/*
* Module specific functions
*/
void Variables::RegisterVariable (const std::string &name, Variable *var) {
LogDebug ("Registering Variable '%s'", name.c_str ());
if (mVariablesData.find (name) != mVariablesData.end()) {
// Variable already existing!
mVariablesData[name] = var;
return;
}
mVariablesData[name] = var;
return;
}
/*
* Variable class
*/
Variable* Variables::GetVariable (const std::string &name) {
if (mVariablesData.find (name) == mVariablesData.end()) {
// Variable not existing!
return NULL;
}
return mVariablesData[name];
}
Variable::Variable (const std::string &name, const std::string &value) {
static std::vector<Variable*> delayed_variables;
mName = name;
mStringValue = value;
mFloatValue = atof (value.c_str());
if (VariablesInstance == NULL) {
delayed_variables.push_back (this);
} else if (delayed_variables.size() > 0) {
LogDebug ("Loading delayed Variables");
unsigned int i;
for (i = 0; i < delayed_variables.size(); i ++)
VariablesInstance->RegisterVariable (delayed_variables[i]->mName, delayed_variables[i]);
delayed_variables.clear ();
RegisterVariable (name);
} else {
RegisterVariable (name);
}
}
void Variable::RegisterVariable (const std::string &name) {
if (! VariablesInstance ) {
LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str());
return;
}
VariablesInstance->RegisterVariable (name, this);
}
/*
* Global functions
*/
Variable* GetVariable (const std::string &name) {
if (! VariablesInstance ) {
LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str());
return NULL;
}
return VariablesInstance->GetVariable (name);
}
bool SetVariableValue (const std::string &name, const std::string value) {
if (! VariablesInstance ) {
LogError ("Unable to set Variable '%s': Variables System not initialized!", name.c_str());
return false;
}
Variable *var = VariablesInstance->GetVariable (name);
if (!var) {
return false;
}
var->SetStringValue (value);
var->SetFloatValue (atof (value.c_str()));
return true;
}
std::string& GetVariableString (const std::string &name, std::string def) {
/* We use a static result variable for the case that def was not passed to
* is function */
static std::string def_result = def;
if (! VariablesInstance ) {
LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str());
return def_result;
}
Variable *var = VariablesInstance->GetVariable (name);
if (!var) {
return def_result;
}
return var->GetStringValue ();
}
float &GetVariableFloat (const std::string &name, float def) {
/* We use a static result variable for the case that def was not passed to
* is function */
static float def_result = def;
if (! VariablesInstance ) {
LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str());
return def_result;
}
Variable *var = VariablesInstance->GetVariable (name);
if (!var) {
return def_result;
}
return var->GetFloatValue ();
}
}

37
engine/Variables.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef _VARIABLES_H
#define _VARIABLES_H
#include "Engine.h"
#include <assert.h>
#include <string>
#include <map>
namespace Engine {
class Module;
class Variable;
/** \brief Manages all variables that can be changed by the Model itself
*
* \todo make the variable names case insensitive
* \todo only allow certain characters in variable names
*/
class Variables : public Module{
public:
void RegisterVariable (const std::string &name, Variable *var);
Variable *GetVariable (const std::string &name);
protected:
/** \brief Initializes the system */
int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
void OnDestroy ();
void OnRegisterCommands ();
std::map<std::string, Variable*> mVariablesData;
};
}
#endif // _VARIABLES_H

View File

@ -0,0 +1,27 @@
#include "Variables.h"
namespace Engine {
bool Cmd_Set (const std::vector<std::string> args) {
if (args.size() != 2) {
CommandSetErrorString ("Usage: set <name> <value>\nSets variables <name> to value <value>.");
return false;
}
Variable *test = GetVariable (args[0]);
if (test) {
test->SetStringValue (args[1]);
test->SetFloatValue (atof (args[1].c_str()));
return true;
}
CommandSetErrorString ("Variable '" + args[0] +"' not found!");
return false;
}
void Variables::OnRegisterCommands () {
AddCommand ("set", Cmd_Set);
}
}

68
engine/VariablesGlobal.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef _VARIABLESGLOBAL_H
#define _VARIABLESGLOBAL_H
namespace Engine {
/** \brief Represents a variable that can be modified and read within the game
*
* \note Variables \b MUST be declared as static variables, otherwise the memory that
* hold their values get invalidated and the system gets unstable!
*/
class Variable {
public:
/** \brief The constructor to be used when initializing a Variable
*
* \param name The name under which the variable is engine wide
* accessible
* \param value The value it is assigned to.
*
* The value string gets automatically converted to a float with atof ().
* Modification of a Variable must always be made with the Get*, Set*
* functions.
*/
Variable (const std::string &name, const std::string &value);
/** \brief Returns the string value of the Variable */
std::string& GetStringValue () {
return mStringValue;
}
/** \brief Returns the float value of the Variable */
float& GetFloatValue () {
return mFloatValue;
}
void SetStringValue (const std::string &value) {
mStringValue = value;
}
void SetFloatValue (float value) {
mFloatValue = value;
}
private:
/** \brief The default constructor must not be used.
*
* Use \code
* Variable (const std::string &name, const std::string &value)
* \endcode
* instead.
* */
Variable () { assert (0); }
/** \brief Registeres this Variable with the Variables System */
void RegisterVariable (const std::string &name);
std::string mName;
std::string mStringValue;
float mFloatValue;
friend class Variables;
};
/** \brief Provides access to a Variable stored under the given name */
Variable* GetVariable (const std::string &name);
/** \brief Sets the vaule of the Variable */
bool SetVariableValue (const std::string &name, const std::string &value);
/** \brief Returns the string value of the Variable with the given name */
std::string& GetVariableString (const std::string &name, std::string def = "");
/** \brief Returns the float value of the Variable with the given name */
float& GetVariableFloat (const std::string &name, float def = 0.);
}
#endif // _VARIABLESGLOBAL_H

330
engine/ViewBase.cc Normal file
View File

@ -0,0 +1,330 @@
#include "ViewBase.h"
#include "ModelBase.h"
#include "ControllerBase.h"
#include "CameraBase.h"
#include "OverlayBase.h"
#include "SimpleConsoleOverlay.h"
#include "OGLFT.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include "DrawingsGL.h"
using namespace std;
namespace Engine {
static ViewBase* ViewInstance = NULL;
void InitGL () {
glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
glClearDepth(1.0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
glEnable (GL_CULL_FACE);
glDisable (GL_FOG);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
}
/*
* Inherited Module functions
*/
int ViewBase::OnInit (int argc, char* argv[]) {
LogMessage ("View Init");
mWindowHeight = VIEW_DEFAULT_HEIGHT;
mWindowWidth = VIEW_DEFAULT_WIDTH;
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL | SDL_RESIZABLE ) == 0 ) {
LogError ("Video mode set failed: %s", SDL_GetError ());
exit (-1);
}
InitGL ();
Resize (mWindowWidth, mWindowHeight);
mConsoleFont = new OGLFT::Monochrome ("./data/fonts/console.ttf", 12);
if ( mConsoleFont == 0 || !mConsoleFont->isValid() ) {
LogError ("Could not load font %s!", "./data/fonts/console.ttf");
exit (-1);
}
SimpleConsoleOverlay* console_overlay = new SimpleConsoleOverlay;
AddOverlay (console_overlay);
mConsoleFont->setForegroundColor (1., 1., 1.);
mDrawAxis = false;
mDrawGrid = false;
mGridSizeX = 8;
mGridSizeZ = 8;
ViewInstance = this;
return 0;
}
void ViewBase::OnDestroy () {
if (mConsoleFont )
delete mConsoleFont;
mConsoleFont = NULL;
std::vector<OverlayBase*>::iterator overlay_iter = mOverlays.begin(), overlay_temp;
while (overlay_iter != mOverlays.end()) {
overlay_temp = overlay_iter;
delete *overlay_temp;
overlay_iter++;
}
ViewInstance = NULL;
LogDebug ("View Destroy");
}
void ViewBase::CalcWorldCoordinates (int screen_x, int screen_y, float world_y, float *pos_out) {
GLdouble modelMatrix[16], projMatrix[16];
GLint viewport[4];
GLdouble wx, wy, wz;
glGetIntegerv (GL_VIEWPORT, viewport);
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projMatrix);
int realy = viewport[3] - screen_y - 1;
gluUnProject ((GLdouble) screen_x, (GLdouble) realy, 1.,
modelMatrix, projMatrix, viewport, &wx, &wy, &wz);
GLdouble t;
GLdouble d[3];
float eye[3];
mCamera->GetEye (&eye[0]);
d[0] = wx - eye[0];
d[1] = wy - eye[1];
d[2] = wz - eye[2];
assert (fabs (d[1]) >= 1.0e-3);
t = -eye[1]/d[1] + world_y;
pos_out[0] = eye[0] + t * d[0];
pos_out[1] = eye[1] + t * d[1];
pos_out[2] = eye[2] + t * d[2];
}
/*
* Module specific functions
*/
void ViewBase::UpdateCamera () {
EntityPhysicState* player_ent = GetEntityPhysicState (GetPlayerEntityId());
if (!player_ent) {
LogError ("Could not call Model::PositionCamera(): player entity not found!");
exit (-1);
}
vector3d entity_camera_distance (-2, 3, 0);
vector3d entity_position = player_ent->GetPosition();
player_ent->Globalize (entity_camera_distance);
mCamera->SetEye (
entity_camera_distance[0],
entity_camera_distance[1],
entity_camera_distance[2]
);
mCamera->SetPointOfIntrest (
entity_position[0],
entity_position[1],
entity_position[2]
);
mCamera->Update ();
}
void ViewBase::Draw () {
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// update the frame rate counter
static Uint32 this_frame_ticks;
static Uint32 last_frame_ticks = 0;
static Uint32 last_fps_update = 0;
static int frame_counter = 0;
this_frame_ticks = SDL_GetTicks ();
last_fps_update += this_frame_ticks - last_frame_ticks;
last_frame_ticks = this_frame_ticks;
frame_counter++;
if (last_fps_update > 1000) {
mFrameRate = frame_counter;
last_fps_update = 0;
frame_counter = 0;
}
UpdateCamera ();
if (mDrawGrid)
DrawGrid ();
if (mDrawAxis)
DrawAxis ();
DrawWorld ();
std::vector<OverlayBase*>::iterator overlay_iter;
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
(*overlay_iter)->Draw();
}
// and update the screen
SDL_GL_SwapBuffers ();
}
void ViewBase::DrawGLString (float x, float y, const char* str) {
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
mConsoleFont->draw (x, y, str);
}
void ViewBase::GetCamereEye (float *eye_out) {
assert (mCamera);
mCamera->GetEye (eye_out);
}
void ViewBase::DrawGrid () {
float xmin, xmax, xstep, zmin, zmax, zstep;
int i, count_x, count_z;
xmin = -mGridSizeX;
xmax = mGridSizeX;
zmin = -mGridSizeZ;
zmax = mGridSizeZ;
count_x = mGridSizeX * 2;
count_z = mGridSizeZ * 2;
xstep = 1.;
zstep = 1.;
glColor3f (1., 1., 1.);
glBegin (GL_LINES);
for (i = 0; i <= count_x; i++) {
glVertex3f (i * xstep + xmin, 0., zmin);
glVertex3f (i * xstep + xmin, 0., zmax);
}
for (i = 0; i <= count_z; i++) {
glVertex3f (xmin, 0, i * zstep + zmin);
glVertex3f (xmax, 0, i * zstep + zmin);
}
glEnd ();
}
void ViewBase::DrawWorld () {
}
void ViewBase::Resize (int width, int height) {
if (height == 0)
height = 1;
mWindowWidth = static_cast<unsigned int> (width);
mWindowHeight = static_cast<unsigned int> (height);
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(mCamera->GetFOVY (), float (width) / float (height), 0.1, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
LogDebug ("Resize to: %d x %d", mWindowWidth,mWindowHeight);
/** \warning
* This call has to be made for SDL 1.2 for 1.3 there seems to be a
* 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 ) {
LogError ("Video mode set failed: %s", SDL_GetError ());
exit (-1);
}
}
bool ViewBase::SendKeyDown (const SDL_keysym &keysym) {
std::vector<OverlayBase*>::iterator overlay_iter;
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
if ( (*overlay_iter)->OnKeyDown (keysym))
return true;
}
return false;
}
bool ViewBase::SendKeyUp (const SDL_keysym &keysym) {
std::vector<OverlayBase*>::iterator overlay_iter;
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
if ( (*overlay_iter)->OnKeyUp (keysym))
return true;
}
return false;
}
bool ViewBase::SendMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) {
std::vector<OverlayBase*>::iterator overlay_iter;
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
if ( (*overlay_iter)->OnMouseButtonUp (button, xpos, ypos))
return true;
}
return false;
}
bool ViewBase::SendMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) {
std::vector<OverlayBase*>::iterator overlay_iter;
for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) {
if ( (*overlay_iter)->OnMouseButtonDown (button, xpos, ypos))
return true;
}
return false;
}
/*
* Global functions
*/
void DrawGLString (float x, float y, const char* str) {
if (!ViewInstance) {
LogError ("Cannot Draw GL String: View not yet initialized!");
return;
}
ViewInstance->DrawGLString (x, y, str);
}
unsigned int GetWindowWidth() {
return ViewInstance->GetWindowWidth ();
}
unsigned int GetWindowHeight() {
return ViewInstance->GetWindowHeight ();
}
int GetFrameRate () {
return ViewInstance->GetFrameRate ();
}
}

98
engine/ViewBase.h Normal file
View File

@ -0,0 +1,98 @@
#ifndef _VIEWBASE_H
#define _VIEWBASE_H
#include "Engine.h"
// forward declarations for the OGLFT fonts
namespace OGLFT {
class Monochrome;
}
namespace Engine {
class Module;
class ModelBase;
class CameraBase;
class OverlayBase;
/** \brief Performs the actual drawing based on Camera and Model
*/
class ViewBase : public Module{
public:
/** \brief Resizes the View */
void Resize (int width, int height);
/** \brief Performs all drawing */
virtual void Draw ();
/** \brief Draws a string at the given position using current projection
* and modelview matrices */
void DrawGLString (float x, float y, const char* str);
/** \brief Stores the eye poisition in eye_out */
void GetCamereEye (float *eye_out);
/** \brief Calculates the world coordinates to given screen coordinates */
void CalcWorldCoordinates (int screen_x, int screen_y, float world_y, float *pos_out);
unsigned int GetWindowWidth () { return mWindowWidth; };
unsigned int GetWindowHeight () { return mWindowHeight; };
int GetFrameRate() { return mFrameRate; };
void SetDrawAxis (bool draw_axis) { mDrawGrid = draw_axis; };
bool GetDrawAxis () { return mDrawGrid; };
void SetDrawGrid (bool draw_grid) { mDrawGrid = draw_grid; };
bool GetDrawGrid () { return mDrawGrid; };
void SetGridSize (int x, int z) { mGridSizeX = x; mGridSizeZ = z; }
void AddOverlay (OverlayBase *overlay) { mOverlays.push_back (overlay); };
/* Input forwarding for the overlays */
bool SendKeyDown (const SDL_keysym &keysym);
bool SendKeyUp (const SDL_keysym &keysym);
bool SendMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos);
bool SendMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos);
protected:
/** \brief Initializes the system */
int OnInit (int argc, char* argv[]);
/** \brief Destroys the system (must be called!) */
void OnDestroy ();
/** \brief Updates the camera for further drawing */
virtual void UpdateCamera ();
/** \brief Draws a grid of 16 x 16 tiles */
void DrawGrid ();
/** \brief Draws the level and all the visible Entities */
virtual void DrawWorld ();
/** \brief Draws orthographic overlay*/
void DrawOverlay2D ();
ModelBase *mModel;
CameraBase *mCamera;
std::vector<OverlayBase*> mOverlays;
/** \brief The height of the canvas we're drawing on */
unsigned int mWindowHeight;
/** \brief The width of the canvas we're drawing on */
unsigned int mWindowWidth;
/** \brief Stores the current frame rate */
int mFrameRate;
bool mDrawAxis;
bool mDrawGrid;
int mGridSizeX;
int mGridSizeZ;
/** \brief Font that is used in the console */
OGLFT::Monochrome* mConsoleFont;
friend class Engine;
};
}
#include "ViewBaseGlobal.h"
#endif // _VIEWBase_H

17
engine/ViewBaseGlobal.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef _VIEWGLOBAL_H
#define _VIEWGLOBAL_H
namespace Engine {
/** \brief Draws the given string at the given position using the current
* OpenGL transformations */
void DrawGLString (float x, float y, const char* str);
unsigned int GetWindowWidth();
unsigned int GetWindowHeight();
int GetFrameRate ();
}
#endif /* _VIEWGLOBAL_H */

View File

@ -0,0 +1,72 @@
#include "EntityBase.h"
#include <GL/gl.h>
namespace Engine {
void EntityVisualState::Draw () {
if (mBaseType == EntityBaseTypeActor) {
int i, segments;
segments = 20;
double x, z, rad, drad;
drad = (M_PI * 2) / segments;
glBegin (GL_TRIANGLE_FAN);
glVertex3f (0., 0., 0.);
for (i = 0; i <= segments; i++) {
rad = drad * i;
sincos (rad, &z, &x);
glVertex3f (x * mRadius, 0., -z * mRadius);
}
glEnd ();
glDisable (GL_DEPTH_TEST);
glColor3f (0.8, 0., 0.2);
glBegin (GL_TRIANGLES);
glVertex3f (mRadius, 0., 0.);
glVertex3f (0., 0., -mRadius * 0.3);
glVertex3f (0., 0., mRadius * 0.3);
glEnd ();
glEnable (GL_DEPTH_TEST);
return;
} else if (mBaseType == EntityBaseTypeBlock) {
glBegin (GL_QUADS);
glVertex3f (-0.5, 0., 0.5);
glVertex3f (0.5, 0., 0.5);
glVertex3f (0.5, 0., -0.5);
glVertex3f (-0.5, 0., -0.5);
glEnd ();
} else if (mBaseType == EntityBaseTypeParticle) {
int i, segments;
segments = 20;
double x, z, rad, drad;
drad = (M_PI * 2) / segments;
glDisable (GL_DEPTH_TEST);
glColor3f (0., 0.8, 0.1);
glBegin (GL_TRIANGLE_FAN);
glVertex3f (0., 0., 0.);
for (i = 0; i <= segments; i++) {
rad = drad * i;
sincos (rad, &z, &x);
glVertex3f (x * mRadius, 0., -z * mRadius);
}
glEnd ();
glColor3f (0.8, 0., 0.2);
glBegin (GL_TRIANGLES);
glVertex3f (mRadius, 0., 0.);
glVertex3f (0., 0., -mRadius * 0.3);
glVertex3f (0., 0., mRadius * 0.3);
glEnd ();
glEnable (GL_DEPTH_TEST);
return;
}
}
}

96
engine/doc/Mainpage.h Normal file
View File

@ -0,0 +1,96 @@
/** \file documentation.h \mainpage Main Page
*
* This is the documentation of Engine -- a game engine without a name.
*
* \section General Information
*
* To get an overview over the most important functions and classes have a
* look at Engine. For the structure of the Engine look at Engine::Engine. In
* \ref usecases you find documentation on how things are supposed to work and
* what the ideas behind certain designs is.
*
* \section Physics
*
* So far we only use a very simple physics system which is mostly defined by
* the Engine::Physics class. It uses the coll2d collision library. Collision
* response so far only tries to prevent interpenetration.
*
* \section Networking
*
* For easier networking it is assumed that everything runs over a network.
* The game itself is rather a smart client to a simple synchronization
* protocol. What is being synchronized is the game state and the client
* simply displays the current state it knows. The client is "smart" as it
* tries to guess what the next state will be.
*
* For networking we want to use Enet http://enet.bespin.org/.
*
* Notes for networking: At
* http://www.gamedev.net/community/forums/topic.asp?topic_id=550962 is an
* interesting thread about modeling the updates. Especially the answer of
* Antheus at http://www.gamedev.net/community/forums/viewreply.asp?ID=3546077
* describes two fundamental models:
*
* Level Triggering (GoF Observer Pattern): Entity A sends its changes to the
* Observer pattern and all objects that are interested in the state of Entity
* A get notfied by the observer.
*
* Edge Triggering: For each Entity A the Observer B is interested, it stores
* a flag for a value it is interested in (e.g. one for position, one for
* velocity, etc.). When Entity A modifies one of the values it notifies the
* observer that its current state has changed and the Observer sets the flag
* to "dirty". Anyone interested in events polls at the Observer and decides
* what to do with it. When the value is queried, the Observer reads the value
* from the Entity and clears the flag.
*
* For networking a combination can be used: During the simulation loop only
* Edge Triggering is performed and at the end of it, all dirty States get
* sent over the network.
*
* When notifying events such as "Add new Entity X" the user tomv describes at
* http://www.gamedev.net/community/forums/viewreply.asp?ID=3545586 a method
* to circumvent these messages. Instead, the server simply sends out updates
* about the Entities around a player where close Entities are updated more
* frequently than ones that are further away. It simply sends "Entity X
* changed by D". If a client does not know what the Entity X is, it queries
* the type and current state of the Entity X and therefore reconstructs the
* surroundings step-by-step. It is also robust against dropped messages of
* the form "Add new Entity X".
*
* At tomvas model there are some problems: How are events "Delete Entity X"
* handled? How to handle malicious clients that request the state of all
* Entities? (Solution for the last question: Request Queues).
*
* Kylotan cals tomvas model as a unreliable system for generic state
* [synchronization?] (http://www.gamedev.net/community/forums/viewreply.asp?ID=3548662).
* Additionally he mentions that it can increase occurences of short term
* unsynchronous states and reccomends using some reliable message passing.
*
* \section ToDos
*
* This is a loose list of items that are to be implemented. For a list of all
* todos within the code have a look at the \ref todo.
*
* \todo [high] Create a simple racing or asteroids game
* \todo [med] Clear all references of EntityVisualState and EntityGameState
* \todo [med] Clarify functionalities of CreateEntity, KillEntity, RegisterEntity, UnregisterEntity (which frees memory, which does only change the state of the Model?)
* \todo [med] Add basic networking
* \todo [med] Add serialization
* \todo [med] Add cal3d support
* \todo [med] Smooth Physics at convex collisions (somehow there is jitter)
* \todo [med] Create better collsion response
* \todo [med] In rare cases two Actors can penetrate each other (probably at slow velocities)
* \todo [low] Add a timer object to keep track of what is sucking performance.
* \todo [low] Add a inspector widget that shows information about a selected Entity
* \todo [low] Use a std::map<string, string> as initialization parameters for
* Engine::Module::Init()
*
* Done:
* - [high] In Physics remove dependancy on the Model to pass on collision
* events
* - [high] Fix Physics bug when two actors collide actively (i.e. velocity
* towards each other)
* - [med] Better Game Input so that each Entity has its own ControllerState
* that can be modified
* - [med] (31-01-2009) Add support for loading levels (and saving!)
*/

114
engine/doc/Usecases.h Normal file
View File

@ -0,0 +1,114 @@
/** \page usecases Usecases
*
* This page contains some information on how various tasks are supposed to be
* performed with this engine. It should help understand how the internals
* work and how to avoid certain pitfalls.
*
* \section entity_management Entity Management
*
* Entities have to be created with Engine::CreateEntity() Factory Method.
* This will also cause the correct registration in all modules (especially in
* the Engine::Physics module). And create the registrations for it.
*
* Once an Entity is no more used, one has to call Engine::DestroyEntity().
*
* \section entitiy_drawing Drawing of an Entity
*
* Aim: The visual state must always represent the visual state of the game
* state.
*
* To do this we always have to update the visual entity from the game state
* entity.
*
* \code
* View::DrawEntity (Entity* entity) {
* // if this entity has no visual part we don't need to draw
* if (! entity->mPhysicState) return;
*
* // update entity->mVisualState based on entity->mGameState
*
* // perform positioning based on entity->mPhysicState
*
* // perform drawing based on entity->mVisualState
* }
* \endcode
*
* \section game_input Game Input
*
* Each Entity has a ControllerState which keeps track on how the Entity is
* currently steered. This is then processed by the model each frame for each
* entity and updates the velocities / orientations etc. before the physical
* simulation is started. The player input simply forwards its input to the
* Entity with the Player Id and updates the ControllerState of the Entity
* with the Id.
*
* To add a new key state one has to follow these steps:
* - add a new EntityControllerKeyState e.g. EntityKeyStateCrouch (must be added
* above EntityKeyStateLast)
* - define the behaviour of the control in Entity::ProcessController() which
* updates the EntityPhysicalState of the Entity
* - add a command to be able to bind a key to the EntityControllerKeyState
*
* \section console_input Console Input
*
* Since it could be that the key being pressed has to be forwarded to another
* system such as the Menu or the Engine::Console system, we have to query the
* Engine::Model whether it is active and forward the input if it is the case.
* Otherwise we just execute the binding for the key (if it exists).
*
* \code
* Controller::OnKeyDown (SDLKey key) {
* if (mConsole->GetActive ())
* mConsole->OnKeyDown (key);
*
* if (mBinding[key].size())
* mCommands->QueueCommand (mBinding[key]);
* }
* \endcode
*
* \section addcommand Adding a Command to the Command System
*
* For the various Modules such as Engine::Controller, Engine::View, etc.
* separate files exist in which commands are defined. The filename pattern is
* usually [ModuleName]Commands.cc and contains the function
* \code void ModuleName::OnRegisterCommands () \endcode which is run during the
* initialization phase of the Engine in Engine::OnInit.
*
* Commands themselves have the signature:
* \code bool Cmd_CrazyCommand (std::vector<std::string> args) \endcode
* and return true on success and error if some error has happened. The
* prefix \e Cmd_ is not mandatory but keeps things clear.
*
* Please make sure to call Engine::CommandSetErrorString in such a case. The
* message itself will be automatically reported to the Engine::Logging system
* as a warning since Command errors are hopefully not that important that
* they can crash the whole Engine.
*
* To register a Command to the Engine::Commands system you have to call \code
* AddCommand ("crazycommand", Cmd_CrazyCommand); \endcode in
* ModuleName::OnRegisterCommands. With this the Command is accessible through
* the Command system.
*
* \section addvariable Adding a Variable to the Engine::Variable System
*
* To register a variable to the Engine::Variables Module one \e must use a
* static variable of type Engine::Variable and use a special constructor:
* \code
* static Var_Variable PlayerSpeed ("playerspeed", "1.25");
* \endcode
* This constructor takes care of registering the Variable PlayerSpeedVariable
* and its value to the Engine::Variables Module. The first argument is the
* name of the variable which can be used to retrieve a pointer to the
* Variable with Engine::GetVariable, Engine::GetVariableString, etc. The
* second argument is the value which the system will automatically try to
* convert to a float. This float is then returned if Engine::GetVariableFloat
* is called.
*
* The prefix \e Var_ is for readability in the code.
*
* The keyword \e static ensures that the lifespan of the variable is not only
* in a local function environment and thus mandatory. However it is
* registered at that time the program executes the first time the line in
* which the definition was made. To be safe define all your variables in the
* global scope of te source file of your Engine::Module.
*/

16
engine/globals.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _GLOBALS_H
#define _GLOBALS_H
#define LOG_DEFAULT_LEVEL LogLevelMessage
#define LOG_MAX_MESSAGE_LENGTH 1024
#define VIEW_DEFAULT_HEIGHT 600
#define VIEW_DEFAULT_WIDTH 800
#ifdef WIN32
#include <windows.h>
#endif
#include <SDL/SDL.h>
#endif // _GLOBALS_H

166
engine/keytable.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef KEYTABLE_H
#define KEYTABLE_H
#include <SDL/SDL.h>
/** \brief Defines the key codes for mouse buttons
*
* With this enum we can treat mouse button events the same way as we treat
* keyboard events. To do so we define the left button as the last keyboard
* button.
*/
enum MouseButton {
MouseButtonUnknown = SDLK_LAST,
MouseButtonLeft,
MouseButtonMiddle,
MouseButtonRight,
MouseButtonWheelUp,
MouseButtonWheelDown,
MouseButtonLast
};
struct key_definition {
int keynum;
const char *keystr;
};
const int keytable_last = MouseButtonLast;
static const key_definition key_table[] = {
{SDLK_BACKSPACE, "backspace"},
{SDLK_TAB, "tab"},
// {SDLK_CLEAR, "12"},
{SDLK_RETURN, "return"},
{SDLK_PAUSE, "pause"},
{SDLK_ESCAPE, "escape"},
{SDLK_SPACE, "space"},
{SDLK_EXCLAIM, "exclaim"},
// {SDLK_QUOTEDBL, ""},
{SDLK_HASH, "#"},
{SDLK_DOLLAR, "$"},
{SDLK_AMPERSAND, "&"},
// {SDLK_QUOTE, "39"},
// {SDLK_LEFTPAREN, "("},
// {SDLK_RIGHTPAREN, ")"},
// {SDLK_ASTERISK, "*"},
{SDLK_PLUS, "+"},
{SDLK_COMMA, ","},
{SDLK_MINUS, "-"},
{SDLK_PERIOD, "."},
{SDLK_SLASH, "/"},
{SDLK_0, "0"},
{SDLK_1, "1"},
{SDLK_2, "2"},
{SDLK_3, "3"},
{SDLK_4, "4"},
{SDLK_5, "5"},
{SDLK_6, "6"},
{SDLK_7, "7"},
{SDLK_8, "8"},
{SDLK_9, "9"},
{SDLK_COLON, ":"},
{SDLK_SEMICOLON, ";"},
{SDLK_LESS, "<"},
{SDLK_EQUALS, "="},
{SDLK_GREATER, ">"},
{SDLK_QUESTION, "?"},
{SDLK_AT, "@"},
{SDLK_LEFTBRACKET, "["},
{SDLK_BACKSLASH, "\\"},
{SDLK_RIGHTBRACKET, "]"},
{SDLK_CARET, "^"},
{SDLK_UNDERSCORE, "_"},
{SDLK_BACKQUOTE, "`"},
{SDLK_a, "a"},
{SDLK_b, "b"},
{SDLK_c, "c"},
{SDLK_d, "d"},
{SDLK_e, "e"},
{SDLK_f, "f"},
{SDLK_g, "g"},
{SDLK_h, "h"},
{SDLK_i, "i"},
{SDLK_j, "j"},
{SDLK_k, "k"},
{SDLK_l, "l"},
{SDLK_m, "m"},
{SDLK_n, "n"},
{SDLK_o, "o"},
{SDLK_p, "p"},
{SDLK_q, "q"},
{SDLK_r, "r"},
{SDLK_s, "s"},
{SDLK_t, "t"},
{SDLK_u, "u"},
{SDLK_v, "v"},
{SDLK_w, "w"},
{SDLK_x, "x"},
{SDLK_y, "y"},
{SDLK_z, "z"},
{SDLK_KP0, "keypad_0"},
{SDLK_KP1, "keypad_1"},
{SDLK_KP2, "keypad_2"},
{SDLK_KP3, "keypad_3"},
{SDLK_KP4, "keypad_4"},
{SDLK_KP5, "keypad_5"},
{SDLK_KP6, "keypad_6"},
{SDLK_KP7, "keypad_7"},
{SDLK_KP8, "keypad_8"},
{SDLK_KP9, "keypad_9"},
{SDLK_KP_PERIOD, "keypad_period"},
{SDLK_KP_DIVIDE, "keypad_devide"},
{SDLK_KP_MULTIPLY, "keypad_multiply"},
{SDLK_KP_MINUS, "keypad_minus"},
{SDLK_KP_PLUS, "keypad_plus"},
{SDLK_KP_ENTER, "keypad_enter"},
{SDLK_KP_EQUALS, "keypad_equals"},
{SDLK_UP, "up"},
{SDLK_DOWN, "down"},
{SDLK_RIGHT, "right"},
{SDLK_LEFT, "left"},
{SDLK_INSERT, "insert"},
{SDLK_HOME, "home"},
{SDLK_END, "end"},
{SDLK_PAGEUP, "pageup"},
{SDLK_PAGEDOWN, "pagedown"},
{SDLK_F1, "f1"},
{SDLK_F2, "f2"},
{SDLK_F3, "f3"},
{SDLK_F4, "f4"},
{SDLK_F5, "f5"},
{SDLK_F6, "f6"},
{SDLK_F7, "f7"},
{SDLK_F8, "f8"},
{SDLK_F9, "f9"},
{SDLK_F10, "f10"},
{SDLK_F11, "f11"},
{SDLK_F12, "f12"},
{SDLK_F13, "f13"},
{SDLK_F14, "f14"},
{SDLK_F15, "f15"},
{SDLK_NUMLOCK, "numlock"},
{SDLK_CAPSLOCK, "capslock"},
{SDLK_SCROLLOCK, "scrollock"},
{SDLK_RSHIFT, "right_shift"},
{SDLK_LSHIFT, "left_shift"},
{SDLK_RCTRL, "right_ctrl"},
{SDLK_LCTRL, "left_ctrl"},
{SDLK_RALT, "right_alt"},
{SDLK_LALT, "left_alt"},
{SDLK_RMETA, "right_meta"},
{SDLK_LMETA, "left_meta"},
{SDLK_LSUPER, "left_super"}, /* Left "Windows" key */
{SDLK_RSUPER, "right_super"}, /* Right "Windows" key */
{SDLK_MODE, "altgr"}, /* "Alt Gr" key */
{SDLK_COMPOSE, "compose"}, /* Multi-key compose key */
{SDLK_DELETE, "delete"},
{MouseButtonLeft, "mouse_left"},
{MouseButtonMiddle, "mouse_middle"},
{MouseButtonRight, "mouse_right"},
{MouseButtonWheelUp, "mouse_wheelup"},
{MouseButtonWheelDown, "mouse_wheeldown"},
{keytable_last, NULL}
};
#endif /* KEYTABLE_H */

View File

@ -0,0 +1,3 @@
ADD_SUBDIRECTORY ( mathlib )
ADD_SUBDIRECTORY ( coll2d )
ADD_SUBDIRECTORY ( oglft )

View File

@ -0,0 +1,28 @@
# - Try to find UnitTest++
#
#
SET (UNITTEST++_FOUND FALSE)
FIND_PATH (UNITTEST++_INCLUDE_DIR UnitTest++.h /usr/include/unittest++ /usr/local/include/unittest++ $ENV{UNITTESTXX_PATH}/src $ENV{UNITTESTXX_INCLUDE_PATH})
FIND_LIBRARY (UNITTEST++_LIBRARY NAMES UnitTest++ PATHS /usr/lib /usr/local/lib $ENV{UNITTESTXX_PATH} ENV{UNITTESTXX_LIBRARY_PATH})
IF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY)
SET (UNITTEST++_FOUND TRUE)
ENDIF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY)
IF (UNITTEST++_FOUND)
IF (NOT UnitTest++_FIND_QUIETLY)
MESSAGE(STATUS "Found UnitTest++: ${UNITTEST++_LIBRARY}")
ENDIF (NOT UnitTest++_FIND_QUIETLY)
ELSE (UNITTEST++_FOUND)
IF (UnitTest++_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find UnitTest++")
ENDIF (UnitTest++_FIND_REQUIRED)
ENDIF (UNITTEST++_FOUND)
MARK_AS_ADVANCED (
UNITTEST++_INCLUDE_DIR
UNITTEST++_LIBRARY
)

View File

@ -0,0 +1,21 @@
PROJECT (COLL2D)
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
# Needed for UnitTest++
LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake )
SET ( COLL2D_SRCS
src/coll2d.cc
)
INCLUDE_DIRECTORIES ( include ../mathlib/ )
SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES
LINKER_LANGUAGE CXX
)
SUBDIRS (tests)
ADD_LIBRARY ( coll2d ${COLL2D_SRCS} )

View File

@ -0,0 +1,346 @@
#ifndef _COLL2D_H
#define _COLL2D_H
/** \brief Coll2d - A 2d collision detection library
* \author Martin Felis <martin@silef.de>
*
* This library provides functions to detect collisions between polygons and
* spheres.
*
* Notes:
* - vertices of polygons are described in local coordinates
* - return values of check_collision are in global coordinates
* - all Shapes get copied to a temporary instance which will then get
* transferred into global coordinates
* - Polygons are assumed to be convex and the vertices are stored
* counter clockwise.
* - The transformation of the global position and velocities towards
* relative position and velocities happens in the
* int check_collision_<type>_<type> functions!
*/
#include <mathlib.h>
#include <iostream>
#include <cstring>
#include <coll2d_errors.h>
namespace coll2d {
/** \brief Contains the information of a collision
*
* \param normal The normal of the reference plane
* \param point The actual point where the collision happens in global
* cooldinates
* \param time If both objects move for this amount of time the contact
* will occur.
* \param reference_shape
* If 0, the first shape passed to he check_collision function
* is the reference shape, otherwise the second.
*/
struct CollisionInfo {
vector3d normal;
vector3d point;
float time;
int reference_shape;
CollisionInfo () : normal (0., 0., 0.), point (0., 0., 0.), time (-1.), reference_shape (-1) {
}
CollisionInfo& operator= (const CollisionInfo& info) {
if (this != &info) {
normal = info.normal;
point = info.point;
time = info.time;
reference_shape = info.reference_shape;
}
return *this;
}
void doPrint (const char* msg) {
std::cout << msg;
std::cout << "Time = " << time << std::endl;
normal.print ("Normal = ");
point.print ( "Point = ");
std::cout << "Reference = " << reference_shape << std::endl;
}
};
/** \brief Base class for all shapes
*
*/
class Shape {
protected:
vector3d mPosition;
vector3d mVelocity;
float mAngle;
float mAngleVelocity;
virtual void dummy() {
}
public:
Shape():
mPosition (0., 0., 0.),
mVelocity (0., 0., 0.),
mAngle (0.),
mAngleVelocity (0.) {
}
Shape (const Shape &shape):
mPosition (shape.mPosition),
mVelocity (shape.mVelocity),
mAngle (shape.mAngle),
mAngleVelocity (shape.mAngleVelocity)
{ }
virtual ~Shape () {};
/** \brief Creates and returns a copy of itself */
virtual Shape* getCopy () = 0;
void setPosition(vector3d position) {
mPosition = position;
}
vector3d getPosition() {
return mPosition;
}
void setVelocity(vector3d velocity) {
mVelocity = velocity;
}
vector3d getVelocity() {
return mVelocity;
}
void setAngle (const float &angle) {
mAngle = angle;
}
float getAngle () {
return mAngle;
}
void setAngleVelocity (float angle_velocity) {
mAngleVelocity = angle_velocity;
}
float getAngleVelocity () {
return mAngleVelocity;
}
virtual void doPrintType() {
std::cout << "Shape" << std::endl;
}
virtual void doPrint (const char* name) {
std::cout << name << "<unknown shape>" << std::endl;
}
friend int check_collision_rel(Shape *shape_a, Shape *shape_b,
vector3d *velocity_b, CollisionInfo* info);
};
class Polygon: public Shape {
private:
unsigned int mVerticeCount;
vector3d *mVertices;
bool mFreeVertices;
public:
Polygon() {
mVerticeCount = 0;
mVertices = NULL;
mFreeVertices = false;
}
Polygon(unsigned int n) {
mVerticeCount = n;
mVertices = new vector3d[n];
mFreeVertices = true;
}
Polygon(unsigned int n, vector3d *vertices) {
mVerticeCount = n;
mFreeVertices = false;
if (vertices == NULL && n > 0) {
mVertices = new vector3d [n];
mFreeVertices = true;
}
mVertices = vertices;
}
Polygon (const Polygon &polygon) : Shape (polygon) {
mVerticeCount = polygon.mVerticeCount;
mFreeVertices = true;
mVertices = new vector3d[mVerticeCount];
memcpy (mVertices, polygon.mVertices, sizeof (vector3d) * mVerticeCount);
}
virtual ~Polygon () {
if (mFreeVertices == true && mVertices)
delete[] mVertices;
}
virtual Polygon* getCopy () {
Polygon *copy = new Polygon (*this);
assert (copy);
return copy;
}
virtual void doPrintType() {
std::cout << "Polygon" << std::endl;
}
virtual void doPrint (const char* name) {
std::cout << name << " (Polygon)" << std::endl;
std::cout << "mVerticeCount = " << mVerticeCount << std::endl;
unsigned int i;
for (i = 0; i < mVerticeCount; i ++) {
std::cout << i << " = " << mVertices[i][0] << ", " <<
mVertices[i][1] << ", " <<
mVertices[i][2] << std::endl;
}
std::cout << "mPosition = " << mPosition[0] <<", " <<
mPosition[1] << ", " <<
mPosition[2] << std::endl;
std::cout << "mVelocity = " << mVelocity[0] <<", " <<
mVelocity[1] << ", " <<
mVelocity[2] << std::endl;
std::cout << "mAngle = " << mAngle << std::endl;
std::cout << "mAngleVelocity = " << mAngleVelocity << std::endl;
}
unsigned int getVerticeCount () {
return mVerticeCount;
}
vector3d& getVertice (unsigned int i) {
assert (i >= 0 && i < mVerticeCount);
return mVertices [i];
}
void setVertice (unsigned int i, const vector3d &vertice) {
assert (i >= 0 && i < mVerticeCount);
mVertices[i] = vertice;
}
friend int check_collision_polygon_sphere(Shape *polygon_a,
Shape *sphere_b, CollisionInfo* info);
};
class Sphere: public Shape {
private:
float mRadius;
public:
Sphere (float radius) {
mRadius = radius;
Shape::setPosition (vector3d (0., 0., 0.));
}
Sphere(float radius, const vector3d &position) {
mRadius = radius;
Shape::setPosition(position);
}
Sphere (const Sphere &sphere):
Shape (sphere),
mRadius (sphere.mRadius) { }
virtual Sphere* getCopy() {
Sphere* copy = new Sphere (*this);
return copy;
}
virtual void doPrintType() {
std::cout << "Sphere" << std::endl;
}
virtual void doPrint (const char* name) {
std::cout << name << " (Sphere)" << std::endl;
std::cout << "mRadius = " << mRadius << std::endl;
std::cout << "mPosition = " << mPosition[0] <<", " <<
mPosition[1] << ", " <<
mPosition[2] << std::endl;
std::cout << "mVelocity = " << mVelocity[0] <<", " <<
mVelocity[1] << ", " <<
mVelocity[2] << std::endl;
std::cout << "mAngle = " << mAngle << std::endl;
std::cout << "mAngleVelocity = " << mAngleVelocity << std::endl;
}
void setRadius (float radius) {
mRadius = radius;
}
float getRadius () {
return mRadius;
}
friend int check_collision_polygon_sphere(Shape *polygon_a,
Shape *sphere_b, CollisionInfo* info);
};
/** \brief The higher level function to call for collision detection
*
* \param stepsize the timestep within we want to check for the collision
* \param shape_a first shape
* \param shape_b second shape
* \param info information about the collision will be to info
*
* \returns 0 - no collision, negative on error, positive on collision
*/
int check_collision(float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo* info);
/** \brief The higher level function to call for collision detection
*
* \param shape_a first shape
* \param shape_b second shape
* \param velocity_b relative velocity of b to a
* \param info information about the collision will be to info
*
* \returns 0 - no collision, negative on error, positive on collision
*/
int check_collision_rel(float timestep, Shape *shape_a, Shape *shape_b, vector3d *velocity_b,
CollisionInfo* info);
/** \brief Callback signature for the check functions */
typedef int (*check_cb)(float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo* info);
/** \brief Returns the check functions which performs the checks depending
* on their types. */
check_cb get_check(Shape *shape_a, Shape *shape_b);
/** \brief Performs a check between a polygon and a sphere
*/
int check_collision_polygon_sphere(float timestep, Shape *shape_a, Shape *shape_b,
CollisionInfo* info);
/** \brief Performs a check between a sphere and a sphere
*/
int check_collision_sphere_sphere(float timestep, Shape *shape_a, Shape *shape_b,
CollisionInfo* info);
/** \brief Calculates the time it takes for a point to touch a plane
*
* \param normal normal vector of the plane
* \param plane_point point on the vector
* \param point point that is moving
* \param velocity velocity of the point
*
* \returns -1 if point is moving away from the plane (or is below and
* moves even further below
* >= 0 if point is moving along the plane or towards the plane
*
* This function depends on the scale of velocity. It is assumed
* that velocity represents the displacement in one frame. In this
* case a return value > 1 means there will not be a contact
* within this frame.
*/
float calculate_contact_plane_point(vector3d &normal, vector3d &plane_point, vector3d &point, vector3d &velocity);
}
#endif /* _COLL2D_H */

View File

@ -0,0 +1,9 @@
#ifndef _COLL2D_ERRORS
#define _COLL2D_ERRORS
#define CHECK_ERROR_UNKNOWN -9999
#define CHECK_ERROR_NOT_IMPLEMENTED -1
#define CHECK_ERROR_INVALID_TYPES -2
#define CHECK_ERROR_OVERLAP -3
#endif /* _COLL2D_ERRORS */

View File

@ -0,0 +1,572 @@
#include <cstring>
#include<coll2d.h>
#include <iostream>
using namespace std;
namespace coll2d {
int check_collision (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) {
check_cb check = NULL;
check = get_check (shape_a, shape_b);
if ( check == NULL ) {
return CHECK_ERROR_NOT_IMPLEMENTED;
}
return check (timestep, shape_a, shape_b, info);
};
check_cb get_check (Shape *shape_a, Shape *shape_b) {
if ( (dynamic_cast<Polygon*> (shape_a) != NULL)
&& (dynamic_cast<Sphere*> (shape_b) != NULL) ) {
return check_collision_polygon_sphere;
}
else if ( (dynamic_cast<Polygon*> (shape_b) != NULL)
&& (dynamic_cast<Sphere*> (shape_a) != NULL) ) {
return check_collision_polygon_sphere;
} else if ( (dynamic_cast<Sphere*> (shape_b) != NULL)
&& (dynamic_cast<Sphere*> (shape_a) != NULL) ) {
return check_collision_sphere_sphere;
}
return NULL;
}
int check_collision_rel (float timestep, Shape *shape_a, Shape *shape_b, vector3d *velocity_b, CollisionInfo *info) {
check_cb check = NULL;
shape_b->setVelocity (*velocity_b);
check = get_check (shape_a, shape_b);
if ( check == NULL ) {
return CHECK_ERROR_NOT_IMPLEMENTED;
}
return check (timestep, shape_a, shape_b, info);
};
/** \brief calculates the time for a moving point to touch a plane
*
* \param normal The normal of the plane
* \param plane_point a point on the plane
* \param point the moving point
* \param velocity the velocity of the point
*
* \returns If the return value is negative, the point is moving away from the
* plane or is below the plane and thus does not touch it. Otherwise
* point + (return value) * velocity
* is on the plane.
*/
float calculate_contact_plane_point (vector3d &normal, vector3d &plane_point, vector3d &point, vector3d &velocity) {
vector3d temp_vector (point);
temp_vector -= plane_point;
// If the following is < 0 then the point is "below" the plane
if (normal * temp_vector < 0) {
// If the following is < 0 then we move even deeper
if ( normal * velocity < 0 )
return -999;
// Or towards the upper side of the plane.
else
return -111;
}
int i,imax = -1;
float vmax = 0.;
for (i = 0; i < 3; i++) {
if ( normal[i] * velocity[i] < vmax) {
vmax = normal[i] * velocity[i];
imax = i;
}
}
if (imax == -1) {
return -1.;
}
return (normal[imax] * plane_point[imax] - normal[imax] * point[imax]) / vmax;
}
// #define VERBOSE_PHASE
/** \brief Checks whether a sphere penetrates one of the polygones edges
*
* This check only finds intersections of the sphere with one of the sides in
* other words it will not find intersections with the vertices. As for convex
* polygons a sphere will either touch one of the edges or one of the
* vertices.
*
* First thing we do is calculate the normals pointing out of the polygon.
* Then for each edge e of the polygon (is being done by the function
* calculate_contatc_plane_point (...):
* - Calculate the point that would be the first to touch edge e if a contact
* occurs
* - Calculate the time it would take for this point to touch the edge with
* the relative velocity of the sphere
*
* Next we calculatefor each edge the point on the sphere which would be the first to touch
* the polygon, if it was moving towards the current edge.
*/
int check_collision_polygon_sphere_sides (Shape *shape_a, Shape *shape_b, CollisionInfo *info) {
Polygon* polygon = dynamic_cast<Polygon*> (shape_a);
Sphere* sphere = dynamic_cast<Sphere*> (shape_b);
if ( polygon == NULL && sphere == NULL) {
polygon = dynamic_cast<Polygon*> (shape_b);
sphere = dynamic_cast<Sphere*> (shape_a);
}
if (!polygon || !sphere) {
return CHECK_ERROR_INVALID_TYPES;
}
vector3d velocity_sphere = sphere->getVelocity();
vector3d temp_vector (velocity_sphere);
if (temp_vector.length2() == 0.)
return 0;
// Calculate the normals pointing OUT of the polygon
float *t = NULL, t_min = 10000.;
unsigned int i,j,vertices,count = 0;
vertices = polygon->getVerticeCount();
vector3d *normals = NULL;
vector3d side (0., 0., 0.);
vector3d up (0., 1., 0.);
/*
if (polygon->getPosition().length() > 0.) {
for (i = 0; i < vertices; i++) {
polygon->getVertice(i) += polygon->getPosition();
}
}
*/
// normals contains all the normal vectors out of
// the polygon
normals = new vector3d[vertices];
// The array t contains the time it takes until the sphere
// would touch the plane with the given velocity
t = new float[vertices];
for (i = 0; i < vertices; i++) {
j = (i + 1) % vertices;
side = polygon->getVertice(j);
side -= polygon->getVertice(i);
normals[i] = cross_product (side, up);
normals[i] /= normals[i].length ();
// temp_vector is the point on the sphere that would touch the plane first
// if it was directly moving towards it
temp_vector = normals[i];
temp_vector *= - sphere->getRadius();
temp_vector += sphere->getPosition();
t[i] = calculate_contact_plane_point (normals[i], polygon->getVertice(i), temp_vector, velocity_sphere);
#ifdef VERBOSE_PHASE
cout << "= next =" << endl;
cout << "t[" << i << "] = " << t[i] << " normal = "; normals[i].print ();
cout << "point = "; temp_vector.print();
cout << "plane point = "; polygon->getVertice(i).print();
#endif
if (t[i] < 0)
normals[i].setValues (0., 0., 0.);
else {
if (t[i] < t_min)
t_min = t[i];
count ++;
}
}
if (count == 0) {
delete[] t;
delete[] normals;
return 0;
}
vector3d contact_point, cp_near;
/// \ToDo: Instead of checking all planes, remember which ones to check
for (i = 0; i < vertices; i++) {
if ( t[i] >= 0) {
// So there seems to occur an collision. Now we have to check,
// whether this is actually between the points that define the
// plane.
unsigned int min_distance_vertex_index = i, other_vertice = (i + 1) % vertices;
float min_distance;
j = (i + 1) % vertices;
// First we calculate the actual contact point:
temp_vector = normals[i];
temp_vector *= - sphere->getRadius();
temp_vector *= t[i];
temp_vector += sphere->getPosition();
contact_point = normals[i];
contact_point *= -sphere->getRadius();
contact_point += temp_vector;
// Now we calculate to which vertice this point is closer:
temp_vector = contact_point;
temp_vector -= polygon->getVertice(i);
min_distance = temp_vector.length2 ();
temp_vector = contact_point;
temp_vector -= polygon->getVertice(j);
if (min_distance > temp_vector.length2 ()) {
min_distance_vertex_index = j;
other_vertice = i;
}
// Then we want to see, whether the contact point actually lies
// on the vector that goes from one vertice to the other. For
// this we calculate (cp - near) * (far - near). The value
// cannot be greater than 1. (otherwise we would have picked
// the other vector as near. If it is < 0 then it is outside
// of the polygon.
temp_vector = polygon->getVertice(other_vertice);
temp_vector -= polygon->getVertice(min_distance_vertex_index);
temp_vector /= temp_vector.length();
cp_near = contact_point;
cp_near -= polygon->getVertice(min_distance_vertex_index);
float val = cp_near * temp_vector;
if ( (val >= 0 && val <= 1) && (t[i] <= 1.)) {
info->time = t[i];
info->normal = normals[i];
info->point = sphere->getPosition();
info->point += sphere->getVelocity() * t[i];
info->point -= normals[i] * sphere->getRadius ();
delete[] t;
delete[] normals;
return 1;
}
}
}
/*
if ( (t_min <= 1.) && (t_min >= 0.)) {
return 1;
}
*/
delete[] t;
delete[] normals;
if (t_min > 1.)
return 0;
return 0;
// return CHECK_ERROR_UNKNOWN;
};
int check_collision_polygon_sphere_vertices (Shape *shape_a, Shape *shape_b, CollisionInfo *info) {
Polygon* polygon = dynamic_cast<Polygon*> (shape_a);
Sphere* sphere = dynamic_cast<Sphere*> (shape_b);
if ( polygon == NULL && sphere == NULL) {
polygon = dynamic_cast<Polygon*> (shape_b);
sphere = dynamic_cast<Sphere*> (shape_a);
}
if (!polygon || !sphere) {
return CHECK_ERROR_INVALID_TYPES;
}
// Transform global velocities to relative velocities:
/*
sphere->setVelocity (sphere->getVelocity() - polygon->getVelocity());
*/
vector3d velocity_sphere = sphere->getVelocity();
vector3d temp_vector (velocity_sphere);
if (temp_vector.length2() == 0.) {
#ifdef VERBOSE_PHASE
cout << "sphere has no velocity!" << endl;
#endif
return 0;
}
vector3d contact_point, cp_near;
// Okay if we happen to be here, there still might be one of
// the corners colliding with the sphere.
float a, b, c, t1, t2, t_min = 10000;
unsigned int i, j, vertices;
vector3d position (sphere->getPosition());
vector3d velocity (sphere->getVelocity());
float radius = sphere->getRadius();
vertices = polygon->getVerticeCount();
for (i = 0; i < vertices; i++) {
vector3d vertice (polygon->getVertice(i));
a = b = c = 0;
// We must ensure that all happens in the x-z-plane (so y == 0.)
vertice[1] = 0.;
position[1] = 0.;
vector3d distance_sphere_vertice;
for (j = 0; j < 3; j++) {
distance_sphere_vertice[j] = position[j] - vertice[j];
}
#ifdef VERBOSE_PHASE
cout << " ===== " << endl;
cout << "vertice = ";
vertice.print ();
velocity.print ();
position.print ();
cout << "radius = " << radius << endl;
cout << "distance = " << distance_sphere_vertice.length () << endl;;
#endif
for (j = 0; j < 3; j++) {
a += velocity[j]*velocity[j];
b += 2 * velocity[j] * (position[j] - vertice[j]);
c += position[j] * position[j] - 2 * position[j] * vertice[j] + vertice[j] * vertice[j];
}
c -= radius * radius;
if (solve_quadratic (a, b, c, &t1, &t2)) {
// cout << "solve_quadratic = " << t1 << "\t" << t2 << endl;
float t_temp_min = t2;
if (t1 < t2)
t_temp_min = t1;
if (t_temp_min < t_min) {
if ((t_temp_min <= 1.) && (t_temp_min >= 0.)) {
t_min = t_temp_min;
temp_vector = position;
temp_vector -= vertice;
temp_vector /= temp_vector.length ();
#ifdef VERBOSE_PHASE
cout << "new closest point t = " << t_min << endl;
#endif
info->time = t_min;
info->normal = temp_vector;
info->point = vertice;
}
}
}
}
if ( (t_min <= 1.) && (t_min >= 0.)) {
return 1;
}
if (t_min > 1.)
return 0;
return CHECK_ERROR_UNKNOWN;
};
int check_collision_polygon_sphere (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) {
/* If the first shape given is a sphere and the second one a shape, we have
* to remember it to set the right value to *info.
*/
bool swapped = false;
Polygon* polygon_cast_test = dynamic_cast<Polygon*> (shape_a);
Sphere* sphere_cast_test = dynamic_cast<Sphere*> (shape_b);
if ( polygon_cast_test == NULL && sphere_cast_test == NULL) {
polygon_cast_test = dynamic_cast<Polygon*> (shape_b);
sphere_cast_test = dynamic_cast<Sphere*> (shape_a);
swapped = true;
}
if (!polygon_cast_test || !sphere_cast_test) {
return CHECK_ERROR_INVALID_TYPES;
}
Polygon polygon_copy (*polygon_cast_test);
Sphere sphere_copy (*sphere_cast_test);
unsigned int vertices;
vertices = polygon_copy.getVerticeCount();
#ifdef VERBOSE_PHASE
cout << "======== New Polygon Sphere Test: Phase 1" << endl;
if (swapped)
cout << "Swapped!" << endl;
cout << "Polygon position: ";
polygon_copy.getPosition().print ();
cout << "Polygon velocity: ";
polygon_copy.getVelocity().print ();
cout << "Sphere position: ";
sphere_copy.getPosition().print ();
cout << "Sphere velocity: ";
sphere_copy.getVelocity().print ();
cout << "Timestep : " << timestep << endl;
cout << "Vertices before transformation = " << endl;
int i;
for (i = 0; i < vertices; i++) {
polygon_copy.getVertice(i).print();
}
#endif
// Here we translate the polygon and its velocity into the reference frame
// of the polygon.
sphere_copy.setPosition (sphere_copy.getPosition() - polygon_copy.getPosition());
sphere_copy.setPosition (sphere_copy.getPosition().rotate_y (-polygon_copy.getAngle()));
// Here scale the velocity so that our time horizon lies in [0., 1.]
sphere_copy.setVelocity ((sphere_copy.getVelocity() - polygon_copy.getVelocity() ) * timestep );
sphere_copy.setVelocity (sphere_copy.getVelocity().rotate_y (-polygon_copy.getAngle()));
#ifdef VERBOSE_PHASE
cout << "Vertices after transformation = " << endl;
for (i = 0; i < vertices; i++) {
polygon_copy.getVertice(i).print();
}
#endif
#ifdef VERBOSE_PHASE
cout << "After transformation:" << endl;
cout << "Polygon position: ";
polygon_copy.getPosition().print ();
cout << "Polygon velocity: ";
polygon_copy.getVelocity().print ();
cout << "Sphere position: ";
sphere_copy.getPosition().print ();
cout << "Sphere velocity: ";
sphere_copy.getVelocity().print ();
#endif
CollisionInfo sides_info;
CollisionInfo vertices_info;
int sides_result = 0, vertices_result = 0;
/* Tricky part: Since polygons are assumed to be convex and we calculated
* with both methods a collision, we take the sides result. Since they are
* convex the first event to happen is the collision with the side.
* Otherwise it would first touch the vertice and then the side, which is
* not possible. (\Todo true?)
*/
sides_result = check_collision_polygon_sphere_sides (&polygon_copy, &sphere_copy, &sides_info);
// We have to transform the time back to [0., timestep]
sides_info.time *= timestep;
#ifdef VERBOSE_PHASE
cout << "sides_result = " << sides_result << " t = " << sides_info.time << endl;
#endif
if (sides_result > 0) {
sides_info.point.rotate_y (polygon_copy.getAngle());
sides_info.normal.rotate_y (polygon_copy.getAngle());
sides_info.point += polygon_copy.getPosition();
memcpy (info, &sides_info, sizeof (CollisionInfo));
sides_info.time *= timestep;
if (swapped == true) {
info->reference_shape = 1;
} else {
info->reference_shape = 0;
}
return sides_result;
}
vertices_result = check_collision_polygon_sphere_vertices (&polygon_copy, &sphere_copy, &vertices_info);
// We have to transform the time back to [0., timestep]
vertices_info.time *= timestep;
#ifdef VERBOSE_PHASE
cout << "vertices_res = " << vertices_result << " t = " << vertices_info.time << endl;
cout << "vertices_point = ";
vertices_info.point.print();
#endif
if (vertices_result > 0) {
vertices_info.point.rotate_y (polygon_copy.getAngle());
vertices_info.normal.rotate_y (polygon_copy.getAngle());
vertices_info.point += polygon_copy.getPosition();
memcpy (info, &vertices_info, sizeof (CollisionInfo));
if (swapped == true) {
info->reference_shape = 1;
} else {
info->reference_shape = 0;
}
return vertices_result;
}
if ((sides_result == 0) && (vertices_result == 0))
return 0;
return CHECK_ERROR_UNKNOWN;
};
int check_collision_sphere_sphere (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) {
/* If the first shape given is a sphere and the second one a shape, we have
* to remember it to set the right value to *info.
*/
Sphere* sphere_test_a = dynamic_cast<Sphere*> (shape_a);
Sphere* sphere_test_b = dynamic_cast<Sphere*> (shape_b);
if (!sphere_test_a || !sphere_test_b) {
return CHECK_ERROR_INVALID_TYPES;
}
Sphere sphere_a (*sphere_test_a);
Sphere sphere_b (*sphere_test_b);
// First we check whether there is actually a relative velocity towards each
// other:
vector3d rel_velocity = sphere_b.getVelocity ();
rel_velocity -= sphere_a.getVelocity ();
rel_velocity *= timestep;
if (rel_velocity.length2() == 0.)
return 0;
vector3d rel_position = sphere_b.getPosition ();
rel_position -= sphere_a.getPosition ();
// We need to ignore height differences
rel_position[1] = 0.;
rel_velocity[1] = 0.;
vector3d rel_position_norm = rel_position;
rel_position_norm.normalize ();
float velocity_projection = rel_position_norm * rel_velocity;
float distance = rel_position.length();
if (velocity_projection >= 0.)
return 0;
float t = (- distance + sphere_a.getRadius () + sphere_b.getRadius ()) / velocity_projection;
#ifdef VERBOSE_PHASE
cout << "==== New Sphere Sphere Test ====" << endl;
cout << "Relative Position = ";
rel_position.print ();
cout << "Relative Velocity = ";
rel_velocity.print ();
cout << "velocity_projection = " << velocity_projection << endl;
cout << "distance = " << distance << endl;
cout << "- distance + Ra + Rb = " << - distance + sphere_a.getRadius () + sphere_b.getRadius () << endl;
cout << "t = " << t << endl;
#endif
// if t < 0 this means we would have to move back in time
// to get to the point where the two spheres touched. In other words: they
// are overlapping, hence it is an invalid state!
if (t < 0)
return CHECK_ERROR_OVERLAP;
if (t > 1)
return 0;
info->point = sphere_a.getPosition() + rel_position_norm * t;
info->time = t * timestep;
if (sphere_a.getVelocity().length2() == 0.) {
info->normal = rel_position_norm;
info->reference_shape = 0;
} else {
info->normal = rel_position_norm * -1.;
info->reference_shape = 1;
}
return 1;
};
}

View File

@ -0,0 +1,51 @@
PROJECT (ENGINETESTS)
CMAKE_MINIMUM_REQUIRED (VERSION 2.6)
# Needed for UnitTest++
LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../CMake )
SET ( TESTS_SRCS
main.cc
general.cc
polygon_sphere.cc
sphere_sphere.cc
)
FIND_PACKAGE (UnitTest++)
INCLUDE_DIRECTORIES ( ../mathlib/ )
SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES
LINKER_LANGUAGE CXX
)
IF ( UNITTEST++_FOUND )
ADD_EXECUTABLE ( coll2dtests ${TESTS_SRCS} )
INCLUDE_DIRECTORIES ( ${UNITTEST++_INCLUDE_DIR} )
SET_TARGET_PROPERTIES ( coll2dtests PROPERTIES
LINKER_LANGUAGE CXX
OUTPUT_NAME runtests
)
TARGET_LINK_LIBRARIES ( coll2dtests
${UNITTEST++_LIBRARY}
mathlib
coll2d
)
OPTION (RUN_AUTOMATIC_TESTS "Perform automatic tests after compilation?" OFF)
IF (RUN_AUTOMATIC_TESTS)
ADD_CUSTOM_COMMAND (TARGET coll2dtests
POST_BUILD
COMMAND coll2dtests
COMMENT "Running automated tests..."
)
ENDIF (RUN_AUTOMATIC_TESTS)
ENDIF ( UNITTEST++_FOUND )

View File

@ -0,0 +1,284 @@
#include <UnitTest++.h>
#include <coll2d.h>
using namespace coll2d;
using namespace std;
TEST ( SphereCopyConstructer ) {
Sphere sphere_a (123.);
sphere_a.setVelocity (vector3d(1., 2., 3.));
Sphere sphere_b (sphere_a);
CHECK_EQUAL (sphere_a.getRadius (), sphere_b.getRadius ());
vector3d velocity_a = sphere_a.getVelocity ();
vector3d velocity_b = sphere_b.getVelocity ();
CHECK (velocity_a == velocity_b );
sphere_b.setVelocity (vector3d (0., 0., 0.));
velocity_a = sphere_a.getVelocity ();
velocity_b = sphere_b.getVelocity ();
CHECK (velocity_a != velocity_b );
}
TEST ( PolygonCopyConstructer ) {
Polygon polygon_a (3);
vector3d vertice0 (-1., 0., -1.);
vector3d vertice1 (1., 0., -1.);
vector3d vertice2 (0., 0., 1.);
polygon_a.setVertice (0, vertice0);
polygon_a.setVertice (1, vertice1);
polygon_a.setVertice (2, vertice2);
polygon_a.setVelocity (vector3d(1., 2., 3.));
Polygon polygon_b (polygon_a);
vector3d velocity_a = polygon_a.getVelocity ();
vector3d velocity_b = polygon_b.getVelocity ();
CHECK (vertice0 == polygon_b.getVertice (0));
CHECK (vertice1 == polygon_b.getVertice (1));
CHECK (vertice2 == polygon_b.getVertice (2));
polygon_b.setVelocity (vector3d (0., 0., 0.));
velocity_a = polygon_a.getVelocity ();
velocity_b = polygon_b.getVelocity ();
CHECK (velocity_a != velocity_b );
}
/** Checks whether we identify the different shapes
* correctly.
*/
TEST ( CheckShapeType ) {
vector3d sphere_position (0., 0., 0.);
Shape* polygon = new Polygon (0, NULL);
Shape* sphere = new Sphere (0, sphere_position);
Shape* cast_test = NULL;
cast_test = dynamic_cast <Sphere*> ( sphere );
CHECK_EQUAL (cast_test, sphere);
cast_test = dynamic_cast <Sphere*> ( polygon );
CHECK (cast_test == NULL );
cast_test = dynamic_cast <Polygon*> (polygon);
CHECK_EQUAL (cast_test, polygon);
cast_test = dynamic_cast <Polygon*> (sphere);
CHECK (cast_test == NULL );
delete polygon;
delete sphere;
}
/** Checks whether the right check function is chosen
*/
TEST ( CheckGetCheck ) {
vector3d sphere_position (0., 0., 0.);
Shape* polygon = new Polygon (0, NULL);
Shape* sphere = new Sphere (0, sphere_position);
check_cb check = NULL;
// polygon - sphere -> polygon_sphere
check = get_check (polygon, sphere);
CHECK (static_cast<check_cb> (check) == static_cast<check_cb> (check_collision_polygon_sphere));
// sphere - polygon -> polygon_sphere
check = get_check (sphere, polygon);
CHECK (static_cast<check_cb> (check) == static_cast<check_cb> (check_collision_polygon_sphere));
// and we do not want that the pointers have changed!
// (as it used to be in previous versions)
CHECK (dynamic_cast <Polygon*> (sphere) == NULL);
CHECK (dynamic_cast <Sphere*> (polygon) == NULL);
check = get_check (sphere, sphere);
CHECK (check == check_collision_sphere_sphere);
delete polygon;
delete sphere;
}
TEST ( CheckErrorNotImplemented ) {
Shape* polygon = new Polygon (0, NULL);
vector3d velocity (0., 0., 0.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, polygon, &velocity, &info);
CHECK (result == CHECK_ERROR_NOT_IMPLEMENTED);
delete polygon;
}
TEST ( CheckCalculateContactPlanePoint ) {
vector3d normal (1., 0., 0.);
vector3d plane_point (0., 0., 0.);
vector3d point (1., 0., 0.);
vector3d velocity (-1., 0., 0);
float result;
// Directly moving towards the plane
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (1, result);
// Moving with a slight angle onto the plane
velocity.setValues (-1., 0., 0.2);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (1, result);
velocity.setValues (-1., 0., 0.);
// Point is "below" the plane and moves even deeper
plane_point.setValues (2., 0., 0);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (-999, result);
// Point is "below" but moves towards the plane
velocity.setValues (1., 0., 0);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (-111, result);
}
TEST ( CheckCollisionStepsize ) {
vector3d normal (1., 0., 0.);
vector3d plane_point (0., 0., 0.);
vector3d point (1., 0., 0.);
vector3d velocity (-1., 0., 0);
float result;
// Directly moving towards the plane
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (1, result);
// Moving with a slight angle onto the plane
velocity.setValues (-1., 0., 0.2);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (1, result);
velocity.setValues (-1., 0., 0.);
// Point is "below" the plane and moves even deeper
plane_point.setValues (2., 0., 0);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (-999, result);
// Point is "below" but moves towards the plane
velocity.setValues (1., 0., 0);
result = calculate_contact_plane_point (normal, plane_point, point, velocity);
CHECK_EQUAL (-111, result);
}
/* Here we test, whether the value of info.reference_shape is correct.
* If we swap the shapes passed to check_collision, then the value must swap,
* too.
*/
TEST ( CheckReferenceShapeValue ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
// first part of the test, the collision occurs
// on an edge
vector3d sphere_position (2.3, 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setVelocity (vector3d (-1., 0., 0.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0, info.reference_shape);
result = check_collision (1., sphere, polygon, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (1, info.reference_shape);
// here the collision occurs on a vertice
sphere->setPosition (vector3d (1.5, 0., -2.5));
sphere->setVelocity (vector3d (0., 0., 1.));
result = check_collision (1., sphere, polygon, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (1, info.reference_shape);
result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0, info.reference_shape);
delete sphere;
delete polygon;
}
/* Test whether Polygon::getCopy() does what it should */
TEST ( CheckPolygonGetCopy ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Polygon* polygon = new Polygon (4, vertices);
Polygon* polygon_copy = polygon->getCopy();
CHECK (polygon != polygon_copy);
CHECK_EQUAL (polygon->getVerticeCount(), polygon_copy->getVerticeCount());
unsigned int i, count;
count = polygon->getVerticeCount ();
for (i = 0; i < count; i ++) {
vector3d vertice = polygon->getVertice (i);
vector3d vertice_copy = polygon_copy->getVertice (i);
int j;
for (j = 0; j < 3; j++) {
CHECK_EQUAL (vertice[j], vertice_copy[j]);
}
}
delete polygon;
delete polygon_copy;
}
/* Test whether Sphere::getCopy() does what it should */
TEST ( CheckSphereGetCopy ) {
Sphere* sphere = new Sphere (1.23);
Sphere* sphere_copy = sphere->getCopy();
CHECK_EQUAL (sphere->getRadius(), sphere_copy->getRadius());
CHECK (sphere != sphere_copy);
delete sphere;
delete sphere_copy;
}
TEST ( SphereSetGetRadius ) {
Sphere sphere (123.);
CHECK_EQUAL (123, sphere.getRadius());
sphere.setRadius (456.);
CHECK_EQUAL (456, sphere.getRadius());
}

View File

@ -0,0 +1,6 @@
#include <UnitTest++.h>
int main (int argc, char *argv[])
{
return UnitTest::RunAllTests ();
}

View File

@ -0,0 +1,544 @@
#include <UnitTest++.h>
#include <coll2d.h>
using namespace coll2d;
using namespace std;
TEST ( CheckErrorInvalidTypes ) {
Shape* polygon = new Polygon (0, NULL);
vector3d velocity (0., 0., 0.);
vector3d sphere_position (2.1, 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setVelocity (vector3d(0., 0., 0.));
CollisionInfo info;
int result = check_collision_polygon_sphere (1., sphere, sphere, &info);
CHECK (result == CHECK_ERROR_INVALID_TYPES);
result = check_collision_polygon_sphere (1., polygon, polygon, &info);
CHECK (result == CHECK_ERROR_INVALID_TYPES);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereNoVelocity ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., -1.);
vertices[1].setValues (1., 0., -1.);
vertices[2].setValues (1., 0., 1.);
vertices[3].setValues (-1., 0., 1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.1, 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0., 0., 0.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK (result >= 0);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereDiverging ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.1, 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (1., 0., 0.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK (result >= 0);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereContact ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2., 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setVelocity (vector3d (-1., 0., 0.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK (result > 0);
CHECK_EQUAL (0.0, info.time);
CHECK_EQUAL (1., info.normal[0]);
CHECK_EQUAL (0., info.normal[1]);
CHECK_EQUAL (0., info.normal[2]);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereColliding ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.5, 0., 0.1);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (-1., 0., 0.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereCollidingTop ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (0., 0., 2.5);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0., 0., -1.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereCollidingNonPerpendicular ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (0., 0., 2.5);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0.01, 0., -1.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereCollidingCorner) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position ( 1.5, 0., 2.5);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0.0, 0., -1.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (1, result);
delete polygon;
delete sphere;
}
// In this test we make sure, that the z value of both the polygon and the
// sphere get ignored:
TEST ( CheckCollisionPolygonSphereCollidingCornerIgnoreHeight) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position ( 1.5, 10., 2.5);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setVelocity (vector3d (0., 0., -1.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
vertices[0].setValues (-1., -10., 1.);
vertices[1].setValues (1., -10., 1.);
vertices[2].setValues (1., -10., -1.);
vertices[3].setValues (-1., -10., -1.);
sphere->setPosition (vector3d (1.5, 0., 2.5));
result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereCollidingCornerTop) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (1.5, 0., - 2.5);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0., 0., 1.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (1, result);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereCollidingCornerNeighbour) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., -0.99);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (1.5 , 0., - 2.5);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (0., 0., 1.);
CollisionInfo info;
check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK (info.point == vertices[2]);
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereNonCollidingSetPosition ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.5, 0., 0.1);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setPosition (vector3d (35, 0, 0));
vector3d velocity (-1., 0., 0.);
CollisionInfo info;
int result = check_collision_rel (1., polygon, sphere, &velocity, &info);
CHECK_EQUAL (0, result);
delete polygon;
delete sphere;
}
// In this test we set the velocity of the polygon instead of the
// sphere.
TEST ( CheckCollisionPolygonSphereCollidingPolygonSetVelocity ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.5, 0., 0.);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (1., 0., 0.);
polygon->setVelocity (velocity);
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
delete polygon;
delete sphere;
}
TEST ( CheckSetGetAngle ) {
Shape* polygon = new Polygon (0, NULL);
polygon->setAngle (0.1234567);
CHECK (fabs (0.1234567 - polygon->getAngle ()) < 1.0e-7);
delete polygon;
}
TEST ( CheckCollisionPolygonSphereDiamond ) {
vector3d vertices[4];
float sqrt2 = sqrt (2);
vertices[0].setValues (-sqrt2, 0., 0.);
vertices[1].setValues (0., 0., sqrt2);
vertices[2].setValues (sqrt2, 0., 0.);
vertices[3].setValues (0., 0., -sqrt2);
Shape* polygon = new Polygon (4, vertices);
Shape* sphere = new Sphere (1., vector3d (sqrt2 + 1.5, 0., 0.));
sphere->setVelocity (vector3d (-1., 0., 0.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK (fabs (0.5 - info.time) <= 1.0e-6);
CHECK_EQUAL (0, info.reference_shape);
delete polygon;
delete sphere;
}
TEST ( CheckPolygonSpherePassingBy ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (1., 0., 2.5);
Shape* sphere = new Sphere (1, sphere_position);
sphere->setVelocity (vector3d (-2., 0., 0.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (0, result);
delete sphere;
delete polygon;
}
TEST ( CheckCollisionPolygonSphereReturnValuePoint ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
polygon->setPosition (vector3d (sqrt (2), 0., - sqrt (2)));
polygon->setAngle (M_PI * 0.25);
Shape* sphere = new Sphere (1, vector3d (-sqrt(2) * 0.5, 0., sqrt (2) * 0.5));
sphere->setVelocity (vector3d (sqrt (2), 0., -sqrt(2)));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK ( fabs (0.5 - info.time) < 1.0e-6);
CHECK ( (info.point - vector3d (sqrt (2) * 0.5, 0., -sqrt(2) * 0.5)).length() < 1.0e-6 );
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereReturnValuePointTranslated ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
polygon->setPosition (vector3d (sqrt (2) + 1., 0., - sqrt (2) - 1.));
polygon->setAngle (M_PI * 0.25);
Shape* sphere = new Sphere (1, vector3d (-sqrt(2) * 0.5 + 1., 0., sqrt (2) * 0.5 -1.));
sphere->setVelocity (vector3d (sqrt (2), 0., -sqrt(2)));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK ( fabs (0.5 - info.time) < 1.0e-6);
CHECK ( (info.point - vector3d (sqrt (2) * 0.5 + 1, 0., -sqrt(2) * 0.5 - 1.)).length() < 1.0e-6 );
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereReturnValuePointVerticeTranslated ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
polygon->setPosition (vector3d (0. + 2., 0., - sqrt (2) - 1.5));
polygon->setAngle (M_PI * 0.25);
Shape* sphere = new Sphere (1, vector3d (0. + 2., 0., 0.));
sphere->setVelocity (vector3d (0. , 0., -1.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK ( fabs (0.5 - info.time) < 1.0e-6);
CHECK ( (info.point - vector3d (0. + 2., 0., -1.5)).length() < 1.0e-6 );
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereReferenceShapePolygon ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
polygon->setPosition (vector3d (0., 0., 0.));
polygon->setVelocity (vector3d (1., 0., 0.));
Shape* sphere = new Sphere (1, vector3d (2.5, 0., 0.));
sphere->setVelocity (vector3d (0., 0., 0.));
CollisionInfo info;
int result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK ( fabs (0.5 - info.time) < 1.0e-6);
CHECK_EQUAL ( 0, info.reference_shape );
polygon->setPosition (vector3d (0., 0., 0.));
polygon->setVelocity (vector3d (1., 0., 0.));
sphere->setPosition (vector3d (2.5, 0., 0.));
sphere->setVelocity (vector3d (0., 0., 0.));
result = check_collision (1., sphere, polygon, &info);
CHECK_EQUAL (1, result);
CHECK ( fabs (0.5 - info.time) < 1.0e-6);
CHECK_EQUAL ( 1, info.reference_shape );
delete polygon;
delete sphere;
}
TEST ( CheckCollisionPolygonSphereTimestep ) {
vector3d vertices[4];
vertices[0].setValues (-1., 0., 1.);
vertices[1].setValues (1., 0., 1.);
vertices[2].setValues (1., 0., -1.);
vertices[3].setValues (-1., 0., -1.);
Shape* polygon = new Polygon (4, vertices);
vector3d sphere_position (2.5, 0., 0.1);
Shape* sphere = new Sphere (1, sphere_position);
vector3d velocity (-1., 0., 0.);
sphere->setVelocity (velocity);
CollisionInfo info;
int result;
result = check_collision (1., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
result = check_collision (2., polygon, sphere, &info);
CHECK_EQUAL (1, result);
CHECK_EQUAL (0.5, info.time);
result = check_collision (0.499, polygon, sphere, &info);
CHECK_EQUAL (0, result);
delete polygon;
delete sphere;
}

Some files were not shown because too many files have changed in this diff Show More