diff --git a/core/src/main/java/com/seibel/lod/core/util/RayCastUtil.java b/core/src/main/java/com/seibel/lod/core/util/RayCastUtil.java
new file mode 100644
index 000000000..8905a9d8b
--- /dev/null
+++ b/core/src/main/java/com/seibel/lod/core/util/RayCastUtil.java
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the Distant Horizons mod (formerly the LOD Mod),
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2022 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package com.seibel.lod.core.util;
+
+import com.seibel.lod.core.util.math.Vec3d;
+import com.seibel.lod.core.util.math.Vec3f;
+import com.seibel.lod.core.util.math.Vec3i;
+
+/**
+ * @author James Seibel
+ * @version 2022-11-19
+ */
+public class RayCastUtil
+{
+
+ public static boolean rayIntersectsCube(Vec3d rayStartingPos, Vec3f rayDirection, Vec3i cubeMinPos, int cubeWidth)
+ {
+ // the ray must intersect all 3 axis in order to have gone through the cube
+ return rayIntersectsSquare(rayStartingPos.x, rayStartingPos.z, rayDirection.x, rayDirection.z, cubeMinPos.x, cubeMinPos.z, cubeWidth) &&
+ rayIntersectsSquare(rayStartingPos.x, rayStartingPos.y, rayDirection.x, rayDirection.y, cubeMinPos.x, cubeMinPos.y, cubeWidth);
+ }
+
+ /**
+ * this function works for any perpendicular axis, X and Y are just for simplicity and could easily be replaced with X, Y, or Z
+ *
+ * @param rayX the ray's starting X position
+ * @param rayY the ray's starting Z position
+ * @param squareMinX the square's X corner closest to negative infinity
+ * @param squareMinY the square's Y corner closest to negative infinity
+ */
+ public static boolean rayIntersectsSquare(
+ double rayX, double rayY, double rayXDirection, double rayYDirection,
+ double squareMinX, double squareMinY, double squareWidth)
+ {
+ double roundingValue = 0.05;
+
+ // determine the other corner of the square
+ double squareMaxX = squareMinX + squareWidth;
+ double squareMaxY = squareMinY + squareWidth;
+
+
+ // check if the ray originates in the square
+ if (rayX >= squareMinX && rayX <= squareMaxX &&
+ rayY >= squareMinY && rayY <= squareMaxY)
+ {
+ return true;
+ }
+
+
+
+ if (isRoughly(rayXDirection, 0, roundingValue) && isRoughly(rayYDirection, 0, roundingValue))
+ {
+ // slope is in a direction perpendicular to this ray
+
+ // this ray can be treated like a point,
+ // checking if the point originated inside the square
+ // should catch if this was true
+ return false;
+ }
+ else if (isRoughly(Math.abs(rayYDirection), 1, roundingValue))
+ {
+ // slope is straight up or down
+
+ // is the ray pointing towards the square?
+ if ((rayYDirection > 0 && rayY > squareMaxY) || // up
+ (rayYDirection < 0 && rayY < squareMinY)) // down
+ {
+ // the ray is pointing away from the square
+ return false;
+ }
+ else
+ {
+ // check if the ray's X value is between the square's left and right sides
+ return rayX >= squareMinX && rayX <= squareMaxX;
+ }
+ }
+ else if (isRoughly(rayYDirection, 0, roundingValue))
+ {
+ // slope is 0 (horizontal line)
+
+ // is the ray pointing towards the square?
+ if ((rayXDirection > 0 && rayX > squareMaxX) || // right
+ (rayXDirection < 0 && rayX < squareMinX)) // left
+ {
+ // the ray is pointing away from the square
+ return false;
+ }
+ else
+ {
+ // check if the ray's Y value is between the square's top and bottom sides
+ return rayY >= squareMinY && rayY <= squareMaxY;
+ }
+ }
+ else
+ {
+ // slope is a valid range (between -infinity and infinity)
+ double slope = rayYDirection / rayXDirection;
+
+ // move the square into ray space (where the ray is at the origin)
+ squareMinX -= rayX;
+ squareMaxX -= rayX;
+
+ squareMinY -= rayY;
+ squareMaxY -= rayY;
+
+
+ boolean intersectsX = false;
+ boolean intersectsY = false;
+
+
+
+ // ray Y intersect
+ // y = mx
+ double yIntersectMin = slope * squareMinX;
+ double yIntersectMax = slope * squareMaxX;
+
+ // does the intersection happen before the ray's origin?
+ if (yIntersectMin <= rayY && (yIntersectMax <= rayY))
+ {
+ return false;
+ }
+ // does the line intersect the square?
+ else if (yIntersectMin >= squareMinY && yIntersectMin <= squareMaxY)
+ {
+ intersectsY = true;
+ }
+ else if (yIntersectMax >= squareMinY && yIntersectMax <= squareMaxY)
+ {
+ intersectsY = true;
+ }
+
+
+ // ray X intersect
+ // x = y/m
+ double xIntersectMin = squareMinY / slope;
+ double xIntersectMax = squareMaxY / slope;
+
+ // does the intersection happen before the ray's origin?
+ if (xIntersectMin <= rayX && (xIntersectMax <= rayX))
+ {
+ return false;
+ }
+ // does the line intersect the square?
+ else if (xIntersectMin >= squareMinX && xIntersectMin <= squareMaxX)
+ {
+ intersectsX = true;
+ }
+ else if (xIntersectMax >= squareMinX && xIntersectMax <= squareMaxX)
+ {
+ intersectsX = true;
+ }
+
+
+ // if the ray intersects both the top and side of the square, that means
+ // the ray intersects the square as a whole
+ return intersectsX && intersectsY;
+ }
+ }
+ /** used to get around floating point number rounding errors */
+ private static boolean isRoughly(double input, double equalsVal, double errorValue) { return input >= equalsVal - errorValue && input <= equalsVal + errorValue; }
+
+
+}
diff --git a/core/src/test/java/tests/RaycastingTest.java b/core/src/test/java/tests/RaycastingTest.java
new file mode 100644
index 000000000..3e02c03b7
--- /dev/null
+++ b/core/src/test/java/tests/RaycastingTest.java
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the Distant Horizons mod (formerly the LOD Mod),
+ * licensed under the GNU LGPL v3 License.
+ *
+ * Copyright (C) 2020-2022 James Seibel
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+package tests;
+
+import com.seibel.lod.core.api.external.methods.data.DhApiTerrainDataRepo;
+import com.seibel.lod.core.util.RayCastUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author James Seibel
+ * @version 2022-11-19
+ */
+public class RaycastingTest
+{
+
+ @Test
+ public void DemoTest()
+ {
+ Assert.assertTrue("Example test 1", true);
+
+ double rayX, rayY;
+ double xDir, yDir;
+
+ // 1x1 square at (1,1) - (2,2)
+ double squareMinX = 1;
+ double squareMinY = 1;
+ int squareWidth = 1;
+
+
+
+ //============//
+ // horizontal //
+ //============//
+
+ // ray points right - direction <1,0>
+ xDir = 1;
+ yDir = 0;
+
+ // ray origin left of square
+ rayX = 0;
+ testRay(false, rayX, 0, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 0.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(true, rayX, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, rayX, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, rayX, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(false, rayX, 2.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 3, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ // ray origin right of square
+ rayX = 2.5;
+ testRay(false, rayX, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+
+ //==========//
+ // vertical //
+ //==========//
+
+ xDir = 0;
+ yDir = 1;
+
+ // ray points up - direction <0,1>
+ rayY = 0;
+
+ // ray origin below square
+ testRay(false, 0, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 0.5, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(true, 1, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 1.5, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 2, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(false, 2.5, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 3, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+ // ray origin in square
+ testRay(true, 1, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 1.5, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 2, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+ // ray origin above square
+ rayY = 2.5;
+ testRay(false, 1, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 1.5, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 2, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+
+ //=======//
+ // point //
+ //=======//
+
+ // AKA the slope is perpendicular to this plane
+ // direction <0,0>
+ xDir = 0;
+ yDir = 0;
+ testRay(false, 0, 0, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 0.5, 0.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(true, 1, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 1.5, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 2, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(false, 2.5, 2.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, 3, 3, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+
+ //==========//
+ // diagonal //
+ //==========//
+
+ // ray points up right - direction <4,3> (a slope of 3/4)
+ xDir = 4;
+ yDir = 3;
+
+ // ray origin bottom left of square
+ rayX = 0;
+ testRay(false, rayX, -1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(true, rayX, -0.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, rayX, 0, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, rayX, 0.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, rayX, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ testRay(false, rayX, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+ // ray origin in square
+ testRay(true, 1, 1, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 1.5, 1.5, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(true, 2, 2, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+
+ // ray origin right of square
+ rayX = 2.5;
+ rayY = (yDir/xDir) * rayX; // y = mx + b // b is the constants defined below
+ testRay(false, rayX, -0.5 + rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 0 + rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 0.5 + rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ testRay(false, rayX, 1 + rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+
+ }
+
+ private static void testRay(boolean expectedToIntersect, double rayX, double rayY, double xDir, double yDir, double squareMinX, double squareMinY, double squareWidth)
+ {
+ boolean intersects = RayCastUtil.rayIntersectsSquare(rayX, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth);
+ Assert.assertEquals(failMessage(rayX, rayY, xDir, yDir, squareMinX, squareMinY, squareWidth), expectedToIntersect, intersects);
+ }
+
+ private static String failMessage(double rayX, double rayY, double xDir, double yDir, double squareMinX, double squareMinY, double squareWidth)
+ {
+ return "ray: [" + rayX + ", " + rayY + "] <" + xDir + ", " + yDir + "> square: [" + squareMinX + ", " + squareMinY + "] - [" + (squareMinX+squareWidth) + ", " + (squareMinY+squareWidth) + "]";
+ }
+
+
+}