/*
* JBox2D - A Java Port of Erin Catto's Box2D
*
* JBox2D homepage: http://jbox2d.sourceforge.net/
* Box2D homepage: http://www.box2d.org
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package org.jbox2d.collision;
import org.jbox2d.common.MathUtils;
import org.jbox2d.common.Settings;
//Updated to rev 56->108->136 of b2PairManager.cpp/.h
public class PairManager {
public static final int NULL_PAIR = Integer.MAX_VALUE;
public static final int NULL_PROXY = Integer.MAX_VALUE;
public static final int TABLE_CAPACITY = Settings.maxPairs;
public static final int TABLE_MASK = PairManager.TABLE_CAPACITY - 1;
public final Pair m_pairs[];
public int m_pairCount;
public int m_hashTable[];
//int m_next[];
public BroadPhase m_broadPhase;
public PairCallback m_callback;
public int m_freePair;
public final BufferedPair[] m_pairBuffer;
public int m_pairBufferCount;
public PairManager() {
m_pairs = new Pair[Settings.maxPairs];
m_hashTable = new int[PairManager.TABLE_CAPACITY];
//m_next = new int[Settings.maxPairs];
m_pairBuffer = new BufferedPair[Settings.maxPairs];
assert MathUtils.isPowerOfTwo(PairManager.TABLE_CAPACITY) == true;
assert PairManager.TABLE_CAPACITY >= Settings.maxPairs;
for (int i = 0; i < PairManager.TABLE_CAPACITY; ++i) {
m_hashTable[i] = PairManager.NULL_PAIR;
}
m_freePair = 0;
for (int i = 0; i < Settings.maxPairs; ++i) {
//m_next[i] = NULL_PAIR;
m_pairs[i] = new Pair();
m_pairs[i].proxyId1 = PairManager.NULL_PROXY;
m_pairs[i].proxyId2 = PairManager.NULL_PROXY;
m_pairs[i].userData = null;
m_pairs[i].status = 0;
m_pairs[i].next = i+1;
m_pairBuffer[i] = new BufferedPair();
}
m_pairs[Settings.maxPairs-1].next = PairManager.NULL_PAIR;
m_pairCount = 0;
m_pairBufferCount = 0;
}
public void initialize(final BroadPhase broadPhase, final PairCallback callback) {
m_broadPhase = broadPhase;
m_callback = callback;
}
// Add a pair and return the new pair. If the pair already exists,
// no new pair is created and the old one is returned.
public Pair addPair(int proxyId1, int proxyId2) {
// System.out.printf("PairManager.Add(%d, %d)\n", proxyId1, proxyId2);
if (proxyId1 > proxyId2) {
// integer primitive swap
proxyId1 += proxyId2;
proxyId2 = proxyId1 - proxyId2;
proxyId1 -= proxyId2;
}
final int hash = hash(proxyId1, proxyId2) & PairManager.TABLE_MASK;
Pair pair = find(proxyId1, proxyId2, hash);
if (pair != null) {
return pair;
}
assert(m_pairCount < Settings.maxPairs):"Too many pairs ("+m_pairCount+" shape AABB overlaps) - this usually means you have too many bodies, or you need to increase Settings.maxPairs.";
assert(m_freePair != PairManager.NULL_PAIR);
final int pairIndex = m_freePair;
pair = m_pairs[pairIndex];
m_freePair = pair.next;
pair.proxyId1 = proxyId1;
pair.proxyId2 = proxyId2;
pair.status = 0;
pair.userData = null;
pair.next = m_hashTable[hash];
m_hashTable[hash] = pairIndex;
++m_pairCount;
return pair;
}
// Remove a pair, return the pair's userData.
public Object removePair(int proxyId1, int proxyId2) {
assert(m_pairCount > 0);
if (proxyId1 > proxyId2) {
// integer primitive swap (safe for small ints)
proxyId1 += proxyId2;
proxyId2 = proxyId1 - proxyId2;
proxyId1 -= proxyId2;
}
final int hash = hash(proxyId1, proxyId2) & PairManager.TABLE_MASK;
//int* node = &m_hashTable[hash];
int derefnode = m_hashTable[hash];
boolean isHash = true;
int pderefnode = 0;
while (derefnode != PairManager.NULL_PAIR) {
if (equals(m_pairs[derefnode], proxyId1, proxyId2)) {
//int index = *node;
final int index = derefnode;
//*node = m_pairs[*node].next;
if (isHash) {
m_hashTable[hash] = m_pairs[m_hashTable[hash]].next;
} else {
m_pairs[pderefnode].next = m_pairs[derefnode].next;
}
final Pair pair = m_pairs[index];
final Object userData = pair.userData;
// Scrub
pair.next = m_freePair;
pair.proxyId1 = PairManager.NULL_PROXY;
pair.proxyId2 = PairManager.NULL_PROXY;
pair.userData = null;
pair.status = 0;
m_freePair = index;
--m_pairCount;
return userData;
} else {
//node = &m_pairs[*node].next;
pderefnode = derefnode;
derefnode = m_pairs[derefnode].next;
isHash = false;
}
}
assert(false) : "Attempted to remove a pair that does not exist";
return null;
}
/*
* As proxies are created and moved, many pairs are created and destroyed. Even worse, the same
* pair may be added and removed multiple times in a single time step of the physics engine. To reduce
* traffic in the pair manager, we try to avoid destroying pairs in the pair manager until the
* end of the physics step. This is done by buffering all the RemovePair requests. AddPair
* requests are processed immediately because we need the hash table entry for quick lookup.
*
* All user user callbacks are delayed until the buffered pairs are confirmed in Commit.
* This is very important because the user callbacks may be very expensive and client logic
* may be harmed if pairs are added and removed within the same time step.
*/
/**
* Buffer a pair for addition.
* We may add a pair that is not in the pair manager or pair buffer.
* We may add a pair that is already in the pair manager and pair buffer.
* If the added pair is not a new pair, then it must be in the pair buffer (because RemovePair was called).
*/
public void addBufferedPair(final int id1, final int id2) {
assert(id1 != PairManager.NULL_PROXY && id2 != PairManager.NULL_PROXY);
assert(m_pairBufferCount < Settings.maxPairs);
final Pair pair = addPair(id1, id2);
// If this pair is not in the pair buffer ...
if (pair.isBuffered() == false) {
// This must be a newly added pair.
assert(pair.isFinal() == false);
// Add it to the pair buffer.
pair.setBuffered();
m_pairBuffer[m_pairBufferCount].proxyId1 = pair.proxyId1;
m_pairBuffer[m_pairBufferCount].proxyId2 = pair.proxyId2;
++m_pairBufferCount;
assert(m_pairBufferCount <= m_pairCount);
}
// Confirm this pair for the subsequent call to Commit.
pair.clearRemoved();
if (BroadPhase.s_validate){
validateBuffer();
}
}
/**
* Buffer a pair for removal.
* @param id1
* @param id2
*/
public void removeBufferedPair(final int id1, final int id2) {
assert(id1 != PairManager.NULL_PROXY && id2 != PairManager.NULL_PROXY);
assert(m_pairBufferCount < Settings.maxPairs);
final Pair pair = find(id1, id2);
if (pair == null) {
// The pair never existed. This is legal (due to collision filtering).
return;
}
// If this pair is not in the pair buffer ...
if (pair.isBuffered() == false) {
// This must be an old pair.
assert(pair.isFinal() == true);
pair.setBuffered();
m_pairBuffer[m_pairBufferCount].proxyId1 = pair.proxyId1;
m_pairBuffer[m_pairBufferCount].proxyId2 = pair.proxyId2;
++m_pairBufferCount;
assert(m_pairBufferCount <= m_pairCount);
}
pair.setRemoved();
if (BroadPhase.s_validate) {
validateBuffer();
}
}
/**
* commits the proxies
*/
public void commit() {
//System.out.println("Entering commit");
int removeCount = 0;
final Proxy[] proxies = m_broadPhase.m_proxyPool;
for (int i = 0; i < m_pairBufferCount; ++i) {
final Pair pair = find(m_pairBuffer[i].proxyId1, m_pairBuffer[i].proxyId2);
assert(pair.isBuffered());
pair.clearBuffered();
assert(pair.proxyId1 < Settings.maxProxies && pair.proxyId2 < Settings.maxProxies);
final Proxy proxy1 = proxies[pair.proxyId1];
final Proxy proxy2 = proxies[pair.proxyId2];
assert(proxy1.isValid());
assert(proxy2.isValid());
if (pair.isRemoved()) {
// It is possible a pair was added then removed before a commit. Therefore,
// we should be careful not to tell the user the pair was removed when the
// the user didn't receive a matching add.
if (pair.isFinal() == true) {
m_callback.pairRemoved(proxy1.userData, proxy2.userData, pair.userData);
}
// Store the ids so we can actually remove the pair below.
m_pairBuffer[removeCount].proxyId1 = pair.proxyId1;
m_pairBuffer[removeCount].proxyId2 = pair.proxyId2;
//System.out.println("Buffering "+pair.proxyId1 + ", "+pair.proxyId2 + " for removal");
++removeCount;
} else {
assert(m_broadPhase.testOverlap(proxy1, proxy2) == true);
if (pair.isFinal() == false) {
pair.userData = m_callback.pairAdded(proxy1.userData, proxy2.userData);
pair.setFinal();
}
// if ( ((Shape)proxy1.userData).getBody().isStatic() &&
// ((Shape)proxy2.userData).getBody().isStatic() ) {
// if (pair.isFinal() == true) {
// m_callback.pairRemoved(proxy1.userData, proxy2.userData, pair.userData);
// }
//
// // Store the ids so we can actually remove the pair below.
// m_pairBuffer[removeCount].proxyId1 = pair.proxyId1;
// m_pairBuffer[removeCount].proxyId2 = pair.proxyId2;
// //System.out.println("Buffering "+pair.proxyId1 + ", "+pair.proxyId2 + " for removal");
// ++removeCount;
// }
}
}
for (int i = 0; i < removeCount; ++i) {
removePair(m_pairBuffer[i].proxyId1, m_pairBuffer[i].proxyId2);
// System.out.println("Remaining pairs: ");
// for (int j=0; j<m_pairCount; ++j) {
// System.out.println(" "+m_pairs[j].proxyId1 + ", " + m_pairs[j].proxyId2);
// }
}
m_pairBufferCount = 0;
if (BroadPhase.s_validate) {
validateTable();
}
}
/**
* Unimplemented - for debugging purposes only in C++ version
*/
public void validateBuffer() {
//#ifdef _DEBUG
// assert(m_pairBufferCount <= m_pairCount);
//
// std::sort(m_pairBuffer, m_pairBuffer + m_pairBufferCount);
//
// for (int32 i = 0; i < m_pairBufferCount; ++i)
// {
// if (i > 0)
// {
// b2Assert(Equals(m_pairBuffer[i], m_pairBuffer[i-1]) == false);
// }
//
// b2Pair* pair = Find(m_pairBuffer[i].proxyId1, m_pairBuffer[i].proxyId2);
// b2Assert(pair->IsBuffered());
//
// b2Assert(pair->proxyId1 != pair->proxyId2);
// b2Assert(pair->proxyId1 < b2_maxProxies);
// b2Assert(pair->proxyId2 < b2_maxProxies);
//
// b2Proxy* proxy1 = m_broadPhase->m_proxyPool + pair->proxyId1;
// b2Proxy* proxy2 = m_broadPhase->m_proxyPool + pair->proxyId2;
//
// b2Assert(proxy1->IsValid() == true);
// b2Assert(proxy2->IsValid() == true);
// }
//#endif
}
/**
* For debugging
*/
public void validateTable() {
// #ifdef _DEBUG
for (int i = 0; i < PairManager.TABLE_CAPACITY; ++i) {
int index = m_hashTable[i];
while (index != PairManager.NULL_PAIR) {
final Pair pair = m_pairs[index];
assert(pair.isBuffered() == false);
assert(pair.isFinal() == true);
assert(pair.isRemoved() == false);
assert(pair.proxyId1 != pair.proxyId2);
assert(pair.proxyId1 < Settings.maxProxies);
assert(pair.proxyId2 < Settings.maxProxies);
final Proxy proxy1 = m_broadPhase.m_proxyPool[pair.proxyId1];
final Proxy proxy2 = m_broadPhase.m_proxyPool[pair.proxyId2];
assert(proxy1.isValid() == true);
assert(proxy2.isValid() == true);
assert(m_broadPhase.testOverlap(proxy1, proxy2) == true);
index = pair.next;
}
}
// #endif
}
/**
* finds the pair with the given hash
* @param proxyId1
* @param proxyId2
* @param hash
* @return
*/
public Pair find(final int proxyId1, final int proxyId2, final int hash) {
int index = m_hashTable[hash];
while (index != PairManager.NULL_PAIR
&& equals(m_pairs[index], proxyId1, proxyId2) == false) {
index = m_pairs[index].next;
}
//System.out.println("Found at index "+index);
if (index == PairManager.NULL_PAIR) {
//System.out.println("Which is null...");
return null;
}
assert index < Settings.maxPairs;
return m_pairs[index];
}
/**
* finds the pair, creates hash and uses {@link #find(int, int, int)}
* @param proxyId1
* @param proxyId2
* @return
*/
public Pair find(int proxyId1, int proxyId2) {
if (proxyId1 > proxyId2) {
final int tmp = proxyId1;
proxyId1 = proxyId2;
proxyId2 = tmp;
}
final int hash = hash(proxyId1, proxyId2) & PairManager.TABLE_MASK;
return find(proxyId1, proxyId2, hash);
}
// public int findIndex(int proxyId1, int proxyId2) {
// // System.out.printf("PairManager.FindIndex(%d, %d)\n", proxyId1,
// // proxyId2);
// if (proxyId1 > proxyId2) {
// // integer primitive swap
// proxyId1 += proxyId2;
// proxyId2 = proxyId1 - proxyId2;
// proxyId1 -= proxyId2;
// }
//
// int hash = hash(proxyId1, proxyId2) & TABLE_MASK;
//
// int index = m_hashTable[hash];
// while (index != NULL_PAIR
// && equals(m_pairs[index], proxyId1, proxyId2) == false) {
// index = m_next[index];
// }
//
// if (index == NULL_PAIR) {
// return -1;
// }
//
// assert index < m_pairCount;
//
// return index;
// }
//
// public int getCount() {
// return m_pairCount;
// }
//
// public Pair[] getPairs() {
// return m_pairs;
// }
//
//
//
// private int findIndex(int proxyId1, int proxyId2, int hash) {
// int index = m_hashTable[hash];
//
// while (index != NULL_PAIR
// && equals(m_pairs[index], proxyId1, proxyId2) == false) {
// index = m_next[index];
// }
//
// if (index == NULL_PAIR) {
// return -1;
// }
//
// assert index < m_pairCount;
//
// return index;
// }
private final int hash(final int proxyId1, final int proxyId2) {
// djm: this operation here is pretty self explanitory,
// so i don't think I need to describe what's happening,
// or what the result is
int key = (proxyId2 << 16) | proxyId1;
key = ~key + (key << 15);
key = key ^ (key >>> 12);
key = key + (key << 2);
key = key ^ (key >>> 4);
key = key * 2057;
key = key ^ (key >>> 16);
return key;
}
/**
* returns if the pair has the two proxy id's
* @param pair
* @param proxyId1
* @param proxyId2
* @return
*/
public final boolean equals(final Pair pair, final int proxyId1, final int proxyId2) {
return pair.proxyId1 == proxyId1 && pair.proxyId2 == proxyId2;
}
/**
* returns if the pairs have the same proxy id's
* @param pair1
* @param pair2
* @return
*/
public final boolean equals(final BufferedPair pair1, final BufferedPair pair2) {
return pair1.proxyId1 == pair2.proxyId1 && pair1.proxyId2 == pair2.proxyId2;
}
/**
* For sorting. Returns if the first pair's proxyid's are less than the
* second pair, starting with proxyId1
* @param pair1
* @param pair2
* @return
*/
public final boolean minor (final BufferedPair pair1, final BufferedPair pair2){
if (pair1.proxyId1 < pair2.proxyId1) {
return true;
}
if (pair1.proxyId1 == pair2.proxyId1) {
return pair1.proxyId2 < pair2.proxyId2;
}
return false;
}
}