All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yungnickyoung.minecraft.yungsapi.util.BoxOctree Maven / Gradle / Ivy

There is a newer version: 1.21.1-NeoForge-5.1.2
Show newest version
package com.yungnickyoung.minecraft.yungsapi.util;

import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.AABB;

import java.util.ArrayList;
import java.util.List;

/**
 * Thanks to TelepathicGrunt for letting me use this octree implementation!
 * Original: https://github.com/TelepathicGrunt/RepurposedStructures/blob/latest-released/src/main/java/com/telepathicgrunt/repurposedstructures/utils/BoxOctree.java
 */
public class BoxOctree {
    private static final int subdivideThreshold = 10;
    private static final int maximumDepth = 3;

    private final AABB boundary;
    private final Vec3i size;
    private final int depth;
    private final List innerBoxes = new ArrayList<>();
    private final List childrenOctants = new ArrayList<>();

    public BoxOctree(AABB axisAlignedBB) {
        this(axisAlignedBB, 0);
    }

    private BoxOctree(AABB axisAlignedBB, int parentDepth) {
        boundary = axisAlignedBB.move(0, 0, 0); // deep copy
        size = new Vec3i((int) boundary.getXsize(),(int) boundary.getYsize(),(int) boundary.getZsize());
        depth = parentDepth + 1;
    }

    private void subdivide() {
        if (!childrenOctants.isEmpty()) {
            throw new UnsupportedOperationException("YUNG's API - Tried to subdivide when there are already children octants.");
        }

        int halfXSize = size.getX() / 2;
        int halfYSize = size.getY() / 2;
        int halfZSize = size.getZ() / 2;

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX, boundary.minY, boundary.minZ,
                boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX + halfXSize, boundary.minY, boundary.minZ,
                boundary.maxX, boundary.minY + halfYSize, boundary.minZ + halfZSize),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX, boundary.minY + halfYSize, boundary.minZ,
                boundary.minX + halfXSize, boundary.maxY, boundary.minZ + halfZSize),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX, boundary.minY, boundary.minZ + halfZSize,
                boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.maxZ),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ,
                boundary.maxX, boundary.maxY, boundary.minZ + halfZSize),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX, boundary.minY + halfYSize, boundary.minZ + halfZSize,
                boundary.minX + halfXSize, boundary.maxY, boundary.maxZ),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX + halfXSize, boundary.minY, boundary.minZ + halfZSize,
                boundary.maxX, boundary.minY + halfYSize, boundary.maxZ),
                depth));

        childrenOctants.add(new BoxOctree(new AABB(
                boundary.minX + halfXSize, boundary.minY + halfYSize, boundary.minZ + halfZSize,
                boundary.maxX, boundary.maxY, boundary.maxZ),
                depth));

        for (AABB parentInnerBox : innerBoxes) {
            for (BoxOctree octree : childrenOctants) {
                if (octree.boundaryIntersectsFuzzy(parentInnerBox)) {
                    octree.addBox(parentInnerBox);
                }
            }
        }

        innerBoxes.clear();
    }

    public void addBox(AABB axisAlignedBB) {
        if (depth < maximumDepth && innerBoxes.size() > subdivideThreshold) {
            subdivide();
        }

        if (!childrenOctants.isEmpty()) {
            for (BoxOctree octree : childrenOctants) {
                if (octree.boundaryIntersectsFuzzy(axisAlignedBB)) {
                    octree.addBox(axisAlignedBB);
                }
            }
        } else {
            // Prevent re-adding the same box if it already exists
            for (AABB parentInnerBox : innerBoxes) {
                if (parentInnerBox.equals(axisAlignedBB)) {
                    return;
                }
            }

            innerBoxes.add(axisAlignedBB);
        }
    }

    public void removeBox(AABB axisAlignedBB) {
        if (!childrenOctants.isEmpty()) {
            for (BoxOctree octree : childrenOctants) {
                if (octree.boundaryIntersectsFuzzy(axisAlignedBB)) {
                    octree.removeBox(axisAlignedBB);
                }
            }
        } else {
            for (AABB innerBox : innerBoxes) {
                if (innerBox.equals(axisAlignedBB)) {
                    innerBoxes.remove(innerBox);
                    return;
                }
            }
        }
    }

    public boolean boundaryIntersectsFuzzy(AABB axisAlignedBB) {
        return boundary.inflate(axisAlignedBB.getSize() / 2).intersects(axisAlignedBB);
    }

    public boolean boundaryContains(AABB axisAlignedBB) {
        return boundary.contains(axisAlignedBB.minX, axisAlignedBB.minY, axisAlignedBB.minZ) &&
                boundary.contains(axisAlignedBB.maxX, axisAlignedBB.maxY, axisAlignedBB.maxZ);
    }

    public boolean intersectsAnyBox(AABB axisAlignedBB) {
        if (!childrenOctants.isEmpty()) {
            for (BoxOctree octree : childrenOctants) {
                if (octree.intersectsAnyBox(axisAlignedBB)) {
                    return true;
                }
            }
        } else {
            for (AABB innerBox : innerBoxes) {
                if (innerBox.intersects(axisAlignedBB)) {
                    return true;
                }
            }
        }

        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy