From a9402f487f9dac137069440de7b4ae399a1ff748 Mon Sep 17 00:00:00 2001 From: Nazrin Date: Fri, 8 Sep 2023 20:06:14 -0700 Subject: [PATCH] Implement RegionShape.POLYGON and RegionShape.CYLINDER (#2348) Also took the opportunity to sort them in order and use multiplication instead of pow. --- .../grasscutter/scripts/data/SceneRegion.java | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java index 77706c7ec..99db23615 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneRegion.java @@ -30,16 +30,48 @@ public class SceneRegion { public boolean contains(Position position) { switch (shape) { - case ScriptRegionShape.CUBIC: - return (Math.abs(pos.getX() - position.getX()) <= size.getX() / 2f) - && (Math.abs(pos.getY() - position.getY()) <= size.getY() / 2f) + case ScriptRegionShape.SPHERE -> { + val x = pos.getX() - position.getX(); + val y = pos.getY() - position.getY(); + val z = pos.getZ() - position.getZ(); + // x^2 + y^2 + z^2 = radius^2 + return x * x + y * y + z * z <= radius * radius; + } + case ScriptRegionShape.CUBIC -> { + return (Math.abs(pos.getX() - position.getX()) <= size.getX() / 2f) + && (Math.abs(pos.getY() - position.getY()) <= size.getY() / 2f) && (Math.abs(pos.getZ() - position.getZ()) <= size.getZ() / 2f); - case ScriptRegionShape.SPHERE: - var x = Math.pow(pos.getX() - position.getX(), 2); - var y = Math.pow(pos.getY() - position.getY(), 2); - var z = Math.pow(pos.getZ() - position.getZ(), 2); - // ^ means XOR in java! - return x + y + z <= (radius * radius); + } + case ScriptRegionShape.POLYGON -> { + // algorithm is "ray casting": https://www.youtube.com/watch?v=RSXM9bgqxJM + if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false; + var count = 0; + for (var i = 0; i < point_array.size(); ++i) { + val j = (i + 1) % point_array.size(); + + val yp = position.getZ(); + val y1 = point_array.get(i).getY(); + val y2 = point_array.get(j).getY(); + + val xp = position.getX(); + val x1 = point_array.get(i).getX(); + val x2 = point_array.get(j).getX(); + + if ((yp < y1) != (yp < y2) + && xp < x1 + ((yp - y1) / (y2 - y1)) * (x2 - x1)) { + ++count; + } + } + + return count % 2 == 1; + } + case ScriptRegionShape.CYLINDER -> { + if (Math.abs(pos.getY() - position.getY()) > height / 2f) return false; + val x = pos.getX() - position.getX(); + val z = pos.getZ() - position.getZ(); + // x^2 + z^2 = radius^2 + return x * x + z * z <= radius * radius; + } } return false; }