initial commit
commit
e7e0b39e82
|
@ -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
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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
|
||||
)
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
#include "AsteroidsEnums.h"
|
||||
|
||||
#define GENERATE_ENUM_STRINGS
|
||||
|
||||
#include "AsteroidsEnums.h"
|
||||
|
||||
#undef GENERATE_ENUM_STRINGS
|
|
@ -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 ...");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 */
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
Indent: Tabs!
|
||||
|
||||
class Uppercase {
|
||||
int OnInit ()
|
||||
void OnDestroy ();
|
||||
|
||||
void SetValue ();
|
||||
int GetValue ();
|
||||
|
||||
void SomeCoolFunction ();
|
||||
};
|
||||
|
Binary file not shown.
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
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 |
|
@ -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
|
||||
)
|
|
@ -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 )
|
||||
|
|
@ -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]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 ();
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -0,0 +1,7 @@
|
|||
#include "EntityBase.h"
|
||||
#include "ModelBase.h"
|
||||
|
||||
namespace Engine {
|
||||
|
||||
}
|
||||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 */
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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!)
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
ADD_SUBDIRECTORY ( mathlib )
|
||||
ADD_SUBDIRECTORY ( coll2d )
|
||||
ADD_SUBDIRECTORY ( oglft )
|
|
@ -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
|
||||
)
|
|
@ -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} )
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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 )
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
#include <UnitTest++.h>
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
return UnitTest::RunAllTests ();
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue