/** * Copyright 2012 Jason Sorensen (sorensenj@smert.net) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package net.smert.frameworkgl.collision.response; import java.util.ArrayList; import java.util.List; import net.smert.frameworkgl.collision.CollisionGameObject; import net.smert.frameworkgl.collision.narrowphase.Contact; import net.smert.frameworkgl.collision.narrowphase.ContactData; import net.smert.frameworkgl.math.Vector3f; import net.smert.frameworkgl.utils.ThreadLocalVars; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class CollisionResolver { private final CollisionResponseFilterCallback collisionResponseFilterCallback; private final List<Contact> contactsToFree; public CollisionResolver(CollisionResponseFilterCallback collisionResponseFilterCallback) { this.collisionResponseFilterCallback = collisionResponseFilterCallback; contactsToFree = new ArrayList<>(); } private void resolveCollision(Contact contact) { float penetration = contact.penetration; CollisionGameObject collisionGameObject0 = contact.collisionGameObject0; CollisionGameObject collisionGameObject1 = contact.collisionGameObject1; Vector3f normal = contact.normal; // From collision objects float restitution = Math.min(collisionGameObject0.getRestitution(), collisionGameObject1.getRestitution()); float totalInverseMass = collisionGameObject0.getInverseMass() + collisionGameObject1.getInverseMass(); Vector3f linearVelocity0 = collisionGameObject0.getLinearVelocity(); Vector3f linearVelocity1 = collisionGameObject1.getLinearVelocity(); // Temp vars from thread local storage ThreadLocalVars vars = ThreadLocalVars.Get(); Vector3f impulse = vars.v3f0; Vector3f positionCorrection = vars.v3f1; Vector3f relativeVelocity = vars.v3f2; // Relative velocity from collisionGameObject0 to collisionGameObject1 relativeVelocity.set(linearVelocity1).subtract(linearVelocity0); float velocityOnNormal = relativeVelocity.dot(normal); // Calculate impulse // j = -(1 + e) * ((Vb - Va) . N) / ( (1 / MASSa) + (1 / MASSb) ) float j = -(1f + restitution) * velocityOnNormal / totalInverseMass; impulse.set(normal).multiply(j); // Apply impulse to both objects but do collisionGameObject1 first // since the impulse to collisionGameObject0 needs to be inverted. collisionGameObject1.applyImpulse(impulse); collisionGameObject0.applyImpulse(impulse.invert()); // Correct position positionCorrection.set(normal).multiply(penetration / totalInverseMass); // Apply position correction to both objects collisionGameObject0.applyPositionCorrection(positionCorrection); collisionGameObject1.applyPositionCorrection(positionCorrection.invert()); // Release vars instance vars.release(); } public void processContacts(ContactData contactData) { Contact[] contacts = contactData.getContacts(); for (int i = 0; i < contacts.length; i++) { Contact contact = contacts[i]; // Skip free contact if (contact.index == ContactData.NULL) { continue; } // Resolve collision if (collisionResponseFilterCallback.needsCollisionResponse(contact.collisionGameObject0, contact.collisionGameObject1)) { resolveCollision(contact); } // Save contact to list contactsToFree.add(contact); } // Notify collision game objects that there was a collision for (Contact contact : contactsToFree) { CollisionGameObject collisionGameObject0 = contact.collisionGameObject0; CollisionGameObject collisionGameObject1 = contact.collisionGameObject1; collisionGameObject0.collidedWith(collisionGameObject1, contact); collisionGameObject1.collidedWith(collisionGameObject0, contact); } // Free all contacts for (Contact contact : contactsToFree) { contactData.freeContact(contact); } // Clear list contactsToFree.clear(); } }