diff --git a/src/engine/CollisionEngine/CollisionManager.java b/src/engine/CollisionEngine/CollisionManager.java index 0867d6fb..cb870c12 100644 --- a/src/engine/CollisionEngine/CollisionManager.java +++ b/src/engine/CollisionEngine/CollisionManager.java @@ -6,17 +6,18 @@ import engine.objects.Building; import java.awt.*; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.HashMap; public class CollisionManager { + public static HashMap> structure_meshes; + public static HashMap> mesh_triangles; public static boolean CollisionDetected(Building building, Line2D travelLine, float charHeight, float charY){ if(building.buildingRect != null) if(!travelLine.intersects(building.buildingRect) && !building.buildingRect.contains(travelLine.getP1()) && !building.buildingRect.contains(travelLine.getP2())) return false; - for (Mesh mesh : building.buildingMeshes) - if(mesh.MeshCollides(travelLine,charHeight,charY)) - return true; return false; } diff --git a/src/engine/CollisionEngine/Mesh.java b/src/engine/CollisionEngine/Mesh.java index 1e8be05a..94f2f762 100644 --- a/src/engine/CollisionEngine/Mesh.java +++ b/src/engine/CollisionEngine/Mesh.java @@ -1,50 +1,62 @@ package engine.CollisionEngine; +import engine.math.Vector3f; +import engine.math.Vector3fImmutable; import engine.objects.Building; +import org.pmw.tinylog.Logger; import java.awt.geom.Line2D; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; public class Mesh { public ArrayList triangles; - public ArrayList BoundingLines; - public Rectangle2D boundsRect; - public float maxY; - public float minY; - public Building parentBuilding; - - public boolean BoundsCollides(Line2D line){ - for(Line2D side : BoundingLines) - if(side.intersectsLine(line)) - return true; - - return false; - } - - public boolean MeshCollides(Line2D line, float charHeight, float charY){ - - //check if movement path intersects this mesh - if(boundsRect == null){ - return false; + public Vector3f mesh_end_point; + public Vector3f mesh_ref_point; + public Vector3f mesh_location; + public float mesh_max_y; + public float mesh_min_y; + public Vector3f mesh_scale; + public int mesh_id; + public int parent_prop_id; + public int parent_structure_id; + public int parentUUID; + public Rectangle2D.Float mesh_bounds; + + public void AdoptTriangles(float rotation){ + if(CollisionManager.mesh_triangles.containsKey(this.mesh_id) == false){ + Logger.error("Failed To Bake Triangles For Mesh: " + this.mesh_id); + return; } - if(!line.intersects(boundsRect) && !boundsRect.contains(line.getP1()) && !boundsRect.contains(line.getP2())) - return false; + this.triangles = new ArrayList<>(); + double radian = (double)rotation; + for(Triangle tri : CollisionManager.mesh_triangles.get(this.mesh_id)){ + Triangle newTri = new Triangle(); + Vector3f rotatedPoint1 = Vector3f.rotateAroundPoint(new Vector3f(tri.point1.x,mesh_location.y,tri.point1.y),mesh_location,radian); + Vector3f rotatedPoint2 = Vector3f.rotateAroundPoint(new Vector3f(tri.point2.x,mesh_location.y,tri.point2.y),mesh_location,radian); + Vector3f rotatedPoint3 = Vector3f.rotateAroundPoint(new Vector3f(tri.point3.x,mesh_location.y,tri.point3.y),mesh_location,radian); - //check to see if character is under or over the mesh - float head = charY + charHeight; - if(head < this.minY || charY > this.maxY) - return false; + newTri.point1 = new Point2D.Float(rotatedPoint1.x,rotatedPoint1.z); + newTri.point2 = new Point2D.Float(rotatedPoint2.x,rotatedPoint2.z); + newTri.point3 = new Point2D.Float(rotatedPoint3.x,rotatedPoint3.z); + + newTri.sides = new ArrayList<>(); + newTri.sides.add(new Line2D.Float(newTri.point1,newTri.point2)); + newTri.sides.add(new Line2D.Float(newTri.point2,newTri.point3)); + newTri.sides.add(new Line2D.Float(newTri.point3,newTri.point1)); + } + } - //check if any triangles intersect the movement path - for(Triangle tri : triangles) - if(tri.collides(line)) - return true; + public void MakeBounds(float rotation){ + double radian = (double)rotation; + Vector3f rotatedEnd = Vector3f.rotateAroundPoint(new Vector3f(this.mesh_end_point.x,mesh_location.y,this.mesh_end_point.z),mesh_location,radian); + Vector3f rotatedRef = Vector3f.rotateAroundPoint(new Vector3f(this.mesh_ref_point.x,mesh_location.y,this.mesh_ref_point.z),mesh_location,radian); - //characters movement path did not intersect this mesh - return false; + this.mesh_bounds = new Rectangle2D.Float(); + this.mesh_bounds.setRect(rotatedEnd.x,rotatedEnd.z,Math.abs(rotatedEnd.x) - Math.abs(rotatedRef.x),Math.abs(rotatedEnd.z) - Math.abs(rotatedRef.z)); } } diff --git a/src/engine/CollisionEngine/Triangle.java b/src/engine/CollisionEngine/Triangle.java index 0a36e03a..933b13e2 100644 --- a/src/engine/CollisionEngine/Triangle.java +++ b/src/engine/CollisionEngine/Triangle.java @@ -2,9 +2,13 @@ package engine.CollisionEngine; import java.awt.geom.Line2D; +import java.awt.geom.Point2D; import java.util.ArrayList; public class Triangle { + public Point2D.Float point1; + public Point2D.Float point2; + public Point2D.Float point3; public ArrayList sides; public boolean collides(Line2D line) { diff --git a/src/engine/db/handlers/dbBuildingHandler.java b/src/engine/db/handlers/dbBuildingHandler.java index 7043204e..b42b9497 100644 --- a/src/engine/db/handlers/dbBuildingHandler.java +++ b/src/engine/db/handlers/dbBuildingHandler.java @@ -9,7 +9,9 @@ package engine.db.handlers; +import engine.CollisionEngine.CollisionManager; import engine.CollisionEngine.MeshData; +import engine.CollisionEngine.Triangle; import engine.Enum; import engine.Enum.DbObjectType; import engine.Enum.ProtectionState; @@ -23,6 +25,8 @@ import engine.objects.*; import org.joda.time.DateTime; import org.pmw.tinylog.Logger; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.sql.Connection; import java.sql.PreparedStatement; @@ -855,11 +859,12 @@ public class dbBuildingHandler extends dbHandlerBase { } public void LOAD_MESH_DATA(){ + CollisionManager.structure_meshes = new HashMap<>(); try (Connection connection = DbManager.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `final_structure_meshes`")) { ResultSet rs = preparedStatement.executeQuery(); - ArrayList meshData = new ArrayList<>(); + while (rs.next()) { MeshData md = new MeshData(); md.propID = rs.getInt("propID"); @@ -870,9 +875,38 @@ public class dbBuildingHandler extends dbHandlerBase { md.endPoint = new Vector3f(rs.getFloat("endX"), rs.getFloat("endY"),rs.getFloat("endZ")); md.minY = rs.getFloat("minY"); md.maxY = rs.getFloat("maxY"); - meshData.add(md); + if(CollisionManager.structure_meshes.containsKey(rs.getInt("propID"))){ + CollisionManager.structure_meshes.get(rs.getInt("propID")).add(md); + } else{ + ArrayList meshData = new ArrayList<>(); + meshData.add(md); + CollisionManager.structure_meshes.put(rs.getInt("propID"),meshData); + } + } + } catch (SQLException e) { + Logger.error(e); + } + } + public void LOAD_MESH_TRIANGLE_DATA(){ + CollisionManager.mesh_triangles = new HashMap<>(); + try (Connection connection = DbManager.getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `final_mesh_triangles`")) { + + ResultSet rs = preparedStatement.executeQuery(); + + while (rs.next()) { + Triangle tri = new Triangle(); + tri.point1 = new Point2D.Float(rs.getFloat("P1X"),rs.getFloat("P1Z")); + tri.point2 = new Point2D.Float(rs.getFloat("P2X"),rs.getFloat("P2Z")); + tri.point3 = new Point2D.Float(rs.getFloat("P3X"),rs.getFloat("P3Z")); + if(CollisionManager.mesh_triangles.containsKey(rs.getInt("meshID"))){ + CollisionManager.mesh_triangles.get(rs.getInt("meshID")).add(tri); + } else{ + ArrayList triData = new ArrayList<>(); + triData.add(tri); + CollisionManager.mesh_triangles.put(rs.getInt("meshID"),triData); + } } - int i = 0; //finished; } catch (SQLException e) { Logger.error(e); } diff --git a/src/engine/devcmd/cmds/ColliderCmd.java b/src/engine/devcmd/cmds/ColliderCmd.java new file mode 100644 index 00000000..654d99f5 --- /dev/null +++ b/src/engine/devcmd/cmds/ColliderCmd.java @@ -0,0 +1,59 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + + +package engine.devcmd.cmds; + +import engine.CollisionEngine.Mesh; +import engine.Enum; +import engine.devcmd.AbstractDevCmd; +import engine.objects.*; + +public class ColliderCmd extends AbstractDevCmd { + + public ColliderCmd() { + super("collider"); + } + + @Override + protected void _doCmd(PlayerCharacter pc, String[] words, + AbstractGameObject target) { + + String newline = "\r\n "; + String output; + if(target.getObjectType().equals(Enum.GameObjectType.Building) == false){ + throwbackInfo(pc,"Please Select A Building To Show Collider Data"); + } + Building building = (Building)target; + output = "Collision Info:" + newline; + output += "Total Meshes: " + building.buildingMeshes.size() + newline; + for(Mesh mesh : building.buildingMeshes){ + output += "-----------------------------"; + output += "Mesh ID: " + mesh.mesh_id + newline; + output += "Mesh Location: " + mesh.mesh_location + newline; + output += "Mesh Min/Max: " + mesh.mesh_min_y + "/" + mesh.mesh_max_y + newline; + output += "Mesh Triangle Count: " + mesh.triangles.size() + newline; + output += "Mesh Rect: " + mesh.mesh_bounds + newline; + output += "-----------------------------"; + } + + throwbackInfo(pc,output); + } + + @Override + protected String _getHelpString() { + return "Displays Information About Colliders"; + } + + @Override + protected String _getUsageString() { + return "' /collider displays collision info when selected on a building"; + + } + +} diff --git a/src/engine/devcmd/cmds/InfoCmd.java b/src/engine/devcmd/cmds/InfoCmd.java index cdb18b90..50451baa 100644 --- a/src/engine/devcmd/cmds/InfoCmd.java +++ b/src/engine/devcmd/cmds/InfoCmd.java @@ -245,11 +245,6 @@ public class InfoCmd extends AbstractDevCmd { for (Regions regions : targetBuilding.getBounds().getRegions()) { //TODO ADD REGION INFO } - output += "-------Mesh Data-------" + newline; - output += "Mesh Count: " + targetBuilding.buildingMeshes.size() + newline; - for(Mesh mesh : targetBuilding.buildingMeshes){ - output += "Mesh Rect: " + mesh.boundsRect + newline; - } break; case PlayerCharacter: output += newline; diff --git a/src/engine/devcmd/cmds/RegionCmd.java b/src/engine/devcmd/cmds/RegionCmd.java index 53ae6880..d5576d4a 100644 --- a/src/engine/devcmd/cmds/RegionCmd.java +++ b/src/engine/devcmd/cmds/RegionCmd.java @@ -47,18 +47,6 @@ public class RegionCmd extends AbstractDevCmd { output += "is Outside: " + region.isOutside(); this.throwbackInfo(pc, output); } - if(building != null){ - this.throwbackInfo(pc, "Building Rect: " + building.buildingRect); - for (Mesh mesh : building.buildingMeshes){ - //this.throwbackInfo(pc, "Mesh Rect: " + mesh.boundsRect); - if(mesh.boundsRect.contains(pc.loc.x,pc.loc.z)) { - this.throwbackInfo(pc, "Inside A Mesh's Bounds"); - this.throwbackInfo(pc, "Rect: " + mesh.boundsRect); - return; - } - } - } - this.throwbackInfo(pc, "Outside All Mesh Bounds"); } @Override diff --git a/src/engine/gameManager/BuildingManager.java b/src/engine/gameManager/BuildingManager.java index d1a47882..30e68f73 100644 --- a/src/engine/gameManager/BuildingManager.java +++ b/src/engine/gameManager/BuildingManager.java @@ -9,6 +9,8 @@ package engine.gameManager; +import engine.CollisionEngine.CollisionManager; +import engine.CollisionEngine.MeshData; import engine.Enum; import engine.Enum.BuildingGroup; import engine.Enum.GameObjectType; @@ -956,8 +958,8 @@ public enum BuildingManager { cleanupHirelings(building); //rebake colliders for change in rank - BuildingManager.BakeBuildingMeshes(building); - + //BuildingManager.BakeBuildingMeshes(building); + BuildingManager.BakeBuildingColliders(building); building.isDeranking.compareAndSet(true, false); } @@ -976,99 +978,32 @@ public enum BuildingManager { return null; } - public static void BakeBuildingMeshes(Building building){ - - try { - if (building == null) - return; - - if(building.meshUUID == 450700){ - int i = 0; // stop point for testing - } - building.buildingMeshes = new ArrayList<>(); - //float rotation = building.getRot().getRotation(); - double radian = building.getBounds().getQuaternion().angleY; - int degrees = (int) Math.toDegrees(radian); - Vector3f buildingLoc = new Vector3f(building.loc.x, building.loc.y, building.loc.z); - if(prop_meshes.containsKey(building.meshUUID) == false) - return;//no meshes to load for this prop - - ArrayList meshes = prop_meshes.get(building.meshUUID); - - for (int mesh : meshes) { - - if (!mesh_heights.containsKey(mesh)) { - continue; //no data for this mesh - } - if(!mesh_triangle_points.containsKey(mesh)) - continue; - - Mesh generatedMesh = new Mesh(); - generatedMesh.parentBuilding = building; - generatedMesh.maxY = building.loc.y + mesh_heights.get(mesh).x; - generatedMesh.minY = building.loc.y + mesh_heights.get(mesh).y; - ArrayList> triPoints = mesh_triangle_points.get(mesh); - - if (mesh_bounding_boxes.containsKey(mesh)) { - Rectangle2D boundingBox = mesh_bounding_boxes.get(mesh); - float halfX = (float) boundingBox.getWidth() * 0.5f; - float halfZ = (float) boundingBox.getHeight() * 0.5f; - Vector3f bottomRight = new Vector3f(building.loc.x + halfX, building.loc.y, building.loc.z + halfZ); - Vector3f topLeft = new Vector3f(building.loc.x - halfX, building.loc.y, building.loc.z - halfZ); - Vector3f topRight = new Vector3f(building.loc.x + halfX, building.loc.y, building.loc.z - halfZ); - Vector3f bottomLeft = new Vector3f(building.loc.x - halfX, building.loc.y, building.loc.z + halfZ); - - generatedMesh.BoundingLines = new ArrayList<>(); - Point2D.Float p1 = new Point2D.Float(topLeft.x, topLeft.z); - Point2D.Float p2 = new Point2D.Float(topRight.x, topRight.z); - Point2D.Float p3 = new Point2D.Float(bottomRight.x, bottomRight.z); - Point2D.Float p4 = new Point2D.Float(bottomLeft.x, bottomLeft.z); - float rectWidth = topLeft.distance(topRight); - float rectHeight = topLeft.distance(bottomLeft); - generatedMesh.boundsRect = new Rectangle2D.Float(); - generatedMesh.boundsRect.setRect(p1.x, p1.y, rectWidth, rectHeight); - generatedMesh.BoundingLines.add(new Line2D.Float(p1, p2)); - generatedMesh.BoundingLines.add(new Line2D.Float(p2, p3)); - generatedMesh.BoundingLines.add(new Line2D.Float(p3, p4)); - generatedMesh.BoundingLines.add(new Line2D.Float(p4, p1)); - //generatedMesh.boundsRect = boundingBox; - } else{ - Logger.error("No Bounding Box Data For Mesh: " + mesh); - } - generatedMesh.triangles = new ArrayList<>(); - for (ArrayList pointList : triPoints) { - - ArrayList rotatedPoints = new ArrayList<>(); - for (Vector3f point : pointList) { - Vector3f calculatedOffsetPoint = buildingLoc.add(point); - rotatedPoints.add(Vector3f.rotateAroundPoint(buildingLoc, calculatedOffsetPoint, radian)); - } - - Point2D.Float p1 = new Point2D.Float(rotatedPoints.get(0).x, rotatedPoints.get(0).z); - Point2D.Float p2 = new Point2D.Float(rotatedPoints.get(1).x, rotatedPoints.get(1).z); - Point2D.Float p3 = new Point2D.Float(rotatedPoints.get(2).x, rotatedPoints.get(2).z); + public static void BakeBuildingColliders(Building building){ - Triangle tri = new Triangle(); - tri.sides = new ArrayList<>(); - tri.sides.add(new Line2D.Float(p1, p2)); - tri.sides.add(new Line2D.Float(p2, p3)); - tri.sides.add(new Line2D.Float(p3, p1)); - generatedMesh.triangles.add(tri); + if(CollisionManager.structure_meshes.containsKey(building.meshUUID) == false) { + Logger.error("No Meshes Found Fro Structure: " + building.meshUUID); + return; + } - } - building.buildingMeshes.add(generatedMesh); - } - //Rectangle2D.Float buildingBound = new Rectangle2D.Float(); - float xLoc = building.loc.x - building.getBounds().getHalfExtents().x; - float zLoc = building.loc.z - building.getBounds().getHalfExtents().y; - float width = building.getBounds().getHalfExtents().x * 2; - float height = building.getBounds().getHalfExtents().y * 2; - building.buildingRect = new Rectangle2D.Float(); - building.buildingRect.setRect(xLoc,zLoc,width,height); - //building.buildingRect = buildingBound; - } - catch(Exception e){ - Logger.info("Failed To Bake Building Mesh Data For Structure: " + building.meshUUID); + //create the empty array of meshes + building.buildingMeshes = new ArrayList<>(); + + //create the actual meshes from the stored mesh data + for(MeshData meshData : CollisionManager.structure_meshes.get(building.meshUUID)){ + Mesh generatedMesh = new Mesh(); + generatedMesh.mesh_end_point = new Vector3f(building.loc.x,building.loc.y,building.loc.z).add(meshData.loc).add(meshData.endPoint); + generatedMesh.mesh_ref_point = new Vector3f(building.loc.x,building.loc.y,building.loc.z).add(meshData.loc).add(meshData.refPoint); + generatedMesh.mesh_location = new Vector3f(building.loc.x,building.loc.y,building.loc.z).add(meshData.loc); + generatedMesh.mesh_max_y = building.loc.y + meshData.maxY; + generatedMesh.mesh_min_y = building.loc.y + meshData.minY; + generatedMesh.mesh_scale = meshData.scale; + generatedMesh.mesh_id = meshData.meshID; + generatedMesh.parent_prop_id = meshData.propID; + generatedMesh.parent_structure_id = building.meshUUID; + generatedMesh.parentUUID = building.getObjectUUID(); + generatedMesh.AdoptTriangles(building.getBounds().getQuaternion().angleY); + generatedMesh.MakeBounds(building.getBounds().getQuaternion().angleY); + building.buildingMeshes.add(generatedMesh); } } } diff --git a/src/engine/gameManager/DevCmdManager.java b/src/engine/gameManager/DevCmdManager.java index 52c730b6..fbf2161a 100644 --- a/src/engine/gameManager/DevCmdManager.java +++ b/src/engine/gameManager/DevCmdManager.java @@ -140,6 +140,7 @@ public enum DevCmdManager { DevCmdManager.registerDevCmd(new BoundsCmd()); DevCmdManager.registerDevCmd(new GotoBoundsCmd()); DevCmdManager.registerDevCmd(new RegionCmd()); + DevCmdManager.registerDevCmd(new ColliderCmd()); DevCmdManager.registerDevCmd(new SetMaintCmd()); DevCmdManager.registerDevCmd(new ApplyBonusCmd()); DevCmdManager.registerDevCmd(new AuditFailedItemsCmd()); diff --git a/src/engine/objects/Building.java b/src/engine/objects/Building.java index 97ce3cb3..b3f5d382 100644 --- a/src/engine/objects/Building.java +++ b/src/engine/objects/Building.java @@ -1010,7 +1010,8 @@ public class Building extends AbstractWorldObject { if (this.upgradeDateTime != null) BuildingManager.submitUpgradeJob(this); - BuildingManager.BakeBuildingMeshes(this); + //BuildingManager.BakeBuildingMeshes(this); + BuildingManager.BakeBuildingColliders(this); } public synchronized boolean setOwner(AbstractCharacter newOwner) { diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index a16afc20..aeed0cdc 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -310,9 +310,10 @@ public class WorldServer { Guild.getErrantGuild(); Logger.info("Loading Server Collision Meshes."); - DbManager.BuildingQueries.LOAD_PROP_MESHES(); + //DbManager.BuildingQueries.LOAD_PROP_MESHES(); DbManager.BuildingQueries.LOAD_MESH_DATA(); - DbManager.BuildingQueries.LOAD_MESH_BOUNDING_BOXES(); + DbManager.BuildingQueries.LOAD_MESH_TRIANGLE_DATA(); + //DbManager.BuildingQueries.LOAD_MESH_BOUNDING_BOXES(); Logger.info("Loading zone template data"); DbManager.ZoneQueries.LOAD_ALL_ZONE_TEMPLATES();