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
25 import scala
.collection
.mutable
._
30 * The central class for the physics simulation.
31 * World is basically a container for objects, whose attributes it updates every simulation step.
36 private[this] val _bodies
= new HashSet
[Body
]
41 * Returns a copy of the body set as an Iterable.
44 def bodies
: Iterable
[Body
] = _bodies
.clone
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.")
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
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
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
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.
133 * The parameter t is the time delta for this simulation step.
136 def step(t
: Double
) {
137 // Check if delta is valid.
138 if (t
< 0.0) throw new IllegalArgumentException("Time delta must be 0 or greater.")
141 _bodies
.foreach(integrate(t
, _
))
143 // Collision detection.
144 val possibleCollisionPairs
= broadPhase(_bodies
.toList
)
145 val possibleCollisions
= possibleCollisionPairs
.map((pair
) => {
146 narrowPhase
.inspectCollision(pair
._1
, pair
._2
)
149 // Compute collision effects.
150 // This is a tricky construction. The "possibleCollision <- collision" part is like an outer for loop
151 // that iterates through all collisions. Collisions is a list of Option[Collision], this means,
152 // during each iteration possibleCollision is either Some(collision) or None.
153 // The "collision <- possibleCollision" part is technically an inner loop that iterates through the
154 // Option[Collision]. This works because because Option is like a collection that contains either one
155 // (if if is an instance of Some) or no (if it is None) elements.
156 // Despite the long explanation, what this does is actually pretty simple: We loop through the list
157 // of possible collisions. We execute the yield stuff only for actual collisions, not for None.
158 for ( possibleCollision
<- possibleCollisions
; collision
<- possibleCollision
) {