diff --git a/src/engine/collision/Mesh.java b/src/engine/collision/Mesh.java new file mode 100644 index 00000000..e0be17b4 --- /dev/null +++ b/src/engine/collision/Mesh.java @@ -0,0 +1,37 @@ +package engine.collision; + +import java.awt.geom.Line2D; + +import java.util.ArrayList; + +public class Mesh { + public ArrayList triangles; + public ArrayList BoundingLines; + public float meshHeight; + + public boolean BoundsCollides(Line2D line){ + for(Line2D side : BoundingLines) + if(side.intersectsLine(line)) + return true; + + return false; + } + + public boolean MeshCollides(Line2D line, float charHeight){ + + //movement path does not intersect this mesh + if(!this.BoundsCollides(line)) + return false; + + //character moving is higher than the max Y of this mesh + if(charHeight < this.meshHeight) + return false; + + for(Triangle tri : triangles) + if(tri.collides(line)) + return true; + + //characters movement path did not intersect this triangle + return false; + } +} diff --git a/src/engine/collision/Triangle.java b/src/engine/collision/Triangle.java new file mode 100644 index 00000000..e282feb0 --- /dev/null +++ b/src/engine/collision/Triangle.java @@ -0,0 +1,17 @@ +package engine.collision; + +import java.awt.geom.Line2D; + +import java.util.ArrayList; + +public class Triangle { + public ArrayList sides; + public boolean collides(Line2D line) + { + for(Line2D side : sides) + if(side.intersectsLine(line)) + return true; + + return false; + } +} diff --git a/src/engine/db/handlers/dbBuildingHandler.java b/src/engine/db/handlers/dbBuildingHandler.java index 375dbf17..6d4fd579 100644 --- a/src/engine/db/handlers/dbBuildingHandler.java +++ b/src/engine/db/handlers/dbBuildingHandler.java @@ -15,11 +15,13 @@ import engine.Enum.ProtectionState; import engine.Enum.TaxType; import engine.gameManager.BuildingManager; import engine.gameManager.DbManager; +import engine.math.Vector3f; import engine.math.Vector3fImmutable; import engine.objects.*; import org.joda.time.DateTime; import org.pmw.tinylog.Logger; +import java.awt.geom.Rectangle2D; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -27,6 +29,7 @@ import java.sql.SQLException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; @@ -849,4 +852,96 @@ public class dbBuildingHandler extends dbHandlerBase { return false; } + public void LOAD_PROP_MESHES() { + + try (Connection connection = DbManager.getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_structure_meshes`")) { + + ResultSet rs = preparedStatement.executeQuery(); + BuildingManager.prop_meshes = new HashMap<>(); + while (rs.next()) { + if(BuildingManager.prop_meshes.containsKey(rs.getInt("propId")) == false){ + ArrayList meshList = new ArrayList<>(); + meshList.add(rs.getInt("meshID")); + BuildingManager.prop_meshes.put(rs.getInt("propId"),meshList); + } + else + { + BuildingManager.prop_meshes.get(rs.getInt("propId")).add(rs.getInt("meshID")); + } + } + + } catch (SQLException e) { + Logger.error(e); + } + + } + + public void LOAD_MESH_DATA() { + + try (Connection connection = DbManager.getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_mesh_triangles`")) { + + ResultSet rs = preparedStatement.executeQuery(); + BuildingManager.mesh_triangle_points = new HashMap<>(); + BuildingManager.mesh_heights = new HashMap<>(); + while (rs.next()) { + + ArrayList floatPoints = new ArrayList<>(); + for(String f : rs.getString("vertices").split(";")) + { + floatPoints.add(Float.parseFloat(f)); + } + ArrayList triPoints = new ArrayList<>(); + for(int i = 0; i < floatPoints.size(); i += 3){ + triPoints.add(new Vector3f(floatPoints.get(i),floatPoints.get(i+1),floatPoints.get(i+2))); + } + + if(BuildingManager.mesh_triangle_points.containsKey(rs.getInt("meshID")) == false){ + ArrayList> newPoints = new ArrayList<>(); + newPoints.add(triPoints); + BuildingManager.mesh_triangle_points.put(rs.getInt("meshID"),newPoints); + } + else + { + BuildingManager.mesh_triangle_points.get(rs.getInt("meshID")).add(triPoints); + } + if(BuildingManager.mesh_heights.containsKey(rs.getInt("meshID")) == false){ + BuildingManager.mesh_heights.put(rs.getInt("meshID"),rs.getFloat("maxY")); + } + } + + } catch (SQLException e) { + Logger.error(e); + } + + } + + public void LOAD_MESH_BOUNDING_BOXES() { + + try (Connection connection = DbManager.getConnection(); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM `static_mesh_triangles`")) { + + ResultSet rs = preparedStatement.executeQuery(); + BuildingManager.mesh_bounding_boxes = new HashMap<>(); + while (rs.next()) { + if(BuildingManager.mesh_bounding_boxes.containsKey(rs.getInt("meshId")) == false){ + float centerX = Float.parseFloat(rs.getString("start").split(";")[0]); + float centerZ = Float.parseFloat(rs.getString("start").split(";")[1]); + float endX = Float.parseFloat(rs.getString("end").split(";")[0]); + float endZ = Float.parseFloat(rs.getString("end").split(";")[1]); + float refX = Float.parseFloat(rs.getString("ref").split(";")[0]); + float refZ = Float.parseFloat(rs.getString("ref").split(";")[1]); + Rectangle2D boundRect = new Rectangle2D.Float(); + boundRect.setRect(centerX,centerZ,Math.abs(endX-refX),Math.abs(endZ-refZ)); + BuildingManager.mesh_bounding_boxes.put(rs.getInt("meshId"),boundRect); + } + } + + } catch (SQLException e) { + Logger.error(e); + } + + } + } diff --git a/src/engine/gameManager/BuildingManager.java b/src/engine/gameManager/BuildingManager.java index bde12730..d0700f36 100644 --- a/src/engine/gameManager/BuildingManager.java +++ b/src/engine/gameManager/BuildingManager.java @@ -14,10 +14,13 @@ import engine.Enum.BuildingGroup; import engine.Enum.GameObjectType; import engine.InterestManagement.InterestManager; import engine.InterestManagement.WorldGrid; +import engine.collision.Mesh; +import engine.collision.Triangle; import engine.job.JobContainer; import engine.job.JobScheduler; import engine.jobs.UpgradeBuildingJob; import engine.math.Bounds; +import engine.math.Vector3f; import engine.math.Vector3fImmutable; import engine.net.client.ClientConnection; import engine.net.client.msg.ErrorPopupMsg; @@ -27,6 +30,10 @@ import engine.objects.*; import engine.server.MBServerStatics; import org.pmw.tinylog.Logger; +import java.awt.*; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; @@ -38,6 +45,12 @@ public enum BuildingManager { BUILDINGMANAGER; + public static HashMap> prop_meshes = new HashMap<>(); + public static HashMap mesh_heights = new HashMap<>(); + public static HashMap>> mesh_triangle_points = new HashMap<>(); + + public static HashMap mesh_bounding_boxes = new HashMap<>(); + public static HashMap> _stuckLocations = new HashMap<>(); public static HashMap> _slotLocations = new HashMap<>(); @@ -942,6 +955,9 @@ public enum BuildingManager { cleanupHirelings(building); + //rebake colliders for change in rank + BuildingManager.BakeBuildingMeshes(building); + building.isDeranking.compareAndSet(true, false); } @@ -960,4 +976,63 @@ public enum BuildingManager { return null; } + public static void BakeBuildingMeshes(Building building){ + + if(building == null) + return; + + building.buildingMeshes = new ArrayList<>(); + float rotation = building.getRot().getRotation(); + Vector3f buildingLoc = new Vector3f(building.loc.x,building.loc.y,building.loc.z); + if(!prop_meshes.containsKey(building.getBlueprint().getMeshForRank(building.getRank()))) + return; //no data for this prop ID + ArrayList meshes = prop_meshes.get(building.getBlueprint().getMeshForRank(building.getRank())); + for(int mesh : meshes){ + + if(!mesh_heights.containsKey(mesh) || !mesh_triangle_points.containsKey(mesh)) + return; //no data for this mesh + + Mesh generatedMesh = new Mesh(); + generatedMesh.meshHeight = mesh_heights.get(mesh); + ArrayList> triPoints = mesh_triangle_points.get(mesh); + + if(mesh_bounding_boxes.containsKey(mesh)){ + Rectangle2D boundingBox = mesh_bounding_boxes.get(mesh); + generatedMesh.BoundingLines = new ArrayList<>(); + float maxX = building.loc.x + (float)boundingBox.getMaxX(); + float minX = building.loc.x + (float)boundingBox.getMinX(); + float maxY = building.loc.z + (float)boundingBox.getMaxY(); + float minY = building.loc.z + (float)boundingBox.getMinY(); + + Point2D.Float p1 = new Point2D.Float(minX,maxY); + Point2D.Float p2 = new Point2D.Float(maxX,maxY); + Point2D.Float p3 = new Point2D.Float(maxX,minY); + Point2D.Float p4 = new Point2D.Float(minX,minY); + 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)); + } + + for(ArrayList pointList : triPoints){ + + ArrayList rotatedPoints = new ArrayList<>(); + for(Vector3f point : pointList) + rotatedPoints.add(Vector3f.rotateAroundPoint(buildingLoc,point,rotation)); + + 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); + + 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); + } + building.buildingMeshes.add(generatedMesh); + } + } + } diff --git a/src/engine/objects/Building.java b/src/engine/objects/Building.java index b288f0e2..398aad3e 100644 --- a/src/engine/objects/Building.java +++ b/src/engine/objects/Building.java @@ -14,6 +14,7 @@ import engine.Enum.*; import engine.InterestManagement.RealmMap; import engine.InterestManagement.Terrain; import engine.InterestManagement.WorldGrid; +import engine.collision.Mesh; import engine.db.archive.CityRecord; import engine.db.archive.DataWarehouse; import engine.db.archive.MineRecord; @@ -99,6 +100,8 @@ public class Building extends AbstractWorldObject { private ConcurrentHashMap condemned; private ArrayList children = null; + public ArrayList buildingMeshes; + /** * ResultSet Constructor */ @@ -1003,6 +1006,8 @@ public class Building extends AbstractWorldObject { if (this.upgradeDateTime != null) BuildingManager.submitUpgradeJob(this); + + BuildingManager.BakeBuildingMeshes(this); } public synchronized boolean setOwner(AbstractCharacter newOwner) { diff --git a/src/engine/server/world/WorldServer.java b/src/engine/server/world/WorldServer.java index 39de164a..a16afc20 100644 --- a/src/engine/server/world/WorldServer.java +++ b/src/engine/server/world/WorldServer.java @@ -309,6 +309,11 @@ public class WorldServer { Logger.info("Initializing Errant Guild"); Guild.getErrantGuild(); + Logger.info("Loading Server Collision Meshes."); + DbManager.BuildingQueries.LOAD_PROP_MESHES(); + DbManager.BuildingQueries.LOAD_MESH_DATA(); + DbManager.BuildingQueries.LOAD_MESH_BOUNDING_BOXES(); + Logger.info("Loading zone template data"); DbManager.ZoneQueries.LOAD_ALL_ZONE_TEMPLATES();