diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index df727956..06ba0539 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1923,6 +1923,7 @@ Properly layout roads along town points in TownLayout Town generates a structure -- scaffolding for doing it across roads Structures create foundations in terrain voxels Place buildings along roads +Fix town placement structure-road intersection test diff --git a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java index aa830fd0..9fe24329 100644 --- a/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java +++ b/src/main/java/electrosphere/renderer/pipelines/debug/DebugContentPipeline.java @@ -3,6 +3,7 @@ package electrosphere.renderer.pipelines.debug; import java.util.LinkedList; import java.util.List; +import org.joml.AABBd; import org.joml.Matrix4d; import org.joml.Quaterniond; import org.joml.Vector3d; @@ -40,6 +41,9 @@ import electrosphere.renderer.pipelines.RenderPipeline; import electrosphere.renderer.texture.Texture; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.utils.EntityLookupUtils; +import electrosphere.server.macro.civilization.road.Road; +import electrosphere.server.macro.structure.VirtualStructure; +import electrosphere.util.math.SpatialMathUtils; /** * Pipeline for rendering content to assist debugging @@ -180,6 +184,17 @@ public class DebugContentPipeline implements RenderPipeline { } } + // + //Draw town data + if(Globals.gameConfigCurrent.getSettings().getGraphicsDebugDrawGridAlignment()){ + for(VirtualStructure struct : Globals.serverState.realmManager.first().getMacroData().getStructures()){ + DebugContentPipeline.renderAABB(openGLState, renderPipelineState, modelTransformMatrix, struct.getAABB(), AssetDataStrings.TEXTURE_BLUE_TRANSPARENT); + } + for(Road road : Globals.serverState.realmManager.first().getMacroData().getRoads()){ + DebugContentPipeline.renderTube(openGLState, renderPipelineState, modelTransformMatrix, road.getPoint1(), road.getPoint2(), road.getRadius(), AssetDataStrings.TEXTURE_BLUE_TRANSPARENT); + } + } + //update pipeline state to use mats again renderPipelineState.setUseMaterial(true); @@ -416,6 +431,55 @@ public class DebugContentPipeline implements RenderPipeline { } } + /** + * Renders an area select + * @param openGLState The opengl state + * @param renderPipelineState The render pipeline state + * @param modelTransformMatrix The model transform matrix + * @param areaSelection The area selection + */ + static void renderAABB(OpenGLState openGLState, RenderPipelineState renderPipelineState, Matrix4d modelTransformMatrix, AABBd aabb, String texturePath){ + Model model = Globals.assetManager.fetchModel(AssetDataStrings.UNITCUBE); + if(model != null){ + Texture texture = Globals.assetManager.fetchTexture(texturePath); + if(texture != null){ + texture.bind(openGLState); + } + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(aabb.minX,aabb.minY,aabb.minZ).lerp(new Vector3d(aabb.maxX,aabb.maxY,aabb.maxZ),0.5).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.scale(new Vector3d(aabb.maxX - aabb.minX,aabb.maxY - aabb.minY,aabb.maxZ - aabb.minZ)); + model.setModelMatrix(modelTransformMatrix); + model.draw(renderPipelineState,openGLState); + } + } + + /** + * Renders an area select + * @param openGLState The opengl state + * @param renderPipelineState The render pipeline state + * @param modelTransformMatrix The model transform matrix + * @param areaSelection The area selection + */ + static void renderTube(OpenGLState openGLState, RenderPipelineState renderPipelineState, Matrix4d modelTransformMatrix, Vector3d start, Vector3d end, double radius, String texturePath){ + Model model = Globals.assetManager.fetchModel(AssetDataStrings.UNITCYLINDER); + if(model != null){ + Texture texture = Globals.assetManager.fetchTexture(texturePath); + if(texture != null){ + texture.bind(openGLState); + } + //calculate camera-modified vector3d + Vector3d cameraModifiedPosition = new Vector3d(end).lerp(new Vector3d(start),0.5).sub(CameraEntityUtils.getCameraCenter(Globals.clientState.playerCamera)); + modelTransformMatrix.identity(); + modelTransformMatrix.translate(cameraModifiedPosition); + modelTransformMatrix.rotate(SpatialMathUtils.calculateRotationFromPointToPoint(start, end).mul(new Quaterniond().rotateZ(Math.PI / 2.0))); + modelTransformMatrix.scale(new Vector3d(radius,end.distance(start) / 2.0,radius)); + model.setModelMatrix(modelTransformMatrix); + model.draw(renderPipelineState,openGLState); + } + } + /** * Gets the bone debugging pipeline * @return The bone debugging pipeline diff --git a/src/main/java/electrosphere/server/macro/MacroData.java b/src/main/java/electrosphere/server/macro/MacroData.java index a04adefa..8b57b218 100644 --- a/src/main/java/electrosphere/server/macro/MacroData.java +++ b/src/main/java/electrosphere/server/macro/MacroData.java @@ -18,6 +18,7 @@ import electrosphere.server.macro.spatial.MacroObject; import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.town.Town; import electrosphere.util.FileUtils; +import electrosphere.util.math.GeomUtils; import java.util.Random; @@ -310,14 +311,20 @@ public class MacroData { * @return true if it intersects any existing structs, false otheriwse */ public boolean intersectsStruct(AABBd aabb){ - List areaObjs = new LinkedList(); - areaObjs.addAll(this.roads); - areaObjs.addAll(this.structures); - for(MacroAreaObject areaObj : areaObjs){ - if(areaObj.getAABB().testAABB(aabb)){ + for(VirtualStructure struct : this.structures){ + if(struct.getAABB().testAABB(aabb)){ return true; } } + for(Road road : this.roads){ + //broad phase + if(road.getAABB().testAABB(aabb)){ + //near phase + if(GeomUtils.intersectAABBTube(aabb, road.getPoint1(), road.getPoint2(), road.getRadius())){ + return true; + } + } + } return false; } diff --git a/src/main/java/electrosphere/server/macro/town/TownLayout.java b/src/main/java/electrosphere/server/macro/town/TownLayout.java index 701e9a19..32b1c7d4 100644 --- a/src/main/java/electrosphere/server/macro/town/TownLayout.java +++ b/src/main/java/electrosphere/server/macro/town/TownLayout.java @@ -90,23 +90,17 @@ public class TownLayout { // //generate roads - Road newRoad; + Road upRoad = Road.createRoad(macroData, upNodeLoc, centerNodeLoc); + Road rightRoad = Road.createRoad(macroData, rightNodeLoc, centerNodeLoc); + Road leftRoad = Road.createRoad(macroData, leftNodeLoc, centerNodeLoc); + Road downRoad = Road.createRoad(macroData, downNodeLoc, centerNodeLoc); - //up-facing road - newRoad = Road.createRoad(macroData, upNodeLoc, centerNodeLoc); - TownLayout.generateStructuresAlongRoad(realm, town, newRoad, allowedStructures); - - //right-facing road - newRoad = Road.createRoad(macroData, rightNodeLoc, centerNodeLoc); - TownLayout.generateStructuresAlongRoad(realm, town, newRoad, allowedStructures); - - //left-facing road - newRoad = Road.createRoad(macroData, leftNodeLoc, centerNodeLoc); - TownLayout.generateStructuresAlongRoad(realm, town, newRoad, allowedStructures); - - //down-facing road - newRoad = Road.createRoad(macroData, downNodeLoc, centerNodeLoc); - TownLayout.generateStructuresAlongRoad(realm, town, newRoad, allowedStructures); + // + //place structures along roads + TownLayout.generateStructuresAlongRoad(realm, town, upRoad, allowedStructures); + TownLayout.generateStructuresAlongRoad(realm, town, rightRoad, allowedStructures); + TownLayout.generateStructuresAlongRoad(realm, town, leftRoad, allowedStructures); + TownLayout.generateStructuresAlongRoad(realm, town, downRoad, allowedStructures); town.setResolution(Town.TOWN_RES_MAX); } @@ -135,12 +129,14 @@ public class TownLayout { isNorthSouth = true; } + int roadRadiusOffsetRaw = (int)(road.getRadius() + 3); + //offset applied to the scan location to not place it on top of the road - Vector3d placementOffset = null; + Vector3d roadOffset = null; if(isNorthSouth){ - placementOffset = new Vector3d(road.getRadius() + 10,0,0); + roadOffset = new Vector3d(roadRadiusOffsetRaw,0,0); } else { - placementOffset = new Vector3d(0,0,road.getRadius() + 10); + roadOffset = new Vector3d(0,0,roadRadiusOffsetRaw); } //the position to try at @@ -151,9 +147,10 @@ public class TownLayout { StructureData structureData = allowedStructures.get(0); //scan along the length of the road - for(int i = 0; i < len; i++){ + for(int i = roadRadiusOffsetRaw; i < len; i++){ //solve terrain position to place - currPos.set(dir).mul(i).add(startPoint).add(placementOffset); + currPos.set(startPoint).lerp(endPoint,i/(double)len).add(roadOffset); + // currPos.set(dir).mul(i).add(startPoint).add(roadOffset); currPos.y = realm.getServerWorldData().getServerTerrainManager().getElevation(currPos); //apply structure placement offset currPos.add(structureData.getPlacementOffset()); @@ -170,7 +167,7 @@ public class TownLayout { //scan along the length of the road for(int i = 0; i < len; i++){ //solve terrain position to place - currPos.set(dir).mul(i).add(startPoint).sub(placementOffset).sub(structureData.getDimensions().x,0,structureData.getDimensions().z); + currPos.set(dir).mul(i).add(startPoint).sub(roadOffset).sub(structureData.getDimensions().x,0,structureData.getDimensions().z); currPos.y = realm.getServerWorldData().getServerTerrainManager().getElevation(currPos); //apply structure placement offset currPos.add(structureData.getPlacementOffset()); diff --git a/src/main/java/electrosphere/util/math/GeomUtils.java b/src/main/java/electrosphere/util/math/GeomUtils.java index fc810d76..16579fd5 100644 --- a/src/main/java/electrosphere/util/math/GeomUtils.java +++ b/src/main/java/electrosphere/util/math/GeomUtils.java @@ -1,6 +1,8 @@ package electrosphere.util.math; +import org.joml.AABBd; import org.joml.Intersectiond; +import org.joml.Vector2d; import org.joml.Vector3d; import org.joml.Vector3i; @@ -663,5 +665,28 @@ public class GeomUtils { double dist = Intersectiond.distancePointLine(point.x, point.y, point.z, lineStart.x, lineStart.y, lineStart.z, lineEnd.x, lineEnd.y, lineEnd.z); return dist < radius; } + + /** + * Tests if an aabb intersects a tube + * @param aabb The aabb + * @param tubeStart The start of the tube + * @param tubeEnd The end of the tube + * @param radius The radius of the tube + * @return true if they intersect, false + */ + public static boolean intersectAABBTube(AABBd aabb, Vector3d tubeStart, Vector3d tubeEnd, double radius){ + Vector2d resDat = new Vector2d(); + int res = Intersectiond.intersectLineSegmentAab( + tubeStart.x, tubeStart.y, tubeStart.z, + tubeEnd.x, tubeEnd.y, tubeEnd.z, + aabb.minX - radius, aabb.minY - radius, aabb.minZ - radius, + aabb.maxX + radius, aabb.maxY + radius, aabb.maxZ + radius, + resDat + ); + if(res != Intersectiond.OUTSIDE){ + return true; + } + return false; + } }