func fire() { const cubesToMake = 4 var offset float32 = 0.0 if len(cubes) > 0 && (len(cubes)/cubesToMake)%2 >= 1 { offset = 0.75 } for i := 0; i < cubesToMake; i++ { e := new(Entity) e.Node = CreateCube(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5) e.Node.Shader = diffuseShader e.Node.Color = mgl.Vec4{1.0, 1.0, 1.0, 1.0} e.Node.Location = mgl.Vec3{float32(i*2.0-cubesToMake/2) - 0.5 + offset, 10.0, 0.0} e.Node.Tex0 = crateTexture // create the collision box for the the cube cubeCollider := cubez.NewCollisionCube(nil, m.Vector3{0.5, 0.5, 0.5}) cubeCollider.Body.Position = m.Vector3{m.Real(i*2.0-cubesToMake/2) - 0.5 + m.Real(offset), 10.0, 0.0} cubeCollider.Body.SetMass(8.0) cubeCollider.Body.CanSleep = true var cubeInertia m.Matrix3 cubeInertia.SetBlockInertiaTensor(&cubeCollider.HalfSize, 8.0) cubeCollider.Body.SetInertiaTensor(&cubeInertia) cubeCollider.Body.CalculateDerivedData() cubeCollider.CalculateDerivedData() e.Collider = cubeCollider cubes = append(cubes, e) } }
// fireCharMovePosX is the event that moves the character 'right' in standard view. func fireCharMovePosX(delta float32) { movement := locPerSec * delta // FIXME: for now we add the force directly. in future, move this to game manager // and have it notify server of update. playerEnt := gameManager.GetLocalPlayer() body := playerEnt.Collider.GetBody() body.AddVelocity(&physmath.Vector3{physmath.Real(movement), physmath.Real(-movement * 0.15), 0.0}) }
func buildCollider(cb *collisionBlock) physics.Collider { length := physmath.Real(cb.EndZ - cb.StartZ + 1) halfLength := physmath.Real(length) / 2.0 // create the collision box for the the cube cubeCollider := physics.NewCollisionCube(nil, physmath.Vector3{0.5, 0.5, halfLength}) cubeCollider.Body.Position = physmath.Vector3{physmath.Real(cb.X) + 0.5, physmath.Real(cb.Y) + 0.5, physmath.Real(cb.StartZ) + halfLength} cubeCollider.Body.SetInfiniteMass() cubeCollider.Body.CanSleep = false cubeCollider.Body.CalculateDerivedData() cubeCollider.CalculateDerivedData() return cubeCollider }
func updateCallback(delta float64) { updateObjects(delta) foundContacts, contacts := generateContacts(delta) if foundContacts { cubez.ResolveContacts(len(contacts)*8, contacts, m.Real(delta)) } }
// Integrate takes all of the forces accumulated in the RigidBody and // change the Position and Orientation of the object. func (body *RigidBody) Integrate(duration m.Real) { if body.IsAwake == false { return } // calculate linear acceleration from force inputs. body.lastFrameAccelleration = body.Acceleration body.lastFrameAccelleration.AddScaled(&body.forceAccum, body.inverseMass) // calculate angular acceleration from torque inputs angularAcceleration := body.inverseInertiaTensorWorld.MulVector3(&body.torqueAccum) // adjust velocities // update linear velocity from both acceleration and impulse body.Velocity.AddScaled(&body.lastFrameAccelleration, duration) // update angular velocity from both acceleration and impulse body.Rotation.AddScaled(&angularAcceleration, duration) // impose drag body.Velocity.MulWith(m.Real(math.Pow(float64(body.LinearDamping), float64(duration)))) body.Rotation.MulWith(m.Real(math.Pow(float64(body.AngularDamping), float64(duration)))) // adjust positions // update linear positions body.Position.AddScaled(&body.Velocity, duration) //update angular position body.Orientation.AddScaledVector(&body.Rotation, duration) // normalize the orientation and update the matrixes with the new position and orientation body.CalculateDerivedData() body.ClearAccumulators() // update the kinetic energy store and possibly put the body to sleep if body.CanSleep { currentMotion := body.Velocity.Dot(&body.Velocity) + body.Rotation.Dot(&body.Rotation) bias := m.Real(math.Pow(0.5, float64(duration))) body.motion = bias*body.motion + (1.0-bias)*currentMotion if body.motion < sleepEpsilon { body.SetAwake(false) } else if body.motion > 10*sleepEpsilon { body.motion = 10 * sleepEpsilon } } }
// update object locations func updateObjects(delta float64) { // for now there's only one box to update body := cube.Collider.GetBody() body.Integrate(m.Real(delta)) cube.Collider.CalculateDerivedData() // for now we hack in the position and rotation of the collider into the renderable SetGlVector3(&cube.Node.Location, &body.Position) SetGlQuat(&cube.Node.LocalRotation, &body.Orientation) for _, bullet := range bullets { bulletBody := bullet.Collider.GetBody() bulletBody.Integrate(m.Real(delta)) bullet.Collider.CalculateDerivedData() SetGlVector3(&bullet.Node.Location, &bulletBody.Position) SetGlQuat(&bullet.Node.LocalRotation, &bulletBody.Orientation) } }
// update object locations func updateObjects(delta float64) { for _, cube := range cubes { body := cube.Collider.GetBody() body.Integrate(m.Real(delta)) cube.Collider.CalculateDerivedData() // for now we hack in the position and rotation of the collider into the renderable SetGlVector3(&cube.Node.Location, &body.Position) SetGlQuat(&cube.Node.LocalRotation, &body.Orientation) } }
// Constructs an arbitrary orthonormal basis for the contact. It's stored // as a 3x3 matrix where each column is a vector for an axis. The x axis // is based off of the contact normal and the y and z axis will be generated // so that they are at right angles to it. func (c *Contact) calculateContactBasis() { var contactTangentY m.Vector3 var contactTangentZ m.Vector3 absContactNormalX := m.RealAbs(c.ContactNormal[0]) absContactNormalY := m.RealAbs(c.ContactNormal[1]) // check whether the z axis is nearer to the x or y axis if absContactNormalX > absContactNormalY { // generate a scaling factor to ensure results are normalized s := m.Real(1.0) / m.RealSqrt(c.ContactNormal[2]*c.ContactNormal[2]+c.ContactNormal[0]*c.ContactNormal[0]) // the new x axis is at right angles to the world y axis contactTangentY[0] = c.ContactNormal[2] * s contactTangentY[1] = 0 contactTangentY[2] = c.ContactNormal[0] * -s // the new y axis is at right angles to the new x and z axes contactTangentZ[0] = c.ContactNormal[1] * contactTangentY[0] contactTangentZ[1] = c.ContactNormal[2]*contactTangentY[0] - c.ContactNormal[0]*contactTangentY[2] contactTangentZ[2] = -c.ContactNormal[1] * contactTangentY[0] } else { // generate a scaling factor to ensure results are normalized s := m.Real(1.0) / m.RealSqrt(c.ContactNormal[2]*c.ContactNormal[2]+c.ContactNormal[1]*c.ContactNormal[1]) // the new x axis is at right angles to the world y axis contactTangentY[0] = 0 contactTangentY[1] = -c.ContactNormal[2] * s contactTangentY[2] = c.ContactNormal[1] * s // the new y axis is at right angles to the new x and z axes contactTangentZ[0] = c.ContactNormal[1]*contactTangentY[2] - c.ContactNormal[2]*contactTangentY[1] contactTangentZ[1] = -c.ContactNormal[0] * contactTangentY[2] contactTangentZ[2] = c.ContactNormal[0] * contactTangentY[1] } // now set the contactToWorld matrix based off of these three vectors c.contactToWorld.SetComponents(&c.ContactNormal, &contactTangentY, &contactTangentZ) }
// SyncToColliders syncs the location and orientation to the collision object // and the bounding box. func (e *Entity) SyncToColliders() { if e.Collider == nil { return } body := e.Collider.GetBody() body.Position[0] = physmath.Real(e.Location[0]) body.Position[1] = physmath.Real(e.Location[1]) body.Position[2] = physmath.Real(e.Location[2]) body.Orientation[0] = physmath.Real(e.Rotation.W) body.Orientation[1] = physmath.Real(e.Rotation.V[0]) body.Orientation[2] = physmath.Real(e.Rotation.V[1]) body.Orientation[3] = physmath.Real(e.Rotation.V[2]) e.BoundingBox.Offset[0] = e.Location[0] e.BoundingBox.Offset[1] = e.Location[1] e.BoundingBox.Offset[2] = e.Location[2] }
// RunCollider updates the physics collider for the entity if it's present func (e *Entity) RunCollider(delta float32) { // if there's no collider just return if e.Collider == nil { return } // for now there's only one box to update body := e.Collider.GetBody() // we don't process entities with infinite mass if body.HasFiniteMass() == false { return } body.Integrate(physmath.Real(delta)) e.Collider.CalculateDerivedData() // pull the positions from the collider into the entity e.SyncFromCollider() // and then push them to the renderable e.SyncToRenderable() }
// UpdatePhysics makes sure the physics objects are updated. func (gm *LocalGameManager) UpdatePhysics(frameDelta float32) { if gm.physicsEnabled == false { return } physicsNow := time.Now() physicsDelta := float32(physicsNow.Sub(gm.physicsLastTime).Seconds()) // sync physics to no more than 60 fps if physicsDelta < 1.0/60.0 { return } // conversely, if it's been too long, we just act like nothing happened ... // what's up with that? if physicsDelta > 0.50 { // try again next time to see if we can process the world gm.physicsLastTime = physicsNow groggy.Logsf("INFO", "LGM:UpdatePhycis() had to skip a physics frame due to excessive lag in delta time (%f)", physicsDelta) return } // update the collider in each entity gm.entityManager.Map(func(e *entity.Entity) { if e.Collider != nil { e.RunCollider(physicsDelta) } }) // generate any contacts //groggy.Logsf("DEBUG", "LGM: UpdatePhysics: checking start") var contacts []*physics.Contact gm.entityManager.Map(func(e *entity.Entity) { if e.Collider != nil { // test collisions against other entities // TODO: do better coarse collision detection gm.entityManager.Map(func(otherEnt *entity.Entity) { if e != otherEnt && otherEnt.Collider != nil { //groggy.Logsf("DEBUG", "\tLGM: UpdatePhysics: checking entitys:") //groggy.Logsf("DEBUG", "\t\tentity %d(%s) @ (%v)", e.Id, e.Name, e.Collider.GetTransform()) //groggy.Logsf("DEBUG", "\t\t%v", e.Collider.GetBody()) //groggy.Logsf("DEBUG", "\t\tentity %d(%s) @ (%v)", otherEnt.Id, otherEnt.Name, otherEnt.Collider.GetTransform()) //groggy.Logsf("DEBUG", "\t\t%v", otherEnt.Collider.GetBody()) _, contacts = physics.CheckForCollisions(e.Collider, otherEnt.Collider, contacts) } }) // stop here with the infinite mass, non-movable entities if e.Collider.GetBody().HasFiniteMass() == false { return } // Now check the entity against the landscape chunks // TODO: super lame selections of chunks and very wasteful intX, intZ := int(e.Location[0]), int(e.Location[2]) chunks := gm.landscapeMan.GetChunksFor(intX, intZ) chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX, intZ+1)...) chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX, intZ-1)...) chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX+1, intZ-1)...) chunks = append(chunks, gm.landscapeMan.GetChunksFor(intX-1, intZ-1)...) for _, chunk := range chunks { // check collision against the relevant landscape collisions for _, chunkCollider := range chunk.Colliders { _, contacts = physics.CheckForCollisions(e.Collider, chunkCollider, contacts) } } } }) // resolve the contacts if contacts != nil && len(contacts) > 0 { physics.ResolveContacts(len(contacts)*2, contacts, physmath.Real(physicsDelta)) } gm.physicsLastTime = physicsNow }