package uk.org.floop.epq3d; import java.util.ArrayList; import java.util.Random; // stores both objects and other object collections public class ObjectCollection { public String name; public ArrayList<PointComp> points = new ArrayList<>(); //private Object3d collectionObject; public double boundingSphereR; public Point3D boundingSphereC; public ArrayList<ObjectCollection> subCollections = new ArrayList<>(); public ArrayList<Object3d> objects = new ArrayList<>(); public ArrayList<PointComp> initialiseAll(Random random){ for (ObjectCollection subCollection: subCollections) { ArrayList<PointComp> subPoints = subCollection.initialiseAll(random); points.addAll(subPoints); } initialise(random); return points; } public void initialise(Random random){ // init bounding sphere double distance = 0; double newDis; Point3D pointA = points.get(0).point; Point3D pointB = points.get(0).point; Vector3D distanceVec = new Vector3D(); // find the point which is the furthest away from arbitrary point 0 for (int i = 1; i < points.size(); i+=1) { distanceVec.createFrom2Points(points.get(0).point, points.get(i).point); newDis = distanceVec.getLength(); if (newDis >= distance){ pointA = points.get(i).point; distance = newDis; } } // find the point which is the furthest away the point which was found previously for (PointComp point : points) { distanceVec.createFrom2Points(pointA, point.point); newDis = distanceVec.getLength(); if (newDis >= distance){ pointB = point.point; distance = newDis;}} // define the bounding sphere in terms of these two furthest points boundingSphereC = new Point3D( (pointA.x + pointB.x)/2, (pointA.y + pointB.y)/2, (pointA.z + pointB.z)/2); boundingSphereR = Math.sqrt(distance)/2; // make the radius bigger depending on the ?? for (PointComp point: points) { distanceVec.createFrom2Points(boundingSphereC, point.point); distance = distanceVec.getLength(); boundingSphereR = Math.max(boundingSphereR, distance); } } public void invalidate(boolean invalidatePoints){ // the first level of object collections should contain all the points for the sublevels. // this means that we only need to invalidate them at the top level if(invalidatePoints){for (PointComp point: points) { point.invalidate(); }} for(ObjectCollection subCollection: subCollections){ subCollection.invalidate(false); } for(Object3d object: objects){ object.invalidate(); } } public void draw(drawData drawData, ArrayList<Integer> frustumInfo){ for (Object3d object: objects) { boolean draw = true; ArrayList<Integer> newFrustumInfo = new ArrayList<>(); // works the same as for collections. // see explanation below for (int planeId: frustumInfo) { double dist = drawData.frustumPlanes[planeId].getDistance(object.boundingSphereC); if(drawData.debugHud || true) { drawData.debugImg.getGraphics().drawString( "Dis: " + String.format("%.1f", dist + object.boundingSphereR), 500, 10 + 20 * planeId); } if (dist < -object.boundingSphereR){ draw = false; break; } else if (dist < object.boundingSphereR){ newFrustumInfo.add(planeId); } } if(draw){object.draw(drawData, newFrustumInfo);} } for(ObjectCollection collection: subCollections){ boolean draw = true; ArrayList<Integer> newFrustumInfo = new ArrayList<>(); for (int planeId: frustumInfo){ double dist = drawData.frustumPlanes[planeId].getDistance(collection.boundingSphereC); if(drawData.debugHud) { drawData.debugImg.getGraphics().drawString( "Dis: " + String.format("%.1f", dist), 500, 10 + 20 * planeId); } /* if the distance is less than (more negative than) the negative bounding sphere radius, we know that the bounding sphere lies completely outside the plane, so we don't need to draw it. */ if(dist < -collection.boundingSphereR){ draw = false; break; } /* if the distance is less than the _positive_ radius of the sphere, then we know that the sphere intersects that frustum plane. Therefore, it's worth checking that frustum plane further collections down the line. However, if the distance is greater than the positive radius, then we know that the sphere does not intersect that frustum plane at all, which means it is pointless to check it. */ else if(dist < collection.boundingSphereR) { newFrustumInfo.add(planeId); } // else if not technically required because of break but it makes me happy } if(draw){collection.draw(drawData, newFrustumInfo);} } } public void addCollection(){ subCollections.add(new ObjectCollection()); } public void addCollection(ObjectCollection newCollection){ subCollections.add(newCollection); } // making sure that all the point indices make sense might be a nightmare but ehh public void addObject(Object3d object){ PointComp[] newPoints = object.points; objects.add(object); // add the new object // merge lists for (PointComp newPoint : newPoints) { boolean found = false; // find out if any of the new points already exist in the list for (PointComp point : points) { if (newPoint == point) { found = true; break; } } if (!found) { points.add(newPoint); } } } }