package worldData;
import gl.GLCamera;
import gl.HasPosition;
import gl.Renderable;
import javax.microedition.khronos.opengles.GL10;
import system.Container;
import util.EfficientList;
import util.QuadTree;
import util.QuadTree.ResultListener;
import util.Vec;
import android.util.Log;
/**
* This Container structure uses a Quadtree (
* http://en.wikipedia.org/wiki/Quadtree )
*
* @author Spobo
*
*/
public class RenderQuadList implements RenderableEntity,
Container<RenderableEntity> {
private static final String LOG_TAG = "RenderQuadList";
private float myRenderDistance;
private float myRecalcDistanceMin;
private float myRecalcDistanceMax;
private EfficientList<RenderableEntity> allItems;
private QuadTree<RenderableEntity> tree;
@SuppressWarnings("rawtypes")
private ResultListener itemsListener;
private volatile EfficientList<RenderableEntity> itemsInRange;
private float oldX;
private float oldY;
private GLCamera myGlCamera;
private boolean wasClearedAtLeastOnce = false;
private Updateable myParent;
/**
* @param glCamera
* @param renderDistance
* @param recalcDistance
* If you pass 10 here then the list of objects currently updated
* will be refreshed every 10 meters when the user moves around
*/
public RenderQuadList(GLCamera glCamera, float renderDistance,
float recalcDistance) {
myGlCamera = glCamera;
myRecalcDistanceMax = recalcDistance;
myRecalcDistanceMin = -recalcDistance;
myRenderDistance = renderDistance;
itemsListener = new QuadTree<RenderableEntity>().new ResultListener() {
@Override
public void onResult(RenderableEntity myValue) {
itemsInRange.add(myValue);
}
};
}
public EfficientList<RenderableEntity> getItems(Vec position,
float maxDistance) {
final EfficientList<RenderableEntity> result = new EfficientList<RenderableEntity>();
if (tree != null) {
tree.findInArea(tree.new ResultListener() {
@Override
public void onResult(RenderableEntity myValue) {
result.add(myValue);
}
}, position.x, position.y, maxDistance);
}
return result;
}
@Override
public Updateable getMyParent() {
return myParent;
}
@Override
public void setMyParent(Updateable parent) {
myParent = parent;
}
@Override
public boolean update(float timeDelta, Updateable parent) {
setMyParent(parent);
Vec p = myGlCamera.getPosition();
EfficientList<RenderableEntity> list = getList(p.x, p.y);
for (int i = 0; i < list.myLength; i++) {
RenderableEntity obj = list.get(i);
if (obj != null)
obj.update(timeDelta, this);
}
return true;
}
@Override
public boolean accept(Visitor visitor) {
if (allItems != null)
for (int i = 0; i < allItems.myLength; i++) {
allItems.get(i).accept(visitor);
}
return true;
}
@Override
public void render(GL10 gl, Renderable parent) {
Vec p = myGlCamera.getPosition();
EfficientList<RenderableEntity> list = getList(p.x, p.y);
for (int i = 0; i < list.myLength; i++) {
RenderableEntity obj = list.get(i);
if (obj != null)
obj.render(gl, this);
}
}
@SuppressWarnings("unchecked")
private synchronized EfficientList<RenderableEntity> getList(float x,
float y) {
if (itemsInRange != null
&& needsNoRecalculation(x - oldX, myRecalcDistanceMin,
myRecalcDistanceMax)
&& needsNoRecalculation(y - oldY, myRecalcDistanceMin,
myRecalcDistanceMax)) {
return itemsInRange;
} else {
if (itemsInRange == null)
itemsInRange = new EfficientList<RenderableEntity>();
else
itemsInRange.clear();
oldX = x;
oldY = y;
refreshItemsInRangeList();
return itemsInRange;
}
}
/**
* Call this method to update the list of objects that are rendered and
* updated after you modified the quad tree
*/
public void refreshList() {
if (itemsInRange == null)
itemsInRange = new EfficientList<RenderableEntity>();
else
itemsInRange.clear();
refreshItemsInRangeList();
}
private void refreshItemsInRangeList() {
if (tree != null && itemsInRange != null)
tree.findInArea(itemsListener, oldX, oldY, myRenderDistance);
}
private boolean needsNoRecalculation(float v, float min, float max) {
return (min < v) && (v < max);
}
@Override
public void clear() {
if (tree != null) {
allItems.clear();
tree.clear();
wasClearedAtLeastOnce = true;
refreshItemsInRangeList();
}
}
@Override
public void removeEmptyItems() {
if (allItems != null) {
for (int i = 0; i < allItems.myLength; i++) {
if (allItems.get(i) instanceof Container) {
Container c = (Container) allItems.get(i);
if (c.isCleared())
remove((RenderableEntity) c);
}
}
}
}
@Override
public boolean isCleared() {
if (allItems != null) {
return allItems.isEmpty() && wasClearedAtLeastOnce;
}
return false;
}
@Override
public int length() {
if (allItems != null) {
return allItems.myLength;
}
return 0;
}
@Override
public EfficientList<RenderableEntity> getAllItems() {
return allItems;
}
@Override
public boolean add(RenderableEntity newElement) {
if (newElement instanceof HasPosition)
return add((HasPosition) newElement);
Log.w(LOG_TAG, "Object was not added to the RenderQuadList "
+ "because it had no HasPosition interface!");
return false;
}
private boolean add(HasPosition x) {
Vec pos = x.getPosition();
if (pos != null) {
addToTree(x, pos);
addToAllItemsList(x);
// refreshList(); //TODO?
return true;
}
return false;
}
private void addToAllItemsList(HasPosition x) {
if (allItems == null)
allItems = new EfficientList<RenderableEntity>();
allItems.add((RenderableEntity) x);
}
private boolean insertInAllItemsList(int pos, RenderableEntity item) {
if (allItems == null)
allItems = new EfficientList<RenderableEntity>();
return allItems.insert(pos, item);
}
private void addToTree(HasPosition x, Vec pos) {
if (tree == null)
tree = new QuadTree<RenderableEntity>();
tree.add(pos.x, pos.y, (RenderableEntity) x);
refreshItemsInRangeList();
}
@Override
public boolean remove(RenderableEntity x) {
if (tree != null) {
boolean rt = tree.remove(x);
boolean rl = allItems.remove(x);
refreshItemsInRangeList();
if ((rt && !rl) || (rl && !rt))
Log.e(LOG_TAG,
"Inconsistency in tree und allItems-list while removing!");
if (rt && rl)
return true;
}
return false;
}
/**
* The current internal tree will be deleted and recreated. This is
* expensive so do not call this too often!
*/
public void rebuildTree() {
if (tree != null) {
tree.clear();
for (int i = 0; i < allItems.myLength; i++) {
this.add(allItems.get(i));
}
}
}
@Override
public boolean insert(int pos, RenderableEntity item) {
if (item instanceof HasPosition) {
boolean result = insertInAllItemsList(pos, item);
if (result)
addToTree((HasPosition) item,
((HasPosition) item).getPosition());
return result;
}
Log.w(LOG_TAG, "Object was not inserted into the RenderQuadList "
+ "because it had no HasPosition interface!");
return false;
}
}