World: Extracted the collision reaction code into its own class. Made the collision...
[scd.git] / src / net / habraun / sd / World.scala
blob42b53e40e2fe0f4d97d4e846d62bd3db673038c1
1 /*
2 Copyright (c) 2009 Hanno Braun <hanno@habraun.net>
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
19 package net.habraun.sd
23 import math._
25 import scala.collection.mutable._
29 /**
30 * The central class for the physics simulation.
31 * World is basically a container for objects, whose attributes it updates every simulation step.
34 class World {
36 private[this] val _bodies = new HashSet[Body]
40 /**
41 * Returns a copy of the body set as an Iterable.
44 def bodies: Iterable[Body] = _bodies.clone
48 /**
49 * The integrator is used to integrate the bodies.
52 private[this] var integrate: Integrator = new EulerIntegrator
54 def integrator = integrate
56 def integrator_=(i: Integrator) {
57 if (i == null) throw new NullPointerException("Integrator must not be null.")
58 integrate = i
63 /**
64 * The broad phase is used for detecting which bodys can possible collide.
65 * This is done to cut down the time spent on doing detailed collision checks.
68 private[this] var _broadPhase: BroadPhase = new SimpleBroadPhase
70 def broadPhase = _broadPhase
72 def broadPhase_=(bp: BroadPhase) = {
73 if (bp == null) throw new NullPointerException
74 _broadPhase = bp
79 /**
80 * The narrow phase is used to perform detailed (and possibly expensive) collision testing on body pairs
81 * that made it through the broad phase.
84 private[this] var _narrowPhase: NarrowPhase = new SimpleNarrowPhase
86 def narrowPhase = _narrowPhase
88 def narrowPhase_=(np: NarrowPhase) = {
89 if (np == null) throw new NullPointerException
90 _narrowPhase = np
95 /**
96 * The constraint solver.
99 private[this] var solve: ConstraintSolver = new ImpulseSolver
101 def constraintSolver = solve
103 def constraintSolver_=(solver: ConstraintSolver) = {
104 if (solver == null) throw new NullPointerException
105 solve = solver
111 * Adds a body to the world. The body will be simulated until it is removed.
114 def add(body: Body) {
115 _bodies.addEntry(body)
121 * Removes the body from the world. The body will no longer be simulated.
124 def remove(body: Body) {
125 _bodies.removeEntry(body)
131 * Steps the physics simulation.
132 * All bodies are moved, according to their velocity and the forces that are applied to them.
135 def step(delta: Double) {
136 // Check if delta is valid.
137 if (delta < 0.0) throw new IllegalArgumentException("Time delta must be 0 or greater.")
139 // Integrate bodies.
140 _bodies.foreach(integrate(delta, _))
142 // Collision detection.
143 val possibleCollisionPairs = broadPhase.detectPossibleCollisions(_bodies.toList)
144 val possibleCollisions = possibleCollisionPairs.map((pair) => {
145 narrowPhase.inspectCollision(pair._1, pair._2)
148 // Compute collision effects.
149 // This is a tricky construction. The "possibleCollision <- collision" part is like an outer for loop
150 // that iterates through all collisions. Collisions is a list of Option[Collision], this means,
151 // during each iteration possibleCollision is either Some(collision) or None.
152 // The "collision <- possibleCollision" part is technically an inner loop that iterates through the
153 // Option[Collision]. This works because because Option is like a collection that contains either one
154 // (if if is an instance of Some) or no (if it is None) elements.
155 // Despite the long explanation, what this does is actually pretty simple: We loop through the list
156 // of possible collisions. We execute the yield stuff only for actual collisions, not for None.
157 for ( possibleCollision <- possibleCollisions; collision <- possibleCollision ) yield {
158 solve(delta, collision)