]> git.localhorst.tv Git - sdl-test8.git/commitdiff
added collision engine, more or less stole gameplay from sdl-test7
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 7 May 2012 20:38:46 +0000 (20:38 +0000)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 7 May 2012 20:38:46 +0000 (20:38 +0000)
33 files changed:
data/font/Magra-Regular.ttf [new file with mode: 0644]
src/app/Application.cpp [new file with mode: 0644]
src/app/Application.h [new file with mode: 0644]
src/app/Control.h [new file with mode: 0644]
src/app/State.h [new file with mode: 0644]
src/app/Timer.cpp [new file with mode: 0644]
src/app/Timer.h [new file with mode: 0644]
src/game/Collision.h [new file with mode: 0644]
src/game/Entity.cpp [new file with mode: 0644]
src/game/Entity.h [new file with mode: 0644]
src/geometry/Ray2D.h [new file with mode: 0644]
src/geometry/Vector2D.h [new file with mode: 0644]
src/geometry/constants.h [new file with mode: 0644]
src/pong/Ball.cpp [new file with mode: 0644]
src/pong/Ball.h [new file with mode: 0644]
src/pong/CountingWall.cpp [new file with mode: 0644]
src/pong/CountingWall.h [new file with mode: 0644]
src/pong/Match.cpp [new file with mode: 0644]
src/pong/Match.h [new file with mode: 0644]
src/pong/Paddle.cpp [new file with mode: 0644]
src/pong/Paddle.h [new file with mode: 0644]
src/sdl-test8.cpp [new file with mode: 0644]
src/sdl/InitSDL.cpp [new file with mode: 0644]
src/sdl/InitSDL.h [new file with mode: 0644]
src/sdl/InitScreen.cpp [new file with mode: 0644]
src/sdl/InitScreen.h [new file with mode: 0644]
src/sdl/InitTTF.cpp [new file with mode: 0644]
src/sdl/InitTTF.h [new file with mode: 0644]
src/shape/AABB.cpp [new file with mode: 0644]
src/shape/AABB.h [new file with mode: 0644]
src/shape/Circle.cpp [new file with mode: 0644]
src/shape/Circle.h [new file with mode: 0644]
src/shape/Shape.h [new file with mode: 0644]

