1 /*
2 Copyright (C) 2009 Facundo Domínguez
3
4 This file is part of Spacejunk.
5
6 Spacejunk is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Foobar is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Foobar. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef SIMULATION_H
21 #define SIMULATION_H
22
23 #include "physicbody.h"
24 #include "gamebody.h"
25 #include "timer.h"
26 #include "functionexts.h"
27 #include <list>
28 #include "eventlistenerlist.h"
29
30 template <class Item,class Data>
31 class CollisionEngine;
32 class Simulation;
33 class Collision;
34
35 typedef TimerQueue<Simulation*>::TimerRef GeneralTimer;
36 typedef TimerQueue<Simulation*> Timer;
37 typedef TimerEvent<Simulation*> Event;
38 typedef _EmptyEvent<Simulation*> EmptyEvent;
39 typedef TimerQueue<Simulation*>::TimerId TimerId;
40
41
42 /** A listener for collision events. */
43 class SimCollisionListener {
44 public:
45 /**
46 * Called whenever a collision occurs.
47 * @param sim is the simulation where the collision occurred
48 * @param c1 one of the coliding bodies
49 * @param c2 the other coliding bodies
50 * @param normal the normal to the surface of contact between the bodies.
51 * */
52 virtual void evtCollision(Simulation * sim,PhysicBody * c1,PhysicBody * c2,const Vector2d & normal)=0;
~SimCollisionListener()53 virtual ~SimCollisionListener() {};
54 };
55
56 /** A listener for object removals. */
57 class SimDeletionListener {
58 public:
59 /**
60 * Called whenever an object with identifier id is removed
61 * from a simulation sim.
62 */
63 virtual void evtDeleteBody(Simulation * sim,int id)=0;
~SimDeletionListener()64 virtual ~SimDeletionListener() {};
65 };
66
67 /** Implementation of a physical simulation of a system with gravitational forces. */
68 class Simulation {
69 public:
70 mutable EventListenerList<SimDeletionListener*> deletionListeners;
71 mutable EventListenerList<SimCollisionListener*> collisionListeners;
72
73 /** The PlayerState may need to be updated whenever an event happens. */
74 Simulation(const PlayerState & ps,const std::list<int> &onStep);
75
76 /** Steps the simulation a period of time in milliseconds. */
77 virtual void step(int delta);
78 /**
79 * Steps the simulation a period of time in milliseconds.
80 * A call to this function may involve various substeps.
81 * The template parameter specifies a function object that
82 * is called after each substep in the simulation (passing it
83 * the size of the substep in milliseconds).
84 * A negative time may be passed to posfunction, which indicates that
85 * the simulation will go backwards the given amount
86 * of time.
87 * */
88 template <class PosUnaryFunction >
89 void step(int delta,PosUnaryFunction posfunction);
90
91 virtual void clear();
92 virtual void purge();
93
94 virtual void addBody(PhysicBody * c);
95 void addBody(int id,PhysicBody * c);
96 virtual void deleteBody(int id);
97
98 /**
99 * Sets a timer.
100 * @param tm timer event to set.
101 * @returns a reference to the set timer.
102 */
set(const GeneralTimer & tm)103 virtual TimerId set(const GeneralTimer & tm) {
104 return tevents.set(tm);
105 };
106
setPrecision(int p)107 virtual void setPrecision(int p) {
108 physic.bigStep=p;
109 }
setCollisionPrecision(int p)110 virtual void setCollisionPrecision(int p) {
111 colPrecision=p;
112 }
113
getPlayerState()114 const PlayerState * getPlayerState() const {
115 return &physic.ps;
116 }
getPlayerState()117 PlayerState * getPlayerState() {
118 return &physic.ps;
119 }
120
121 PhysicEngine physic;
122 Timer tevents;
123
124 virtual ~Simulation();
125
126 void operator = (const Simulation & orig);
127
128 private:
129 CollisionEngine<PhysicBody,Simulation> * ce;
130 /**
131 * Pushes into the timer event queue the collisions that happened at the exact
132 * collision times. Collisions are detected after they occurred, therefore
133 * the simulation may need to undo a small amount of time so the collisions
134 * are produced at their real times with accuracy. This method unsteps the
135 * simulation if needed and returns the amount of undone time.
136 * */
137 int handle_collisions();
138
139 const std::list<int> & onStep;
140 // how much time to wait between collision tests
141 int colPrecision;
142 // time remaining before the next collision test
143 int deltaCol;
144 std::list<std::pair<int,Collision> > coldata;
145 template <class PosUnaryFunction >
146 /** Steps the simulation without testing for collisions. */
147 void _step(int delta,PosUnaryFunction posfunction);
148 friend int handle_col(Simulation * sim,PhysicBody * c1,PhysicBody * c2,real t,const Vector2d & normal);
149 friend class Collision;
150 void deleteBody_without_event(int id);
151 };
152
153
154 template <class PosUnaryFunction >
step(int delta,PosUnaryFunction posfunction)155 void Simulation::step(int delta,PosUnaryFunction posfunction) {
156 int first_delta = delta;
157 if (physic.minDist<32 && deltaCol>10) {
158 // test collisions regardless the remaining time for the
159 // next test
160 int t=handle_collisions();
161 if (t) {
162 posfunction(-t);
163 _step(t,posfunction);
164 }
165 deltaCol=colPrecision;
166 }
167
168 if (deltaCol<=delta) {
169 if (deltaCol) {
170 _step(deltaCol,posfunction);
171 delta-=deltaCol;
172 int t=handle_collisions();
173 if (t) {
174 posfunction(-t);
175 _step(t,posfunction);
176 }
177 }
178 while (delta>=colPrecision) {
179 _step(colPrecision,posfunction);
180 delta-=colPrecision;
181 int t=handle_collisions();
182 if (t) {
183 posfunction(-t);
184 _step(t,posfunction);
185 }
186 }
187 deltaCol=colPrecision-delta;
188 } else
189 deltaCol-=delta;
190
191 // if step(0) was called force evaluation of _step
192 if (!first_delta || delta)
193 _step(delta,posfunction);
194 };
195
196
197 template <class PosUnaryFunction >
_step(int delta,PosUnaryFunction posfunction)198 void Simulation::_step(int delta,PosUnaryFunction posfunction) {
199 for (std::list<int>::const_iterator i=onStep.begin();i!=onStep.end();i++)
200 physic.bodies[(*i)]->onStep();
201 tevents.step(delta,bind2nd(
202 bind1st3(
203 mem_fun2(&PhysicEngine::best_step<PosUnaryFunction>),
204 &physic),
205 posfunction),
206 bind2nd(
207 bind1st3(
208 mem_fun2(&PhysicEngine::step<PosUnaryFunction>),
209 &physic),
210 posfunction)
211 );
212 };
213
214
215 #endif // SIMULATION_H
216