/**
* 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.broadphase;
import net.smert.frameworkgl.collision.CollisionGameObject;
import net.smert.frameworkgl.math.AABB;
import net.smert.frameworkgl.math.AABBUtilities;
import net.smert.frameworkgl.math.Vector3f;
import net.smert.frameworkgl.opengl.pipeline.PipelineRenderDebugCallback;
import net.smert.frameworkgl.utils.ThreadLocalVars;
/**
*
* @author Jason Sorensen <sorensenj@smert.net>
*/
public class SimpleBroadphase implements BroadphaseAlgorithm {
private final static int NULL = -1;
private float displacementMultiplier;
private int capacity;
private int free;
private int size;
private final OverlappingPairCache overlappingPairCache;
private SimpleBroadphaseProxy[] proxies;
private final Vector3f margin;
public SimpleBroadphase(OverlappingPairCache overlappingPairCache) {
this.overlappingPairCache = overlappingPairCache;
displacementMultiplier = 2f;
capacity = 16;
free = 0;
size = 0;
proxies = new SimpleBroadphaseProxy[capacity];
margin = new Vector3f(.1f, .1f, .1f);
createProxies(free);
}
private SimpleBroadphaseProxy allocateProxy() {
// Expand proxies array
if (free == NULL) {
assert (size == capacity);
capacity *= 2;
free = size;
SimpleBroadphaseProxy[] newProxies = new SimpleBroadphaseProxy[capacity];
System.arraycopy(proxies, 0, newProxies, 0, size);
proxies = newProxies;
createProxies(free);
}
// Allocate proxy
int index = free;
SimpleBroadphaseProxy proxy = proxies[index];
free = proxy.next; // Next free proxy
size++;
// Defaults
proxy.index = index; // Mark proxy in use and also used to free proxy
proxy.next = NULL; // Last free proxy
return proxy;
}
private void createProxies(int index) {
assert (index >= 0);
assert (index < capacity);
// Create all proxies except the last one
for (int i = index; i < capacity - 1; i++) {
SimpleBroadphaseProxy proxy = new SimpleBroadphaseProxy();
proxy.index = NULL; // Mark not in use
proxy.next = i + 1; // Next free proxy
proxies[i] = proxy;
}
// Create last proxy
SimpleBroadphaseProxy proxy = new SimpleBroadphaseProxy();
proxy.index = NULL; // Mark not in use
proxy.next = NULL; // Last free proxy
proxies[capacity - 1] = proxy;
}
private void freeProxy(SimpleBroadphaseProxy proxy) {
assert (size > 0);
proxy.next = free; // Next free proxy
free = proxy.index;
size--;
proxy.index = NULL; // Mark proxy free
}
public float getDisplacementMultiplier() {
return displacementMultiplier;
}
public void setDisplacementMultiplier(float displacementMultiplier) {
this.displacementMultiplier = displacementMultiplier;
}
public Vector3f getMargin() {
return margin;
}
public void setMargin(float margin) {
this.margin.set(margin, margin, margin);
}
public void setMargin(float x, float y, float z) {
this.margin.set(x, y, z);
}
public void setMargin(Vector3f margin) {
this.margin.set(margin);
}
@Override
public BroadphaseProxy createProxy(CollisionGameObject collisionGameObject, int collisionGroup,
int collisionCollidesWith, AABB worldAabb) {
SimpleBroadphaseProxy proxy = allocateProxy();
// Set proxy AABB and add margin
proxy.aabb.set(worldAabb);
proxy.aabb.expand(margin);
proxy.collisionCollidesWith = collisionCollidesWith;
proxy.collisionGameObject = collisionGameObject;
proxy.collisionGroup = collisionGroup;
return proxy;
}
@Override
public void destroyDebugRender() {
}
@Override
public OverlappingPairCache getOverlappingPairCache() {
return overlappingPairCache;
}
@Override
public PipelineRenderDebugCallback getPipelineRenderDebugCallback() {
return null;
}
@Override
public boolean moveProxy(BroadphaseProxy broadphaseProxy, AABB worldAabb) {
// Downcast
SimpleBroadphaseProxy proxy = (SimpleBroadphaseProxy) broadphaseProxy;
// If the new AABB is still contained in the proxy's AABB then we do nothing
if (AABBUtilities.IsAabb0ContainedInAabb1(worldAabb, proxy.aabb)) {
return false;
}
// Temp vars from thread local storage
ThreadLocalVars vars = ThreadLocalVars.Get();
Vector3f worldCenterNew = vars.v3f1;
Vector3f worldCenterOld = vars.v3f2;
// Calculate displacement
worldCenterNew.set(worldAabb.getMin()).add(worldAabb.getMax()).multiply(.5f);
worldCenterOld.set(proxy.aabb.getMin()).add(proxy.aabb.getMax()).multiply(.5f);
worldCenterNew.subtract(worldCenterOld).multiply(displacementMultiplier);
// Set proxy AABB and add margin
proxy.aabb.set(worldAabb);
proxy.aabb.expand(margin);
// Expand AABB by the displacement vector
if (worldCenterNew.getX() < 0f) {
proxy.aabb.getMin().addX(worldCenterNew.getX());
} else {
proxy.aabb.getMax().addX(worldCenterNew.getX());
}
if (worldCenterNew.getY() < 0f) {
proxy.aabb.getMin().addY(worldCenterNew.getY());
} else {
proxy.aabb.getMax().addY(worldCenterNew.getY());
}
if (worldCenterNew.getZ() < 0f) {
proxy.aabb.getMin().addZ(worldCenterNew.getZ());
} else {
proxy.aabb.getMax().addZ(worldCenterNew.getZ());
}
// Release vars instance
vars.release();
return true;
}
@Override
public void removeProxy(BroadphaseProxy broadphaseProxy) {
SimpleBroadphaseProxy proxy = (SimpleBroadphaseProxy) broadphaseProxy;
freeProxy(proxy);
}
@Override
public void updateOverlappingPairs() {
for (int i = 0; i < proxies.length; i++) {
SimpleBroadphaseProxy proxy0 = proxies[i];
// Skip free proxy
if (proxy0.index == NULL) {
continue;
}
for (int j = i + 1; j < proxies.length; j++) {
SimpleBroadphaseProxy proxy1 = proxies[j];
// Skip free proxy
if (proxy1.index == NULL) {
continue;
}
// Test for intersection
if (AABBUtilities.DoesAabb0IntersectAabb1(proxy0.aabb, proxy1.aabb)) {
if (overlappingPairCache.findOverlappingPair(proxy0, proxy1) == null) {
overlappingPairCache.addOverlappingPair(proxy0, proxy1);
}
} else {
if (overlappingPairCache.findOverlappingPair(proxy0, proxy1) != null) {
overlappingPairCache.removeOverlappingPair(proxy0, proxy1);
}
}
}
}
}
}