Newer
Older
EPQ-3D-renderer / src / main / java / uk / org / floop / epq3d / ObjectCollection.java
@cory cory on 15 Apr 2023 6 KB Final changes (idk)
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(drawData drawData, 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(drawData, false);
        }
        for(Object3d object:
        objects){
            object.invalidate(drawData);
        }
    }

    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);
            }
        }
    }
}