|
|
@@ -4,16 +4,29 @@
|
|
|
|
|
|
package physics
|
|
|
|
|
|
+import (
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
type ICollidable interface {
|
|
|
+ Collided() bool
|
|
|
+ Static() bool
|
|
|
+ //GetCollisions() []*Collision
|
|
|
+}
|
|
|
|
|
|
+type CollisionGroup struct {
|
|
|
+ // TODO future
|
|
|
}
|
|
|
|
|
|
// Simulation represents a physics simulation.
|
|
|
type Simulation struct {
|
|
|
- forceFields []*ForceField
|
|
|
- rigidBodies []*RigidBody
|
|
|
+ forceFields []ForceField
|
|
|
+ bodies []*Body
|
|
|
particles []*Particle
|
|
|
|
|
|
+ //dynamical []int // non collidable but still affected by force fields
|
|
|
+ //collidables []ICollidable
|
|
|
+
|
|
|
}
|
|
|
|
|
|
// NewSimulation creates and returns a pointer to a new physics simulation.
|
|
|
@@ -24,14 +37,14 @@ func NewSimulation() *Simulation {
|
|
|
}
|
|
|
|
|
|
// AddForceField adds a force field to the simulation.
|
|
|
-func (s *Simulation) AddForceField(ff *ForceField) {
|
|
|
+func (s *Simulation) AddForceField(ff ForceField) {
|
|
|
|
|
|
s.forceFields = append(s.forceFields, ff)
|
|
|
}
|
|
|
|
|
|
// RemoveForceField removes the specified force field from the simulation.
|
|
|
-// Returns true if found or false otherwise.
|
|
|
-func (s *Simulation) RemoveForceField(ff *ForceField) bool {
|
|
|
+// Returns true if found, false otherwise.
|
|
|
+func (s *Simulation) RemoveForceField(ff ForceField) bool {
|
|
|
|
|
|
for pos, current := range s.forceFields {
|
|
|
if current == ff {
|
|
|
@@ -44,26 +57,125 @@ func (s *Simulation) RemoveForceField(ff *ForceField) bool {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
-func (s *Simulation) CollisionBroadphase() {
|
|
|
+// AddBody adds a body to the simulation.
|
|
|
+func (s *Simulation) AddBody(rb *Body) {
|
|
|
|
|
|
+ s.bodies = append(s.bodies, rb)
|
|
|
}
|
|
|
|
|
|
-func (s *Simulation) CheckCollisions(collidables []ICollidable) {
|
|
|
+// RemoveBody removes the specified body from the simulation.
|
|
|
+// Returns true if found, false otherwise.
|
|
|
+func (s *Simulation) RemoveBody(rb *Body) bool {
|
|
|
|
|
|
+ for pos, current := range s.bodies {
|
|
|
+ if current == rb {
|
|
|
+ copy(s.bodies[pos:], s.bodies[pos+1:])
|
|
|
+ s.bodies[len(s.bodies)-1] = nil
|
|
|
+ s.bodies = s.bodies[:len(s.bodies)-1]
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
}
|
|
|
|
|
|
-// Step steps the simulation.
|
|
|
-func (s *Simulation) Step() {
|
|
|
+// AddParticle adds a particle to the simulation.
|
|
|
+func (s *Simulation) AddParticle(rb *Particle) {
|
|
|
|
|
|
- // Check for collisions (broad phase)
|
|
|
- //s.CollisionBroadphase()
|
|
|
+ s.particles = append(s.particles, rb)
|
|
|
+}
|
|
|
|
|
|
- // Check for collisions (narrow phase)
|
|
|
- //s.CheckCollisions()
|
|
|
+// RemoveParticle removes the specified particle from the simulation.
|
|
|
+// Returns true if found, false otherwise.
|
|
|
+func (s *Simulation) RemoveParticle(rb *Particle) bool {
|
|
|
|
|
|
- // Apply forces/inertia/impulses
|
|
|
+ for pos, current := range s.particles {
|
|
|
+ if current == rb {
|
|
|
+ copy(s.particles[pos:], s.particles[pos+1:])
|
|
|
+ s.particles[len(s.particles)-1] = nil
|
|
|
+ s.particles = s.particles[:len(s.particles)-1]
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
|
|
|
+func (s *Simulation) applyForceFields(frameDelta time.Duration) {
|
|
|
+
|
|
|
+ for _, ff := range s.forceFields {
|
|
|
+ for _, rb := range s.bodies {
|
|
|
+ pos := rb.GetNode().Position()
|
|
|
+ force := ff.ForceAt(&pos)
|
|
|
+ //log.Error("force: %v", force)
|
|
|
+ //log.Error("mass: %v", rb.mass)
|
|
|
+ //log.Error("frameDelta: %v", frameDelta.Seconds())
|
|
|
+ vdiff := force.MultiplyScalar(float32(frameDelta.Seconds())/rb.mass)
|
|
|
+ //log.Error("vdiff: %v", vdiff)
|
|
|
+ rb.velocity.Add(vdiff)
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // Update visual representation
|
|
|
+ for _, ff := range s.forceFields {
|
|
|
+ for _, rb := range s.particles {
|
|
|
+ pos := rb.GetNode().Position()
|
|
|
+ force := ff.ForceAt(&pos)
|
|
|
+ //log.Error("force: %v", force)
|
|
|
+ //log.Error("mass: %v", rb.mass)
|
|
|
+ //log.Error("frameDelta: %v", frameDelta.Seconds())
|
|
|
+ vdiff := force.MultiplyScalar(float32(frameDelta.Seconds())/rb.mass)
|
|
|
+ //log.Error("vdiff: %v", vdiff)
|
|
|
+ rb.velocity.Add(vdiff)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-}
|
|
|
+// updatePositions integrates the velocity of the objects to obtain the position in the next frame.
|
|
|
+func (s *Simulation) updatePositions(frameDelta time.Duration) {
|
|
|
+
|
|
|
+ for _, rb := range s.bodies {
|
|
|
+ pos := rb.GetNode().Position()
|
|
|
+ posDelta := rb.velocity
|
|
|
+ posDelta.MultiplyScalar(float32(frameDelta.Seconds()))
|
|
|
+ pos.Add(&posDelta)
|
|
|
+ rb.GetNode().SetPositionVec(&pos)
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, rb := range s.particles {
|
|
|
+ pos := rb.GetNode().Position()
|
|
|
+ posDelta := rb.velocity
|
|
|
+ posDelta.MultiplyScalar(float32(frameDelta.Seconds()))
|
|
|
+ pos.Add(&posDelta)
|
|
|
+ rb.GetNode().SetPositionVec(&pos)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//func (s *Simulation) CheckCollisions() []*Collision{//collidables []ICollidable) {
|
|
|
+//
|
|
|
+// return
|
|
|
+//}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Simulation) Backtrack() {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Step steps the simulation.
|
|
|
+func (s *Simulation) Step(frameDelta time.Duration) {
|
|
|
+
|
|
|
+ // Check for collisions
|
|
|
+ //collisions := s.CheckCollisions()
|
|
|
+ //if len(collisions) > 0 {
|
|
|
+ // // Backtrack to 0 penetration
|
|
|
+ // s.Backtrack()
|
|
|
+ //}
|
|
|
+
|
|
|
+ // Apply static forces/inertia/impulses (only to objects that did not collide)
|
|
|
+ s.applyForceFields(frameDelta)
|
|
|
+ // s.applyDrag(frameDelta) // TODO
|
|
|
+
|
|
|
+ // Apply impact forces/inertia/impulses to objects that collided
|
|
|
+ //s.applyImpactForces(frameDelta)
|
|
|
+
|
|
|
+ // Update object positions based on calculated final speeds
|
|
|
+ s.updatePositions(frameDelta)
|
|
|
+
|
|
|
+}
|