diff --git a/data/font/Magra-Regular.ttf b/data/font/Magra-Regular.ttf
new file mode 100644 (file)
index 0000000..6e252b0
Binary files /dev/null and b/data/font/Magra-Regular.ttf differ
diff --git a/src/app/Application.cpp b/src/app/Application.cpp
new file mode 100644 (file)
index 0000000..bcf4f29
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Application.cpp
+ *
+ *  Created on: Apr 8, 2012
+ *      Author: holy
+ */
+
+#include "Application.h"
+
+#include "State.h"
+
+#include <cassert>
+
+namespace app {
+
+Application::Application(sdl::InitScreen *screen, State *initialState)
+: screen(screen)
+, states()
+, timer()
+, last(SDL_GetTicks()) {
+       assert(screen && "cannot create application without screen");
+       assert(initialState && "cannot create application without initial state");
+       RealPushState(initialState);
+}
+
+Application::~Application(void) {
+       PopAllStates();
+}
+
+
+State *Application::CurrentState(void) {
+       return states.top();
+}
+
+void Application::ChangeState(State *s) {
+       RealPopState();
+       RealPushState(s);
+}
+
+void Application::PushState(State *s) {
+       RealPushState(s);
+}
+
+void Application::RealPushState(State *s) {
+       states.push(s);
+       s->EnterState(this, screen->Screen());
+}
+
+void Application::PopState(void) {
+       RealPopState();
+}
+
+void Application::RealPopState(void) {
+       if (states.empty()) return;
+       states.top()->ExitState();
+       delete states.top();
+       states.pop();
+}
+
+void Application::Quit(void) {
+       PopAllStates();
+}
+
+void Application::PopAllStates(void) {
+       while (!states.empty()) {
+               RealPopState();
+       }
+}
+
+
+void Application::Run(void) {
+       while (CurrentState()) {
+               Loop();
+       }
+}
+
+void Application::Loop(void) {
+       Uint32 now(SDL_GetTicks());
+       Uint32 deltaT(now - last);
+       if (deltaT > 34) deltaT = 34;
+
+       HandleEvents();
+       UpdateWorld(deltaT);
+       Render();
+
+       last = now;
+}
+
+
+void Application::HandleEvents(void) {
+       if (!CurrentState()) return;
+       SDL_Event event;
+       while (SDL_PollEvent(&event)) {
+               switch (event.type) {
+                       case SDL_QUIT:
+                               PopAllStates();
+                               break;
+                       default:
+                               CurrentState()->HandleEvent(event);
+                               break;
+               }
+       }
+}
+
+void Application::UpdateWorld(Uint32 deltaT) {
+       if (!CurrentState()) return;
+       for (Uint32 i(0); i < deltaT; ++i) {
+               timer.Update(0.001);
+               CurrentState()->UpdateWorld(timer);
+       }
+}
+
+void Application::Render(void) {
+       if (!CurrentState()) return;
+       CurrentState()->Render(screen->Screen());
+       screen->Flip();
+}
+
+}
diff --git a/src/app/Application.h b/src/app/Application.h
new file mode 100644 (file)
index 0000000..73e6400
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Application.h
+ *
+ *  Created on: Apr 8, 2012
+ *      Author: holy
+ */
+
+#ifndef APP_APPLICATION_H_
+#define APP_APPLICATION_H_
+
+#include "Control.h"
+#include "Timer.h"
+#include "../sdl/InitScreen.h"
+
+#include <stack>
+#include <SDL/SDL.h>
+
+
+namespace app {
+
+class State;
+
+class Application
+: public Control {
+
+       public:
+               explicit Application(sdl::InitScreen *screen, State *initialState);
+               virtual ~Application(void);
+       private:
+               Application(const Application &);
+               Application &operator =(const Application &);
+
+       public:
+               void Run(void);
+               void Loop(void);
+
+       public:
+               virtual void ChangeState(State *);
+               virtual void PushState(State *);
+               virtual void PopState(void);
+               virtual void Quit(void);
+
+       private:
+               State *CurrentState(void);
+               void RealPushState(State *);
+               void RealPopState(void);
+               void PopAllStates(void);
+
+       private:
+               void HandleEvents(void);
+               void UpdateWorld(Uint32 deltaT);
+               void Render(void);
+
+       private:
+               sdl::InitScreen *screen;
+               std::stack<State *> states;
+               Timer timer;
+               Uint32 last;
+
+};
+
+}
+
+#endif /* APP_APPLICATION_H_ */
diff --git a/src/app/Control.h b/src/app/Control.h
new file mode 100644 (file)
index 0000000..b6cd742
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Control.h
+ *
+ *  Created on: Apr 19, 2012
+ *      Author: holy
+ */
+
+#ifndef APP_CONTROL_H_
+#define APP_CONTROL_H_
+
+namespace app {
+
+class State;
+
+class Control {
+
+       public:
+               virtual ~Control(void) { };
+
+       public:
+               virtual void ChangeState(State *) = 0;
+               virtual void PushState(State *) = 0;
+               virtual void PopState(void) = 0;
+               virtual void Quit(void) = 0;
+};
+
+}
+
+#endif /* APP_CONTROL_H_ */
diff --git a/src/app/State.h b/src/app/State.h
new file mode 100644 (file)
index 0000000..548fc94
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * State.h
+ *
+ *  Created on: Apr 8, 2012
+ *      Author: holy
+ */
+
+#ifndef APP_APPLICATIONSTATE_H_
+#define APP_APPLICATIONSTATE_H_
+
+#include <SDL/SDL.h>
+
+namespace app {
+
+class Control;
+class Timer;
+
+class State {
+
+       public:
+               virtual ~State(void) { };
+
+       public:
+               virtual void EnterState(Control *ctrl, SDL_Surface *screen) = 0;
+               virtual void ExitState(void) = 0;
+
+               virtual void HandleEvent(const SDL_Event &) = 0;
+               virtual void UpdateWorld(const Timer &) = 0;
+               virtual void Render(SDL_Surface *) = 0;
+
+};
+
+}
+
+#endif /* APP_STATE_H_ */
diff --git a/src/app/Timer.cpp b/src/app/Timer.cpp
new file mode 100644 (file)
index 0000000..48a5eba
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Timer.cpp
+ *
+ *  Created on: Apr 25, 2012
+ *      Author: holy
+ */
+
+#include "Timer.h"
+
+namespace app {
+
+void Timer::Update(double dt) {
+       delta = dt * scale;
+       elapsed += dt;
+}
+
+double Timer::Elapsed(void) const {
+       return elapsed * delta;
+}
+
+} /* namespace app */
diff --git a/src/app/Timer.h b/src/app/Timer.h
new file mode 100644 (file)
index 0000000..997d92c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Timer.h
+ *
+ *  Created on: Apr 25, 2012
+ *      Author: holy
+ */
+
+#ifndef APP_TIMER_H_
+#define APP_TIMER_H_
+
+namespace app {
+
+class Timer {
+
+       public:
+               explicit Timer(double scale = 1.0, double initialTime = 0.0)
+                               : scale(scale), elapsed(initialTime), delta(0.0) { };
+
+       public:
+               void Update(double dt);
+
+       public:
+               double DeltaT(void) const { return delta; };
+               double Elapsed(void) const;
+
+       private:
+               double scale, elapsed, delta;
+
+};
+
+}
+
+#endif /* APP_TIMER_H_ */
diff --git a/src/game/Collision.h b/src/game/Collision.h
new file mode 100644 (file)
index 0000000..e2ecb01
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Collision.h
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#ifndef GAME_COLLISION_H_
+#define GAME_COLLISION_H_
+
+#include "Entity.h"
+
+
+namespace game {
+
+class Collision {
+
+       public:
+               Collision(Entity *left, Entity *right, const Entity::Ray &normal) : left(left), right(right), normal(normal) { };
+               ~Collision(void) { };
+
+       public:
+               Entity *left, *right;
+               Entity::Ray normal;
+
+};
+
+}
+
+#endif /* GAME_COLLISION_H_ */
diff --git a/src/game/Entity.cpp b/src/game/Entity.cpp
new file mode 100644 (file)
index 0000000..1c3f785
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Entity.cpp
+ *
+ *  Created on: Apr 25, 2012
+ *      Author: holy
+ */
+
+#include "Entity.h"
+
+#include "../app/Timer.h"
+#include "../geometry/constants.h"
+
+#include <limits>
+
+using geometry::PI;
+using std::numeric_limits;
+
+namespace game {
+
+
+void Entity::Update(const app::Timer &timer) {
+       translation += linearVelocity * timer.DeltaT();
+       rotation += angularVelocity * timer.DeltaT();
+       if (rotation > 2.0 * PI) {
+               rotation -= 2.0 * PI;
+       } else if (rotation < -(2.0 * PI)) {
+               rotation += 2.0 * PI;
+       }
+       shape->Translate(linearVelocity * timer.DeltaT());
+       shape->Rotate(angularVelocity * timer.DeltaT());
+}
+
+bool Entity::CheckCollision(const Entity &other, Ray &ray) {
+       return shape->CheckCollision(*other.shape, ray);
+}
+
+
+Entity::Vector Entity::VelocityAt(const Vector &p) const {
+       if (angularVelocity == 0 || p == translation) return linearVelocity;
+
+       Vector v(linearVelocity);
+       Vector pRelative(p - translation);
+       v += pRelative.Rotate90() * angularVelocity;
+       return v;
+}
+
+
+void Entity::Rotate(Scalar d) {
+       rotation += d;
+       shape->Rotate(d);
+}
+
+void Entity::Translate(const Vector &d) {
+       translation += d;
+       shape->Translate(d);
+}
+
+void Entity::Accelerate(const Vector &dvl, Scalar dva) {
+       linearVelocity += dvl;
+       angularVelocity += dva;
+}
+
+
+void Entity::CollisionResponse(Entity &a, const Ray &na, Entity &b) {
+       if (a.mass == numeric_limits<Scalar>::infinity()
+                       && b.mass == numeric_limits<Scalar>::infinity()) {
+               // special case: collision of two infinitely heavy entities
+               InfInfCollisionResponse(a, na, b);
+               return;
+       }
+
+       const Scalar e(1);
+
+       const Vector va(a.LinearVelocity());
+       const Vector vb(b.LinearVelocity());
+       const Scalar wa(a.AngularVelocity());
+       const Scalar wb(b.AngularVelocity());
+
+       const Vector vap(a.VelocityAt(na.Origin()));
+       const Vector vbp(b.VelocityAt(na.Origin()));
+       const Vector vab = vap - vbp;
+
+       const Vector rap90((na.Origin() - a.Origin()).Rotate90());
+       const Vector rbp90((na.Origin() - b.Origin()).Rotate90());
+
+       const Scalar ia(a.Mass());
+       const Scalar ib(b.Mass());
+
+       const Scalar rap90dotN(rap90.Dot(na.Direction()));
+       const Scalar rbp90dotN(rbp90.Dot(na.Direction()));
+
+       const Scalar j(
+                       (((-(1 + e)) * vab).Dot(na.Direction()))
+                       / (na.Direction().Dot(na.Direction() * ((1.0 / a.Mass()) + (1.0 / b.Mass())))
+                                       + ((rap90dotN * rap90dotN) / ia)
+                                       + ((rbp90dotN * rbp90dotN) / ib) ));
+
+       const Vector va2(va + ((j / a.Mass()) * na.Direction()));
+       const Vector vb2(vb - ((j / b.Mass()) * na.Direction()));
+       const Scalar wa2(wa + ((rap90.Dot(j * na.Direction())) / ia));
+       const Scalar wb2(wb - ((rbp90.Dot(j * na.Direction())) / ib));
+
+       a.linearVelocity = va2;
+       a.angularVelocity = wa2;
+       b.linearVelocity = vb2;
+       b.angularVelocity = wb2;
+}
+
+void Entity::InfInfCollisionResponse(Entity &a, const Ray &na, Entity &b) {
+       // If both elements are standing still or both are moving, move them away
+       // from each other. Otherwise, move the moving one.
+       if ((a.linearVelocity == Vector() && b.linearVelocity == Vector())
+                       || (a.linearVelocity != Vector() && b.linearVelocity != Vector())) {
+               a.Translate(na.Direction());
+               b.Translate(-na.Direction());
+               Ray n;
+               while (a.shape->CheckCollision(*b.shape, n)) {
+                       a.Translate(n.Direction());
+                       b.Translate(-n.Direction());
+               }
+       } else if (a.linearVelocity == Vector()) {
+               b.Translate(-na.Direction());
+               Ray n;
+               while (b.shape->CheckCollision(*a.shape, n)) {
+                       b.Translate(n.Direction());
+               }
+       } else {
+               a.Translate(na.Direction());
+               Ray n;
+               while (a.shape->CheckCollision(*b.shape, n)) {
+                       a.Translate(n.Direction());
+               }
+       }
+}
+
+}
diff --git a/src/game/Entity.h b/src/game/Entity.h
new file mode 100644 (file)
index 0000000..22d46bd
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Entity.h
+ *
+ *  Created on: Apr 25, 2012
+ *      Author: holy
+ */
+
+#ifndef GAME_ENTITY_H_
+#define GAME_ENTITY_H_
+
+#include "../shape/Shape.h"
+
+#include <SDL/SDL.h>
+
+namespace app { class Timer; }
+
+
+namespace game {
+
+class Entity {
+
+       friend std::ostream &operator <<(std::ostream &, const Entity &);
+
+       public:
+               typedef shape::Shape::Scalar Scalar;
+               typedef shape::Shape::Vector Vector;
+               typedef shape::Shape::Ray Ray;
+
+       public:
+               static void CollisionResponse(Entity &a, const Entity::Ray &na, Entity &b);
+
+       public:
+               explicit Entity(shape::Shape *s, Scalar mass = 1.0) : shape(s), mass(mass) { };
+               virtual ~Entity(void) { };
+
+       public:
+               void Update(const app::Timer &);
+               bool CheckCollision(const Entity &, Ray &);
+
+       public:
+               virtual void Collide(Entity &other, const Ray &) { };
+               virtual void Render(SDL_Surface *dest) const { };
+
+       public:
+               const Vector &Origin(void) const { return translation; };
+               const Vector &LinearVelocity(void) const { return linearVelocity; };
+               Scalar Angle(void) const { return rotation; };
+               Scalar AngularVelocity(void) const { return angularVelocity; };
+               Vector VelocityAt(const Vector &p) const;
+               const Scalar Mass(void) const { return mass; };
+
+       public:
+               void Rotate(Scalar);
+               void Translate(const Vector &);
+               void Accelerate(const Vector &linear, Scalar angular = 0);
+
+       private:
+               static void InfInfCollisionResponse(Entity &a, const Entity::Ray &na, Entity &b);
+
+       private:
+               shape::Shape *shape;
+               Vector translation, linearVelocity;
+               Scalar rotation, angularVelocity, mass;
+
+};
+
+
+inline std::ostream &operator <<(std::ostream &out, const Entity &e) {
+       return out << *e.shape;
+}
+
+}
+
+#endif /* GAME_ENTITY_H_ */
diff --git a/src/geometry/Ray2D.h b/src/geometry/Ray2D.h
new file mode 100644 (file)
index 0000000..408a061
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Ray2D.h
+ *
+ *  Created on: Apr 25, 2012
+ *      Author: holy
+ */
+
+#ifndef GEOMETRY_RAY2D_H_
+#define GEOMETRY_RAY2D_H_
+
+#include "Vector2D.h"
+
+namespace geometry {
+
+template<typename Scalar>
+class Ray2D {
+
+       public:
+               explicit Ray2D(const Vector2D<Scalar> &directionUnit = Vector2D<Scalar>(), const Vector2D<Scalar> &origin = Vector2D<Scalar>())
+                               : direction(directionUnit), origin(origin) { };
+
+       public:
+               Vector2D<Scalar> &Direction(void) { return direction; };
+               Vector2D<Scalar> &Origin(void) { return origin; };
+               const Vector2D<Scalar> &Direction(void) const { return direction; };
+               const Vector2D<Scalar> &Origin(void) const { return origin; };
+
+       private:
+               Vector2D<Scalar> direction, origin;
+
+};
+
+}
+
+#endif /* GEOMETRY_RAY2D_H_ */
diff --git a/src/geometry/Vector2D.h b/src/geometry/Vector2D.h
new file mode 100644 (file)
index 0000000..1134b38
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Vector.h
+ *
+ *  Created on: Apr 23, 2012
+ *      Author: holy
+ */
+
+#ifndef GEOMETRY_VECTOR2D_H_
+#define GEOMETRY_VECTOR2D_H_
+
+#include <cmath>
+#include <ostream>
+
+
+namespace geometry {
+
+template<typename Scalar>
+class Vector2D {
+
+       public:
+               Vector2D(void) : x(), y() { };
+               Vector2D(Scalar x, Scalar y) : x(x), y(y) { };
+
+       public:
+               Scalar X(void) const { return x; };
+               Scalar Y(void) const { return y; };
+
+       public:
+               Scalar Dot(const Vector2D<Scalar> &o) const { return (X() * o.X()) + (Y() * o.Y()); };
+               Scalar LengthSquared(void) const { return Dot(*this); };
+               Scalar Length(void) const { return std::sqrt(LengthSquared()); };
+
+       public:
+               Vector2D<Scalar> &Unify(void) {
+                       if (LengthSquared() == Scalar()) return *this;
+                       *this /= Length();
+                       return *this;
+               };
+               Vector2D<Scalar> Unit(void) const {
+                       if (LengthSquared() == Scalar()) return *this;
+                       return *this / Length();
+               };
+               Vector2D<Scalar> &Reflect(const Vector2D<Scalar> &normal);
+               Vector2D<Scalar> &Rotate90(void) {
+                       return *this = Vector2D<Scalar>(-Y(), X());
+               };
+               Vector2D<Scalar> &Rotate(Scalar by) {
+                       Scalar sine(std::sin(by)), cosine(std::cos(by));
+                       return *this = Vector2D<Scalar>(X() * cosine - Y() * sine, X() * sine + y * cosine);
+               };
+
+       public:
+               Vector2D<Scalar> &operator +=(const Vector2D<Scalar> &o) {
+                       x += o.X();
+                       y += o.Y();
+                       return *this;
+               };
+               Vector2D<Scalar> &operator -=(const Vector2D<Scalar> &o) {
+                       x -= o.X();
+                       y -= o.Y();
+                       return *this;
+               };
+               template<typename T>
+               Vector2D<Scalar> &operator +=(T s) { x += s; y += s; return *this; };
+               template<typename T>
+               Vector2D<Scalar> &operator -=(T s) { x -= s; y -= s; return *this; };
+               template<typename T>
+               Vector2D<Scalar> &operator *=(T s) { x *= s; y *= s; return *this; };
+               template<typename T>
+               Vector2D<Scalar> &operator /=(T s) { x /= s; y /= s; return *this; };
+
+       private:
+               Scalar x, y;
+
+};
+
+template<typename Scalar>
+inline Vector2D<Scalar> &Vector2D<Scalar>::Reflect(const Vector2D<Scalar> &normal) {
+       Scalar doubleDot(2 * Dot(normal));
+       x -= doubleDot * normal.X();
+       y -= doubleDot * normal.Y();
+       return *this;
+};
+
+
+template<typename Scalar>
+inline bool operator ==(const Vector2D<Scalar> &lhs, const Vector2D<Scalar> &rhs) {
+       return lhs.X() == rhs.X() && lhs.Y() == rhs.Y();
+};
+
+template<typename Scalar>
+inline bool operator !=(const Vector2D<Scalar> &lhs, const Vector2D<Scalar> &rhs) {
+       return !(lhs == rhs);
+};
+
+template<typename Scalar>
+inline Vector2D<Scalar> operator +(const Vector2D<Scalar> &v) {
+       return v;
+};
+template<typename Scalar>
+inline Vector2D<Scalar> operator -(const Vector2D<Scalar> &v) {
+       return Vector2D<Scalar>(-v.X(), -v.Y());
+};
+
+template<typename Scalar>
+inline Vector2D<Scalar> operator +(const Vector2D<Scalar> &lhs, const Vector2D<Scalar> &rhs) {
+       Vector2D<Scalar> temp(lhs);
+       temp += rhs;
+       return temp;
+};
+template<typename Scalar>
+inline Vector2D<Scalar> operator -(const Vector2D<Scalar> &lhs, const Vector2D<Scalar> &rhs) {
+       Vector2D<Scalar> temp(lhs);
+       temp -= rhs;
+       return temp;
+};
+
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator +(const Vector2D<VScalar> &v, SScalar s) {
+       Vector2D<VScalar> temp(v);
+       temp += s;
+       return temp;
+};
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator -(const Vector2D<VScalar> &v, SScalar s) {
+       Vector2D<VScalar> temp(v);
+       temp -= s;
+       return temp;
+};
+
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator +(SScalar s, const Vector2D<VScalar> &v) {
+       Vector2D<VScalar> temp(v);
+       temp += s;
+       return temp;
+};
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator -(SScalar s, const Vector2D<VScalar> &v) {
+       Vector2D<VScalar> temp(v);
+       temp -= s;
+       return temp;
+};
+
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator *(const Vector2D<VScalar> &v, SScalar s) {
+       Vector2D<VScalar> temp(v);
+       temp *= s;
+       return temp;
+};
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator *(SScalar s, const Vector2D<VScalar> &v) {
+       Vector2D<VScalar> temp(v);
+       temp *= s;
+       return temp;
+};
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator /(const Vector2D<VScalar> &v, SScalar s) {
+       Vector2D<VScalar> temp(v);
+       temp /= s;
+       return temp;
+};
+template<typename VScalar, typename SScalar>
+inline Vector2D<VScalar> operator /(SScalar s, const Vector2D<VScalar> &v) {
+       Vector2D<VScalar> temp(v);
+       temp /= s;
+       return temp;
+};
+
+
+template<typename Scalar>
+std::ostream &operator <<(std::ostream &out, const Vector2D<Scalar> &v) {
+       return out << '<' << v.X() << '|' << v.Y() << '>';
+}
+
+}
+
+#endif /* GEOMETRY_VECTOR2D_H_ */
diff --git a/src/geometry/constants.h b/src/geometry/constants.h
new file mode 100644 (file)
index 0000000..a33ea2a
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * constants.h
+ *
+ *  Created on: Apr 26, 2012
+ *      Author: holy
+ */
+
+#ifndef GEOMETRY_CONSTANTS_H_
+#define GEOMETRY_CONSTANTS_H_
+
+#include <cmath>
+
+
+namespace geometry {
+
+const double PI(std::atan(1.0) * 4.0);
+
+}
+
+
+#endif /* GEOMETRY_CONSTANTS_H_ */
diff --git a/src/pong/Ball.cpp b/src/pong/Ball.cpp
new file mode 100644 (file)
index 0000000..97248f3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Ball.cpp
+ *
+ *  Created on: Apr 9, 2012
+ *      Author: holy
+ */
+
+#include "Ball.h"
+
+#include "Paddle.h"
+#include "../geometry/constants.h"
+
+#include <cmath>
+#include <SDL/SDL_gfxPrimitives.h>
+
+using game::Entity;
+using geometry::PI;
+
+
+namespace pong {
+
+Ball::Ball(Scalar r)
+: Entity(&shape, 4 * PI * r * r * r / 3)
+, shape(r) {
+
+}
+
+
+void Ball::Render(SDL_Surface *screen) const {
+       circleRGBA(screen, shape.Center().X(), shape.Center().Y(), shape.Radius(), 0xFF, 0xFF, 0xFF, 0xFF);
+       Vector angle(Vector(0, 1).Rotate(Angle()) * shape.Radius() + Origin());
+       lineRGBA(screen, shape.Center().X(), shape.Center().Y(), angle.X(), angle.Y(), 0xFF, 0xFF, 0xFF, 0xFF);
+}
+
+}
diff --git a/src/pong/Ball.h b/src/pong/Ball.h
new file mode 100644 (file)
index 0000000..2e54a8c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Ball.h
+ *
+ *  Created on: Apr 9, 2012
+ *      Author: holy
+ */
+
+#ifndef PONG_BALL_H_
+#define PONG_BALL_H_
+
+#include "../game/Entity.h"
+#include "../shape/Circle.h"
+
+
+namespace pong {
+
+class Ball
+: public game::Entity {
+
+       public:
+               explicit Ball(Scalar radius);
+               virtual ~Ball(void) { };
+
+       public:
+               virtual void Render(SDL_Surface *dest) const;
+
+       private:
+               shape::Circle shape;
+
+};
+
+}
+
+#endif /* PONG_BALL_H_ */
diff --git a/src/pong/CountingWall.cpp b/src/pong/CountingWall.cpp
new file mode 100644 (file)
index 0000000..4b044a7
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * CountingWall.cpp
+ *
+ *  Created on: Apr 17, 2012
+ *      Author: holy
+ */
+
+#include "CountingWall.h"
+
+#include <limits>
+
+using game::Entity;
+
+
+namespace pong {
+
+CountingWall::CountingWall(Scalar width, Scalar height)
+: Entity(&shape, std::numeric_limits<Scalar>::infinity())
+, shape(width, height)
+, count(0) {
+
+}
+
+CountingWall::~CountingWall(void) {
+
+}
+
+
+void CountingWall::Collide(Entity &, const Ray &) {
+       ++count;
+}
+
+void CountingWall::Render(SDL_Surface *dest) const {
+
+}
+
+}
diff --git a/src/pong/CountingWall.h b/src/pong/CountingWall.h
new file mode 100644 (file)
index 0000000..c14820d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * CountingWall.h
+ *
+ *  Created on: Apr 17, 2012
+ *      Author: holy
+ */
+
+#ifndef PONG_COUNTINGWALL_H_
+#define PONG_COUNTINGWALL_H_
+
+#include "../game/Entity.h"
+#include "../shape/AABB.h"
+
+#include <SDL/SDL.h>
+
+namespace pong {
+
+class CountingWall
+: public game::Entity {
+
+       public:
+               CountingWall(Scalar width, Scalar height);
+               virtual ~CountingWall(void);
+
+       public:
+               int HitCount(void) { return count; };
+
+       public:
+               virtual void Collide(Entity &other, const Ray &);
+               virtual void Render(SDL_Surface *dest) const;
+
+       private:
+               shape::AABB shape;
+               int count;
+
+};
+
+}
+
+#endif /* PONG_COUNTINGWALL_H_ */
diff --git a/src/pong/Match.cpp b/src/pong/Match.cpp
new file mode 100644 (file)
index 0000000..5754b36
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Match.cpp
+ *
+ *  Created on: Apr 9, 2012
+ *      Author: holy
+ */
+
+#include "Match.h"
+
+#include "../app/Control.h"
+
+#include <sstream>
+#include <stdexcept>
+#include <utility>
+#include <SDL/SDL.h>
+
+using app::Control;
+using app::Timer;
+using game::Collision;
+using game::Entity;
+using std::runtime_error;
+using std::stringstream;
+using std::vector;
+
+
+namespace pong {
+
+Match::Match(void)
+: ctrl(0)
+, scoreFont(TTF_OpenFont("data/font/Magra-Regular.ttf", 30))
+, leftScoreText(0)
+, rightScoreText(0)
+, paddleSpeed(150)
+, worldWidth(800)
+, worldHeight(480)
+, ball(10)
+, secondBall(7)
+, thirdBall(5)
+, leftPaddle(10, 100)
+, rightPaddle(10, 100)
+, topWall(800, 10)
+, bottomWall(800, 10)
+, leftWall(10, 480)
+, rightWall(10, 480)
+, entities()
+, collisions()
+, updateScore(true) {
+
+       if (!scoreFont) {
+               throw runtime_error("failed to load score font");
+       }
+
+       ball.Translate(Entity::Vector(400, 240));
+       ball.Accelerate(Entity::Vector(180, 180));
+//     ball.SetMaxVelocity(500.0f);
+
+       secondBall.Translate(Entity::Vector(300, 240));
+       secondBall.Accelerate(Entity::Vector(-50, -50), 1);
+//     secondBall.SetMaxVelocity(600.0f);
+
+       thirdBall.Translate(Entity::Vector(200, 440));
+       thirdBall.Accelerate(Entity::Vector(60, 40));
+
+       leftPaddle.Translate(Entity::Vector(5, 200));
+       rightPaddle.Translate(Entity::Vector(795, 280));
+
+       leftPaddle.SetMovementSpeed(Entity::Vector(0, paddleSpeed));
+       rightPaddle.SetMovementSpeed(Entity::Vector(0, paddleSpeed));
+
+       topWall.Translate(Entity::Vector(400, -5));
+       rightWall.Translate(Entity::Vector(805, 240));
+       bottomWall.Translate(Entity::Vector(400, 485));
+       leftWall.Translate(Entity::Vector(-5, 240));
+
+       entities.reserve(9);
+       entities.push_back(&ball);
+       entities.push_back(&secondBall);
+       entities.push_back(&thirdBall);
+       entities.push_back(&leftPaddle);
+       entities.push_back(&rightPaddle);
+       entities.push_back(&topWall);
+       entities.push_back(&bottomWall);
+       entities.push_back(&leftWall);
+       entities.push_back(&rightWall);
+
+       collisions.reserve(2 * entities.size());
+}
+
+Match::~Match(void) {
+
+}
+
+
+void Match::EnterState(Control *c, SDL_Surface *screen) {
+       ctrl = c;
+}
+
+void Match::ExitState(void) {
+       if (scoreFont) {
+               TTF_CloseFont(scoreFont);
+               scoreFont = 0;
+       }
+}
+
+void Match::HandleEvent(const SDL_Event &e) {
+       if (e.type == SDL_KEYDOWN) {
+               if (e.key.keysym.sym == SDLK_w) {
+                       leftPaddle.StartMovingUp();
+               } else if (e.key.keysym.sym == SDLK_s) {
+                       leftPaddle.StartMovingDown();
+               } else if (e.key.keysym.sym == SDLK_UP) {
+                       rightPaddle.StartMovingUp();
+               } else if (e.key.keysym.sym == SDLK_DOWN) {
+                       rightPaddle.StartMovingDown();
+               }
+       } else if (e.type == SDL_KEYUP) {
+               if (e.key.keysym.sym == SDLK_w) {
+                       leftPaddle.StopMovingUp();
+               } else if (e.key.keysym.sym == SDLK_s) {
+                       leftPaddle.StopMovingDown();
+               } else if (e.key.keysym.sym == SDLK_UP) {
+                       rightPaddle.StopMovingUp();
+               } else if (e.key.keysym.sym == SDLK_DOWN) {
+                       rightPaddle.StopMovingDown();
+               } else if (e.key.keysym.sym == SDLK_q) {
+                       if (ctrl) ctrl->Quit();
+               }
+       }
+}
+
+void Match::UpdateWorld(const Timer &timer) {
+       UpdateEntities(timer);
+       CheckCollisions(timer);
+       HandleCollisions(timer);
+}
+
+void Match::UpdateEntities(const Timer &timer) {
+       for (vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
+               (*i)->Update(timer);
+       }
+}
+
+void Match::CheckCollisions(const Timer &timer) {
+       Entity::Ray normal;
+       for (vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
+               for (vector<Entity *>::const_iterator j(i + 1); j != end; ++j) {
+                       if ((*i)->CheckCollision(**j, normal)) {
+                               collisions.push_back(Collision(*i, *j, normal));
+                       }
+               }
+       }
+}
+
+void Match::HandleCollisions(const Timer &timer) {
+       for (vector<Collision>::const_iterator i(collisions.begin()), end(collisions.end()); i != end; ++i) {
+               Entity::CollisionResponse(*(i->left), i->normal, *(i->right));
+       }
+       // TODO: resolve collisions of static objects
+       for (vector<Collision>::const_iterator i(collisions.begin()), end(collisions.end()); i != end; ++i) {
+               i->left->Collide(*i->right, i->normal);
+               i->right->Collide(*i->left, i->normal);
+       }
+       if (!collisions.empty()) {
+               updateScore = true;
+       }
+       collisions.clear();
+}
+
+
+void Match::Render(SDL_Surface *screen) {
+       SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
+       for (vector<Entity *>::const_iterator i(entities.begin()), end(entities.end()); i != end; ++i) {
+               (*i)->Render(screen);
+       }
+       RenderHUD(screen);
+}
+
+void Match::RenderHUD(SDL_Surface *screen) {
+       if (updateScore) {
+               UpdateScore(screen);
+               updateScore = false;
+       }
+       RenderScore(screen);
+}
+
+void Match::RenderScore(SDL_Surface *screen) {
+       SDL_Rect dest = { 2, 2, 0, 0 };
+       SDL_BlitSurface(leftScoreText, 0, screen, &dest);
+
+       dest.x = screen->w - rightScoreText->w - 2;
+       SDL_BlitSurface(rightScoreText, 0, screen, &dest);
+}
+
+void Match::UpdateScore(SDL_Surface *screen) {
+       if (!scoreFont) return;
+       if (leftScoreText) SDL_FreeSurface(leftScoreText);
+       if (rightScoreText) SDL_FreeSurface(rightScoreText);
+
+       SDL_Color color;
+       color.r = 0xFF;
+       color.g = 0xFF;
+       color.b = 0xFF;
+
+       stringstream s;
+       s << rightWall.HitCount();
+       leftScoreText = TTF_RenderUTF8_Blended(scoreFont, s.str().c_str(), color);
+
+       s.str("");
+       s << leftWall.HitCount();
+       rightScoreText = TTF_RenderUTF8_Blended(scoreFont, s.str().c_str(), color);
+}
+
+}
diff --git a/src/pong/Match.h b/src/pong/Match.h
new file mode 100644 (file)
index 0000000..a838ebe
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Match.h
+ *
+ *  Created on: Apr 9, 2012
+ *      Author: holy
+ */
+
+#ifndef PONG_MATCH_H_
+#define PONG_MATCH_H_
+
+#include "Ball.h"
+#include "CountingWall.h"
+#include "Paddle.h"
+#include "../app/State.h"
+#include "../game/Collision.h"
+#include "../game/Entity.h"
+
+#include <utility>
+#include <vector>
+#include <SDL/SDL_ttf.h>
+
+
+namespace pong {
+
+class Match
+: public app::State {
+
+       public:
+               Match(void);
+               virtual ~Match(void);
+
+       public:
+               virtual void EnterState(app::Control *ctrl, SDL_Surface *screen);
+               virtual void ExitState(void);
+
+               virtual void HandleEvent(const SDL_Event &);
+               virtual void UpdateWorld(const app::Timer &);
+               virtual void Render(SDL_Surface *);
+
+       private:
+               void UpdateEntities(const app::Timer &);
+               void CheckCollisions(const app::Timer &);
+               void CheckWorldCollisions(const app::Timer &);
+               void HandleCollisions(const app::Timer &);
+               void UpdateScore(SDL_Surface *);
+               void RenderHUD(SDL_Surface *);
+               void RenderScore(SDL_Surface *);
+
+       private:
+               app::Control *ctrl;
+               TTF_Font *scoreFont;
+               SDL_Surface *leftScoreText, *rightScoreText;
+               Sint32 paddleSpeed;
+               Uint32 worldWidth, worldHeight;
+               Ball ball, secondBall, thirdBall;
+               Paddle leftPaddle, rightPaddle;
+               CountingWall topWall, bottomWall, leftWall, rightWall;
+               std::vector<game::Entity *> entities;
+               std::vector<game::Collision> collisions;
+               bool updateScore;
+
+};
+
+}
+
+#endif /* PONG_MATCH_H_ */
diff --git a/src/pong/Paddle.cpp b/src/pong/Paddle.cpp
new file mode 100644 (file)
index 0000000..ef922cc
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Paddle.cpp
+ *
+ *  Created on: Apr 10, 2012
+ *      Author: holy
+ */
+
+#include "Paddle.h"
+
+#include "Ball.h"
+
+#include <limits>
+
+
+namespace pong {
+
+Paddle::Paddle(Scalar width, Scalar height)
+: Entity(&shape, std::numeric_limits<Scalar>::infinity())
+, shape(width, height) {
+
+}
+
+
+void Paddle::Collide(Entity &other, const Ray &) {
+       if (dynamic_cast<Ball *>(&other)) {
+               Vector distance(other.Origin() - Origin());
+               other.Accelerate(distance.Unit() * 5);
+       }
+}
+
+void Paddle::Render(SDL_Surface *screen) const {
+       SDL_Rect destRect;
+       destRect.x = shape.Left();
+       destRect.y = shape.Top();
+       destRect.w = shape.Width();
+       destRect.h = shape.Height();
+       SDL_FillRect(screen, &destRect, SDL_MapRGB(screen->format, 0xFF, 0xFF, 0xFF));
+}
+
+
+void Paddle::SetMovementSpeed(Vector s) {
+//     Vector d(s - movementSpeed);
+       movementSpeed = s;
+//     Accelerate(d);
+}
+
+
+void Paddle::StartMovingUp(void) {
+       Accelerate(-movementSpeed);
+}
+
+void Paddle::StopMovingUp(void) {
+       Accelerate(movementSpeed);
+}
+
+void Paddle::StartMovingDown(void) {
+       Accelerate(movementSpeed);
+}
+
+void Paddle::StopMovingDown(void) {
+       Accelerate(-movementSpeed);
+}
+
+}
diff --git a/src/pong/Paddle.h b/src/pong/Paddle.h
new file mode 100644 (file)
index 0000000..85da2af
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Paddle.h
+ *
+ *  Created on: Apr 10, 2012
+ *      Author: holy
+ */
+
+#ifndef PONG_PADDLE_H_
+#define PONG_PADDLE_H_
+
+#include "../game/Entity.h"
+#include "../shape/AABB.h"
+
+
+namespace pong {
+
+class Paddle
+: public game::Entity {
+
+       public:
+               Paddle(Scalar width, Scalar height);
+               virtual ~Paddle(void) { };
+
+       public:
+               virtual void Collide(Entity &other, const Ray &);
+               virtual void Render(SDL_Surface *dest) const;
+
+       public:
+               void SetMovementSpeed(Vector);
+
+               void StartMovingUp(void);
+               void StopMovingUp(void);
+               void StartMovingDown(void);
+               void StopMovingDown(void);
+
+       private:
+               shape::AABB shape;
+               Vector movementSpeed;
+
+};
+
+}
+
+#endif /* PONG_PADDLE_H_ */
diff --git a/src/sdl-test8.cpp b/src/sdl-test8.cpp
new file mode 100644 (file)
index 0000000..ad1e891
--- /dev/null
@@ -0,0 +1,26 @@
+//============================================================================
+// Name        : sdl-test8.cpp
+// Author      : HolySmoke
+// Version     : .3
+// Copyright   : MINE, ALL MINE!
+// Description : Some pong crap supposed to have somewhat realistic physics.
+//============================================================================
+
+#include "app/Application.h"
+#include "pong/Match.h"
+#include "sdl/InitScreen.h"
+#include "sdl/InitSDL.h"
+#include "sdl/InitTTF.h"
+
+int main(int argc, char *argv[]) {
+       const int width(800);
+       const int height(480);
+
+       sdl::InitSDL initSDL;
+       sdl::InitTTF initTTF;
+       sdl::InitScreen initScreen(width, height);
+
+       app::Application a(&initScreen, new pong::Match);
+       a.Run();
+       return 0;
+}
diff --git a/src/sdl/InitSDL.cpp b/src/sdl/InitSDL.cpp
new file mode 100644 (file)
index 0000000..0f2eacd
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * InitSDL.cpp
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#include "InitSDL.h"
+
+#include <stdexcept>
+
+using std::runtime_error;
+
+
+namespace sdl {
+
+InitSDL::InitSDL(Uint32 flags) {
+       if (SDL_Init(flags) != 0) {
+               throw runtime_error("failed to initialize SDL");
+       }
+}
+
+InitSDL::~InitSDL(void) {
+       SDL_Quit();
+}
+
+}
diff --git a/src/sdl/InitSDL.h b/src/sdl/InitSDL.h
new file mode 100644 (file)
index 0000000..016c0b4
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * InitSDL.h
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#ifndef SDL_INITSDL_H_
+#define SDL_INITSDL_H_
+
+#include <SDL/SDL.h>
+
+
+namespace sdl {
+
+class InitSDL {
+
+       public:
+               explicit InitSDL(Uint32 flags = SDL_INIT_EVERYTHING);
+               virtual ~InitSDL(void);
+       private:
+               InitSDL(const InitSDL &);
+               InitSDL &operator =(const InitSDL &);
+
+};
+
+}
+
+#endif /* SDL_INITSDL_H_ */
diff --git a/src/sdl/InitScreen.cpp b/src/sdl/InitScreen.cpp
new file mode 100644 (file)
index 0000000..c9b275f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * InitScreen.cpp
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#include "InitScreen.h"
+
+#include <stdexcept>
+
+using std::runtime_error;
+
+
+namespace sdl {
+
+InitScreen::InitScreen(int width, int height, int bpp, Sint32 flags)
+: screen(SDL_SetVideoMode(width, height, bpp, flags)) {
+       if (!screen) {
+               throw runtime_error("failed to open screen");
+       }
+}
+
+InitScreen::~InitScreen(void) {
+
+}
+
+}
diff --git a/src/sdl/InitScreen.h b/src/sdl/InitScreen.h
new file mode 100644 (file)
index 0000000..ba731da
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * InitScreen.h
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#ifndef SDL_INITSCREEN_H_
+#define SDL_INITSCREEN_H_
+
+#include <SDL/SDL.h>
+
+namespace sdl {
+
+class InitScreen {
+
+       public:
+               InitScreen(int width, int height, int bpp = 32, Sint32 flags = SDL_HWSURFACE | SDL_DOUBLEBUF);
+               virtual ~InitScreen(void);
+       private:
+               InitScreen(const InitScreen &);
+               InitScreen &operator =(const InitScreen &);
+
+       public:
+               SDL_Surface *Screen(void) { return screen; };
+               const SDL_Surface *Screen(void) const { return screen; };
+
+               void Flip(void) { SDL_Flip(screen); };
+
+       private:
+               SDL_Surface *screen;
+
+};
+
+}
+
+#endif /* SDL_INITSCREEN_H_ */
diff --git a/src/sdl/InitTTF.cpp b/src/sdl/InitTTF.cpp
new file mode 100644 (file)
index 0000000..5818a73
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * InitTTF.cpp
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#include "InitTTF.h"
+
+#include <stdexcept>
+#include <SDL/SDL_ttf.h>
+
+using std::runtime_error;
+
+
+namespace sdl {
+
+InitTTF::InitTTF(void) {
+       if (TTF_Init() != 0) {
+               throw runtime_error("failed to initialize SDL TTF");
+       }
+}
+
+InitTTF::~InitTTF(void) {
+       TTF_Quit();
+}
+
+}
diff --git a/src/sdl/InitTTF.h b/src/sdl/InitTTF.h
new file mode 100644 (file)
index 0000000..6f0306c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * InitTTF.h
+ *
+ *  Created on: Apr 22, 2012
+ *      Author: holy
+ */
+
+#ifndef SDL_INITTTF_H_
+#define SDL_INITTTF_H_
+
+namespace sdl {
+
+class InitTTF {
+
+       public:
+               InitTTF(void);
+               virtual ~InitTTF(void);
+       private:
+               InitTTF(const InitTTF &);
+               InitTTF &operator =(const InitTTF &);
+
+};
+
+}
+
+#endif /* SDL_INITTTF_H_ */
diff --git a/src/shape/AABB.cpp b/src/shape/AABB.cpp
new file mode 100644 (file)
index 0000000..75007ec
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * AABB.cpp
+ *
+ *  Created on: Apr 30, 2012
+ *      Author: holy
+ */
+
+#include "AABB.h"
+
+#include "Circle.h"
+
+namespace shape {
+
+void AABB::Translate(const Vector &delta) {
+       leftTop += delta;
+       rightBottom += delta;
+}
+
+void AABB::Rotate(Scalar delta) {
+
+}
+
+
+bool AABB::CheckCollision(const Shape &other, Ray &na) const {
+       if (other.CheckCollision(*this, na)) {
+               na.Direction() *= -1;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+bool AABB::CheckCollision(const AABB &other, Ray &na) const {
+       if (Bottom() <= other.Top()) return false;
+       if (other.Bottom() <= Top()) return false;
+       if (Right() <= other.Left()) return false;
+       if (other.Right() <= Left()) return false;
+
+       if (Bottom() <= other.Top() || other.Bottom() <= Top()) {
+               if (Left() < other.Left()) {
+                       na.Origin() = Vector(((Top() + Bottom()) / 2 + (other.Top() + other.Bottom()) / 2) / 2, Right());
+                       na.Direction() = Vector(-1, 0);
+               } else {
+                       na.Origin() = Vector(((Top() + Bottom()) / 2 + (other.Top() + other.Bottom()) / 2) / 2, Left());
+                       na.Direction() = Vector(1, 0);
+               }
+       } else {
+               if (Top() < other.Top()) {
+                       na.Origin() = Vector(Bottom(), ((Left() + Right()) / 2 + (other.Left() + other.Right()) / 2) / 2);
+                       na.Direction() = Vector(0, -1);
+               } else {
+                       na.Origin() = Vector(Top(), ((Left() + Right()) / 2 + (other.Left() + other.Right()) / 2) / 2);
+                       na.Direction() = Vector(0, 1);
+               }
+       }
+       return true;
+}
+
+bool AABB::CheckCollision(const Circle &other, Ray &na) const {
+       if (other.CheckCollision(*this, na)) {
+               na.Direction() *= -1;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+
+std::ostream &AABB::Write(std::ostream &out) const {
+       return out << "AABB(" << Width() << 'x' << Height() << ", " << Center() << ')';
+}
+
+}
diff --git a/src/shape/AABB.h b/src/shape/AABB.h
new file mode 100644 (file)
index 0000000..956114a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * AABB.h
+ *
+ *  Created on: Apr 30, 2012
+ *      Author: holy
+ */
+
+#ifndef SHAPE_AABB_H_
+#define SHAPE_AABB_H_
+
+#include "Shape.h"
+
+namespace shape {
+
+class AABB
+: public Shape {
+
+       public:
+               AABB(void) { };
+               AABB(Scalar width, Scalar height) : leftTop(-width/2, -height/2), rightBottom(width/2, height/2) { };
+               virtual ~AABB(void) { };
+
+       public:
+               Scalar Left(void) const { return leftTop.X(); };
+               Scalar Top(void) const { return leftTop.Y(); };
+               Scalar Right(void) const { return rightBottom.X(); };
+               Scalar Bottom(void) const { return rightBottom.Y(); };
+
+               Vector Center(void) const { return leftTop + (rightBottom - leftTop) / 2; };
+               Scalar Width(void) const { return Right() - Left(); };
+               Scalar Height(void) const { return Bottom() - Top(); };
+
+       public:
+               virtual void Translate(const Vector &delta);
+               virtual void Rotate(Scalar delta);
+
+       public:
+               virtual bool CheckCollision(const Shape &, Ray &) const;
+               virtual bool CheckCollision(const AABB &, Ray &) const;
+               virtual bool CheckCollision(const Circle &, Ray &) const;
+
+       public:
+               virtual std::ostream &Write(std::ostream &) const;
+
+       private:
+               Vector leftTop, rightBottom;
+
+};
+
+}
+
+#endif /* SHAPE_AABB_H_ */
diff --git a/src/shape/Circle.cpp b/src/shape/Circle.cpp
new file mode 100644 (file)
index 0000000..3549db9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Circle.cpp
+ *
+ *  Created on: Apr 29, 2012
+ *      Author: holy
+ */
+
+#include "Circle.h"
+
+#include "AABB.h"
+
+namespace shape {
+
+void Circle::Translate(const Vector &delta) {
+       center += delta;
+}
+
+void Circle::Rotate(Scalar delta) {
+
+}
+
+
+bool Circle::CheckCollision(const Shape &other, Ray &na) const {
+       if (other.CheckCollision(*this, na)) {
+               na.Direction() *= -1;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+bool Circle::CheckCollision(const AABB &other, Ray &na) const {
+       int xZone(
+                       center.X() < other.Left() ? 0 : (
+                       other.Right() < center.X() ? 2 : 1));
+       int yZone(
+                               center.Y() < other.Top() ? 0 : (
+                               other.Bottom() < center.Y() ? 2 : 1));
+       int zone(xZone + 3 * yZone);
+
+       if (zone == 4) {
+               na.Origin() = center;
+               na.Direction() = (center - other.Center()).Unit();
+               return true;
+       }
+
+       if (zone % 2) {
+               if (xZone == 1) { // vertical
+                       if (Bottom() < other.Top()) return false;
+                       if (other.Bottom() < Top()) return false;
+
+                       if (Bottom() < other.Bottom()) {
+                               na.Origin() = Vector(center.X(), Bottom());
+                               na.Direction() = Vector(0, -1);
+                       } else {
+                               na.Origin() = Vector(center.X(), Top());
+                               na.Direction() = Vector(0, 1);
+                       }
+
+                       return true;
+               } else {
+                       if (Right() < other.Left()) return false;
+                       if (other.Right() < Left()) return false;
+
+                       if (Left() < other.Left()) {
+                               na.Origin() = Vector(Left(), center.Y());
+                               na.Direction() = Vector(-1, 0);
+                       } else {
+                               na.Origin() = Vector(Right(), center.Y());
+                               na.Direction() = Vector(1, 0);
+                       }
+
+                       return true;
+               }
+       } else {
+               Vector near(
+                               yZone ? other.Right() : other.Left(),
+                               xZone ? other.Bottom() : other.Top());
+               Vector distance(Center() - near);
+               Scalar distanceSquared(distance.LengthSquared());
+               if (distanceSquared < (Radius() * Radius())) {
+                       na.Origin() = near;
+                       na.Direction() = distance.Unit();
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+}
+
+bool Circle::CheckCollision(const Circle &other, Ray &na) const {
+       const Vector distance(center - other.center);
+       const Scalar distanceSquared(distance.LengthSquared());
+       const Scalar radii(radius + other.radius);
+
+       if (distanceSquared < (radii * radii)) {
+               na.Direction() = distance.Unit();
+               na.Origin() = center + (na.Direction() * radius);
+               return true;
+       } else {
+               return false;
+       }
+}
+
+
+std::ostream &Circle::Write(std::ostream &out) const {
+       return out << "Circle(" << Radius() << ", " << Center() << ')';
+}
+
+}
diff --git a/src/shape/Circle.h b/src/shape/Circle.h
new file mode 100644 (file)
index 0000000..ff0363d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Circle.h
+ *
+ *  Created on: Apr 29, 2012
+ *      Author: holy
+ */
+
+#ifndef SHAPE_CIRCLE_H_
+#define SHAPE_CIRCLE_H_
+
+#include "Shape.h"
+
+namespace shape {
+
+class Circle
+: public Shape {
+
+       public:
+               Circle(void) { };
+               explicit Circle(Scalar r, Vector pos = Vector()) : center(pos), radius(r) { };
+               virtual ~Circle(void) { };
+
+       public:
+               const Vector &Center(void) const { return center; };
+               Scalar Radius(void) const { return radius; };
+
+               Scalar Left(void) const { return center.X() - radius; };
+               Scalar Top(void) const { return center.Y() - radius; };
+               Scalar Right(void) const { return center.X() + radius; };
+               Scalar Bottom(void) const { return center.Y() + radius; };
+
+       public:
+               virtual void Translate(const Vector &delta);
+               virtual void Rotate(Scalar delta);
+
+       public:
+               virtual bool CheckCollision(const Shape &, Ray &) const;
+               virtual bool CheckCollision(const AABB &, Ray &) const;
+               virtual bool CheckCollision(const Circle &, Ray &) const;
+
+       public:
+               virtual std::ostream &Write(std::ostream &) const;
+
+       private:
+               Vector center;
+               Scalar radius;
+
+};
+
+}
+
+#endif /* SHAPE_CIRCLE_H_ */
diff --git a/src/shape/Shape.h b/src/shape/Shape.h
new file mode 100644 (file)
index 0000000..69eedc2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Shape.h
+ *
+ *  Created on: Apr 29, 2012
+ *      Author: holy
+ */
+
+#ifndef SHAPE_SHAPE_H_
+#define SHAPE_SHAPE_H_
+
+#include "../geometry/Ray2D.h"
+#include "../geometry/Vector2D.h"
+
+#include <ostream>
+
+
+namespace shape {
+
+class AABB;
+class Circle;
+
+class Shape {
+
+       public:
+               typedef float Scalar;
+               typedef geometry::Vector2D<Scalar> Vector;
+               typedef geometry::Ray2D<Scalar> Ray;
+
+       public:
+               virtual ~Shape(void) { };
+
+       public:
+               virtual void Translate(const Vector &delta) = 0;
+               virtual void Rotate(Scalar delta) = 0;
+
+       public:
+               virtual bool CheckCollision(const Shape &, Ray &) const = 0;
+               virtual bool CheckCollision(const AABB &, Ray &) const = 0;
+               virtual bool CheckCollision(const Circle &, Ray &) const = 0;
+
+       public:
+               virtual std::ostream &Write(std::ostream &) const = 0;
+
+};
+
+
+inline std::ostream &operator <<(std::ostream &out, const Shape &s) {
+       return s.Write(out);
+};
+
+}
+
+#endif /* SHAPE_SHAPE_H_ */