mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-15 10:03:45 +01:00
Proper Propa
- Improvements to graph building - Fixed a number of issues caused by junctions and inconsistent track propagation - Trains no longer disassemble with reversed carriage spacing - Trains can no longer jump to perpendicular tracks on an intersection
This commit is contained in:
parent
2ca099ce6b
commit
033e7e1a0d
10 changed files with 301 additions and 260 deletions
|
@ -1,9 +1,18 @@
|
|||
package com.simibubi.create.content.logistics.trains;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.core.PartialModel;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
|
||||
import com.simibubi.create.content.logistics.trains.track.TrackShape;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
|
||||
|
@ -32,6 +41,67 @@ public interface ITrackBlock {
|
|||
return existing;
|
||||
}
|
||||
|
||||
public default double getElevationAtCenter(BlockGetter world, BlockPos pos, BlockState state) {
|
||||
return isSlope(world, pos, state) ? .5 : 0;
|
||||
}
|
||||
|
||||
public static Collection<DiscoveredLocation> walkConnectedTracks(BlockGetter world, TrackNodeLocation location,
|
||||
boolean linear) {
|
||||
List<DiscoveredLocation> list = new ArrayList<>();
|
||||
for (BlockPos blockPos : location.allAdjacent()) {
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
if (blockState.getBlock()instanceof ITrackBlock track)
|
||||
list.addAll(track.getConnected(world, blockPos, blockState, linear, location));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public default Collection<DiscoveredLocation> getConnected(BlockGetter world, BlockPos pos, BlockState state,
|
||||
boolean linear, @Nullable TrackNodeLocation connectedTo) {
|
||||
Vec3 center = Vec3.atBottomCenterOf(pos)
|
||||
.add(0, getElevationAtCenter(world, pos, state), 0);
|
||||
List<DiscoveredLocation> list = new ArrayList<>();
|
||||
TrackShape shape = state.getValue(TrackBlock.SHAPE);
|
||||
getTrackAxes(world, pos, state).forEach(axis -> {
|
||||
addToListIfConnected(connectedTo, list, (d, b) -> axis.scale(b ? d : -d)
|
||||
.add(center), b -> shape.getNormal(), null);
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void addToListIfConnected(@Nullable TrackNodeLocation fromEnd, Collection<DiscoveredLocation> list,
|
||||
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
|
||||
BezierConnection viaTurn) {
|
||||
|
||||
DiscoveredLocation firstLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, true)).viaTurn(viaTurn)
|
||||
.withNormal(normalFactory.apply(true));
|
||||
DiscoveredLocation secondLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, false)).viaTurn(viaTurn)
|
||||
.withNormal(normalFactory.apply(false));
|
||||
|
||||
boolean skipFirst = false;
|
||||
boolean skipSecond = false;
|
||||
|
||||
if (fromEnd != null) {
|
||||
boolean equalsFirst = firstLocation.equals(fromEnd);
|
||||
boolean equalsSecond = secondLocation.equals(fromEnd);
|
||||
|
||||
// not reachable from this end
|
||||
if (!equalsFirst && !equalsSecond)
|
||||
return;
|
||||
|
||||
if (equalsFirst)
|
||||
skipFirst = true;
|
||||
if (equalsSecond)
|
||||
skipSecond = true;
|
||||
}
|
||||
|
||||
if (!skipFirst)
|
||||
list.add(firstLocation);
|
||||
if (!skipSecond)
|
||||
list.add(secondLocation);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public PartialModel prepareStationOverlay(BlockGetter world, BlockPos pos, BlockState state,
|
||||
AxisDirection direction, PoseStack transform);
|
||||
|
|
|
@ -409,11 +409,12 @@ public class TrackGraph {
|
|||
if (location.distanceTo(camera) > 50)
|
||||
continue;
|
||||
|
||||
Vec3 v1 = location.add(0, 3 / 16f, 0);
|
||||
Vec3 v2 = v1.add(node.normal.scale(0.75f));
|
||||
Vec3 yOffset = new Vec3(0, 3 / 16f, 0);
|
||||
Vec3 v1 = location.add(yOffset);
|
||||
Vec3 v2 = v1.add(node.normal.scale(0.125f));
|
||||
CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2)
|
||||
.colored(Color.mixColors(Color.WHITE, color, 1))
|
||||
.lineWidth(1 / 8f);
|
||||
.lineWidth(1 / 4f);
|
||||
|
||||
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
|
||||
if (map == null)
|
||||
|
@ -428,8 +429,10 @@ public class TrackGraph {
|
|||
|
||||
TrackEdge edge = entry.getValue();
|
||||
if (!edge.isTurn()) {
|
||||
CreateClient.OUTLINER
|
||||
.showLine(edge, edge.getPosition(node, other, 0), edge.getPosition(node, other, 1))
|
||||
CreateClient.OUTLINER.showLine(edge, edge.getPosition(node, other, 0)
|
||||
.add(yOffset),
|
||||
edge.getPosition(node, other, 1)
|
||||
.add(yOffset))
|
||||
.colored(color)
|
||||
.lineWidth(1 / 16f);
|
||||
continue;
|
||||
|
@ -440,7 +443,7 @@ public class TrackGraph {
|
|||
for (int i = 0; i <= turn.getSegmentCount(); i++) {
|
||||
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
|
||||
if (previous != null)
|
||||
CreateClient.OUTLINER.showLine(previous, previous, current)
|
||||
CreateClient.OUTLINER.showLine(previous, previous.add(yOffset), current.add(yOffset))
|
||||
.colored(color)
|
||||
.lineWidth(1 / 16f);
|
||||
previous = current;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package com.simibubi.create.content.logistics.trains;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||
import com.simibubi.create.content.logistics.trains.management.GraphLocation;
|
||||
import com.simibubi.create.foundation.utility.Couple;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction.AxisDirection;
|
||||
|
@ -24,48 +25,88 @@ public class TrackGraphHelper {
|
|||
|
||||
Vec3 axis = targetAxis.scale(targetDirection.getStep());
|
||||
double length = axis.length();
|
||||
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends =
|
||||
TrackPropagator.getEnds(level, pos, trackBlockState, true, null, null);
|
||||
|
||||
TrackGraph graph = null;
|
||||
|
||||
// Case 1: Centre of block lies on a node
|
||||
|
||||
TrackNodeLocation location = new TrackNodeLocation(Vec3.atBottomCenterOf(pos)
|
||||
.add(0, track.getElevationAtCenter(level, pos, trackBlockState), 0));
|
||||
graph = Create.RAILWAYS.getGraph(level, location);
|
||||
if (graph != null) {
|
||||
TrackNode node = graph.locateNode(location);
|
||||
if (node != null) {
|
||||
Map<TrackNode, TrackEdge> connectionsFrom = graph.getConnectionsFrom(node);
|
||||
for (Entry<TrackNode, TrackEdge> entry : connectionsFrom.entrySet()) {
|
||||
TrackNode backNode = entry.getKey();
|
||||
Vec3 direction = entry.getValue()
|
||||
.getDirection(node, backNode, true);
|
||||
if (direction.scale(length)
|
||||
.distanceToSqr(axis.scale(-1)) > 1 / 4096f)
|
||||
continue;
|
||||
|
||||
GraphLocation graphLocation = new GraphLocation();
|
||||
graphLocation.edge = Couple.create(node.getLocation(), backNode.getLocation());
|
||||
graphLocation.position = 0;
|
||||
graphLocation.graph = graph;
|
||||
return graphLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2: Center of block is between two nodes
|
||||
|
||||
Collection<DiscoveredLocation> ends = track.getConnected(level, pos, trackBlockState, true, null);
|
||||
Vec3 start = Vec3.atBottomCenterOf(pos)
|
||||
.add(0, track.getElevationAtCenter(level, pos, trackBlockState), 0);
|
||||
|
||||
TrackNode frontNode = null;
|
||||
TrackNode backNode = null;
|
||||
double position = 0;
|
||||
|
||||
for (Pair<BlockPos, DiscoveredLocation> pair : ends) {
|
||||
DiscoveredLocation current = pair.getSecond();
|
||||
BlockPos currentPos = pair.getFirst();
|
||||
Vec3 offset = Vec3.atLowerCornerOf(currentPos.subtract(pos));
|
||||
for (DiscoveredLocation current : ends) {
|
||||
Vec3 offset = current.getLocation()
|
||||
.subtract(start)
|
||||
.normalize()
|
||||
.scale(length);
|
||||
|
||||
boolean forward = offset.distanceToSqr(axis.scale(-1)) < 1 / 4096f;
|
||||
boolean backwards = offset.distanceToSqr(axis) < 1 / 4096f;
|
||||
|
||||
if (!forward && !backwards)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
DiscoveredLocation previous = null;
|
||||
double distance = 0;
|
||||
for (int i = 0; i < 100 && distance < 32; i++) {
|
||||
DiscoveredLocation loc = current;
|
||||
List<Pair<BlockPos, DiscoveredLocation>> list =
|
||||
TrackPropagator.getEnds(level, currentPos, level.getBlockState(currentPos), true, current, null);
|
||||
if (!list.isEmpty()) {
|
||||
currentPos = list.get(0)
|
||||
.getFirst();
|
||||
current = list.get(0)
|
||||
.getSecond();
|
||||
}
|
||||
|
||||
if (graph == null)
|
||||
graph = Create.RAILWAYS.getGraph(level, loc);
|
||||
if (graph == null)
|
||||
|
||||
if (graph == null || graph.locateNode(loc) == null) {
|
||||
Collection<DiscoveredLocation> list = ITrackBlock.walkConnectedTracks(level, loc, true);
|
||||
for (DiscoveredLocation discoveredLocation : list) {
|
||||
if (discoveredLocation == previous)
|
||||
continue;
|
||||
Vec3 diff = discoveredLocation.getLocation()
|
||||
.subtract(loc.getLocation());
|
||||
if ((forward ? axis.scale(-1) : axis).distanceToSqr(diff.normalize()
|
||||
.scale(length)) > 1 / 4096f)
|
||||
continue;
|
||||
|
||||
previous = current;
|
||||
current = discoveredLocation;
|
||||
distance += diff.length();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
TrackNode node = graph.locateNode(loc);
|
||||
if (node == null)
|
||||
continue;
|
||||
if (forward)
|
||||
frontNode = node;
|
||||
if (backwards) {
|
||||
backNode = node;
|
||||
position = (i + .5) * length;
|
||||
position = distance + .5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package com.simibubi.create.content.logistics.trains;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
@ -36,9 +42,21 @@ public class TrackNodeLocation extends Vec3i {
|
|||
return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
}
|
||||
|
||||
public Collection<BlockPos> allAdjacent() {
|
||||
Set<BlockPos> set = new HashSet<>();
|
||||
Vec3 vec3 = getLocation();
|
||||
double step = 1 / 8f;
|
||||
for (int x : Iterate.positiveAndNegative)
|
||||
for (int y : Iterate.positiveAndNegative)
|
||||
for (int z : Iterate.positiveAndNegative)
|
||||
set.add(new BlockPos(vec3.add(x * step, y * step, z * step)));
|
||||
return set;
|
||||
}
|
||||
|
||||
public static class DiscoveredLocation extends TrackNodeLocation {
|
||||
|
||||
BezierConnection turn = null;
|
||||
boolean forceNode = false;
|
||||
Vec3 normal;
|
||||
|
||||
public DiscoveredLocation(double p_121865_, double p_121866_, double p_121867_) {
|
||||
|
@ -51,6 +69,13 @@ public class TrackNodeLocation extends Vec3i {
|
|||
|
||||
public DiscoveredLocation viaTurn(BezierConnection turn) {
|
||||
this.turn = turn;
|
||||
if (turn != null)
|
||||
forceNode();
|
||||
return this;
|
||||
}
|
||||
|
||||
public DiscoveredLocation forceNode() {
|
||||
forceNode = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -67,6 +92,10 @@ public class TrackNodeLocation extends Vec3i {
|
|||
return turn;
|
||||
}
|
||||
|
||||
public boolean shouldForceNode() {
|
||||
return forceNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,101 +1,89 @@
|
|||
package com.simibubi.create.content.logistics.trains;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
|
||||
import com.simibubi.create.content.logistics.trains.track.TrackShape;
|
||||
import com.simibubi.create.content.logistics.trains.track.TrackTileEntity;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
import com.simibubi.create.foundation.utility.VecHelper;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class TrackPropagator {
|
||||
|
||||
static class FrontierEntry {
|
||||
BlockPos prevPos;
|
||||
DiscoveredLocation prevNode;
|
||||
BlockPos currentPos;
|
||||
DiscoveredLocation currentNode;
|
||||
DiscoveredLocation parentNode;
|
||||
|
||||
public FrontierEntry(BlockPos previousPos, BlockPos pos, DiscoveredLocation location) {
|
||||
this(null, previousPos, null, pos, location);
|
||||
}
|
||||
|
||||
public FrontierEntry(DiscoveredLocation parent, BlockPos previousPos, DiscoveredLocation previousNode,
|
||||
BlockPos pos, DiscoveredLocation location) {
|
||||
public FrontierEntry(DiscoveredLocation parent, DiscoveredLocation previousNode, DiscoveredLocation location) {
|
||||
parentNode = parent;
|
||||
prevPos = previousPos;
|
||||
prevNode = previousNode;
|
||||
currentPos = pos;
|
||||
currentNode = location;
|
||||
}
|
||||
}
|
||||
|
||||
public static void onRailRemoved(LevelAccessor reader, BlockPos pos, BlockState state) {
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends = getEnds(reader, pos, state, false, null, null);
|
||||
TrackGraph foundGraph = null;
|
||||
if (!(state.getBlock()instanceof ITrackBlock track))
|
||||
return;
|
||||
|
||||
Collection<DiscoveredLocation> ends = track.getConnected(reader, pos, state, false, null);
|
||||
GlobalRailwayManager manager = Create.RAILWAYS;
|
||||
TrackGraphSync sync = manager.sync;
|
||||
TrackGraph foundGraph = null;
|
||||
|
||||
for (Pair<BlockPos, DiscoveredLocation> removedEnd : ends) {
|
||||
DiscoveredLocation removedLocation = removedEnd.getSecond();
|
||||
// 1. Remove any nodes this rail was part of
|
||||
|
||||
for (DiscoveredLocation removedLocation : ends) {
|
||||
if (foundGraph == null)
|
||||
foundGraph = manager.getGraph(reader, removedLocation);
|
||||
if (foundGraph != null) {
|
||||
if (foundGraph == null)
|
||||
continue;
|
||||
TrackNode removedNode = foundGraph.locateNode(removedLocation);
|
||||
if (removedNode != null) {
|
||||
foundGraph.removeNode(reader, removedLocation);
|
||||
sync.nodeRemoved(foundGraph, removedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundGraph != null && foundGraph.isEmpty()) {
|
||||
manager.removeGraph(foundGraph);
|
||||
sync.graphRemoved(foundGraph);
|
||||
}
|
||||
|
||||
Set<BlockPos> positionsToUpdate = new HashSet<>();
|
||||
for (DiscoveredLocation removedEnd : ends)
|
||||
positionsToUpdate.addAll(removedEnd.allAdjacent());
|
||||
|
||||
// 2. Re-run railAdded for any track that was disconnected from this track
|
||||
|
||||
Set<TrackGraph> toUpdate = new HashSet<>();
|
||||
for (Pair<BlockPos, DiscoveredLocation> removedEnd : ends) {
|
||||
BlockPos adjPos = removedEnd.getFirst();
|
||||
BlockState adjState = reader.getBlockState(adjPos);
|
||||
List<Pair<BlockPos, DiscoveredLocation>> adjEnds =
|
||||
getEnds(reader, adjPos, adjState, true, removedEnd.getSecond(), null);
|
||||
if (adjEnds.isEmpty())
|
||||
continue;
|
||||
Vec3 filter = adjEnds.get(0)
|
||||
.getSecond()
|
||||
.getLocation()
|
||||
.subtract(removedEnd.getSecond()
|
||||
.getLocation());
|
||||
toUpdate.add(onRailAdded(reader, adjPos, adjState, filter.normalize()));
|
||||
for (BlockPos blockPos : positionsToUpdate)
|
||||
if (!blockPos.equals(pos)) {
|
||||
TrackGraph onRailAdded = onRailAdded(reader, blockPos, reader.getBlockState(blockPos));
|
||||
if (onRailAdded != null)
|
||||
toUpdate.add(onRailAdded);
|
||||
}
|
||||
|
||||
// 3. Ensure any affected graph gets checked for segmentation
|
||||
|
||||
for (TrackGraph railGraph : toUpdate)
|
||||
manager.updateSplitGraph(railGraph);
|
||||
|
||||
manager.markTracksDirty();
|
||||
}
|
||||
|
||||
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state, Vec3 axisFilter) {
|
||||
public static TrackGraph onRailAdded(LevelAccessor reader, BlockPos pos, BlockState state) {
|
||||
if (!(state.getBlock()instanceof ITrackBlock track))
|
||||
return null;
|
||||
|
||||
// 1. Remove all immediately reachable node locations
|
||||
|
||||
GlobalRailwayManager manager = Create.RAILWAYS;
|
||||
|
@ -103,7 +91,7 @@ public class TrackPropagator {
|
|||
List<FrontierEntry> frontier = new ArrayList<>();
|
||||
Set<DiscoveredLocation> visited = new HashSet<>();
|
||||
Set<TrackGraph> connectedGraphs = new HashSet<>();
|
||||
addInitialEndsOf(reader, pos, state, frontier, false, axisFilter);
|
||||
addInitialEndsOf(reader, pos, state, track, frontier, false);
|
||||
|
||||
int emergencyExit = 1000;
|
||||
while (!frontier.isEmpty()) {
|
||||
|
@ -111,7 +99,6 @@ public class TrackPropagator {
|
|||
break;
|
||||
|
||||
FrontierEntry entry = frontier.remove(0);
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
|
||||
TrackGraph graph = manager.getGraph(reader, entry.currentNode);
|
||||
if (graph != null) {
|
||||
TrackNode node = graph.locateNode(entry.currentNode);
|
||||
|
@ -121,6 +108,9 @@ public class TrackPropagator {
|
|||
continue;
|
||||
}
|
||||
|
||||
Collection<DiscoveredLocation> ends = ITrackBlock.walkConnectedTracks(reader, entry.currentNode, false);
|
||||
if (entry.prevNode != null)
|
||||
ends.remove(entry.prevNode);
|
||||
continueSearch(frontier, visited, entry, ends);
|
||||
}
|
||||
|
||||
|
@ -157,11 +147,10 @@ public class TrackPropagator {
|
|||
manager.putGraph(graph = new TrackGraph());
|
||||
|
||||
DiscoveredLocation startNode = null;
|
||||
List<BlockPos> startPositions = new ArrayList<>();
|
||||
|
||||
// 2. Find the first graph node candidate nearby
|
||||
|
||||
addInitialEndsOf(reader, pos, state, frontier, true, axisFilter);
|
||||
addInitialEndsOf(reader, pos, state, track, frontier, true);
|
||||
|
||||
emergencyExit = 1000;
|
||||
while (!frontier.isEmpty()) {
|
||||
|
@ -169,26 +158,12 @@ public class TrackPropagator {
|
|||
break;
|
||||
|
||||
FrontierEntry entry = frontier.remove(0);
|
||||
|
||||
// CreateClient.OUTLINER
|
||||
// .showAABB(entry.currentNode, new AABB(entry.currentNode.getLocation(), entry.currentNode.getLocation()
|
||||
// .add(0, 2, 0)), 120)
|
||||
// .colored(Color.GREEN)
|
||||
// .lineWidth(1 / 16f);
|
||||
// CreateClient.OUTLINER.showAABB(entry.currentPos, new AABB(entry.currentPos).contract(0, 1, 0), 120)
|
||||
// .colored(0x7777ff)
|
||||
// .lineWidth(1 / 16f);
|
||||
// if (entry.prevPos != null) {
|
||||
// CreateClient.OUTLINER.showAABB(entry.prevPos, new AABB(entry.prevPos).contract(0, 1, 0), 120)
|
||||
// .colored(0x3333aa)
|
||||
// .lineWidth(1 / 16f);
|
||||
// }
|
||||
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
|
||||
if (isValidGraphNodeLocation(entry.currentNode, ends)) {
|
||||
Collection<DiscoveredLocation> ends = ITrackBlock.walkConnectedTracks(reader, entry.currentNode, false);
|
||||
boolean first = entry.prevNode == null;
|
||||
if (!first)
|
||||
ends.remove(entry.prevNode);
|
||||
if (isValidGraphNodeLocation(entry.currentNode, ends, first)) {
|
||||
startNode = entry.currentNode;
|
||||
startPositions.add(entry.prevPos);
|
||||
startPositions.add(entry.currentPos);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -199,12 +174,7 @@ public class TrackPropagator {
|
|||
if (graph.createNode(startNode))
|
||||
sync.nodeAdded(graph, graph.locateNode(startNode));
|
||||
|
||||
// CreateClient.OUTLINER.showAABB(graph, new AABB(startNode.getLocation(), startNode.getLocation()
|
||||
// .add(0, 2, 0)), 20)
|
||||
// .lineWidth(1 / 32f);
|
||||
|
||||
for (BlockPos position : startPositions)
|
||||
frontier.add(new FrontierEntry(startNode, null, null, position, startNode));
|
||||
frontier.add(new FrontierEntry(startNode, null, startNode));
|
||||
|
||||
// 3. Build up the graph via all connected nodes
|
||||
|
||||
|
@ -215,9 +185,12 @@ public class TrackPropagator {
|
|||
|
||||
FrontierEntry entry = frontier.remove(0);
|
||||
DiscoveredLocation parentNode = entry.parentNode;
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends = findReachableEnds(reader, entry);
|
||||
Collection<DiscoveredLocation> ends = ITrackBlock.walkConnectedTracks(reader, entry.currentNode, false);
|
||||
boolean first = entry.prevNode == null;
|
||||
if (!first)
|
||||
ends.remove(entry.prevNode);
|
||||
|
||||
if (isValidGraphNodeLocation(entry.currentNode, ends) && entry.currentNode != startNode) {
|
||||
if (isValidGraphNodeLocation(entry.currentNode, ends, first) && entry.currentNode != startNode) {
|
||||
boolean nodeIsNew = graph.createNode(entry.currentNode);
|
||||
if (nodeIsNew)
|
||||
sync.nodeAdded(graph, graph.locateNode(entry.currentNode));
|
||||
|
@ -234,70 +207,35 @@ public class TrackPropagator {
|
|||
return graph;
|
||||
}
|
||||
|
||||
private static void addInitialEndsOf(LevelAccessor reader, BlockPos pos, BlockState state,
|
||||
List<FrontierEntry> frontier, boolean ignoreTurns, Vec3 axisFilter) {
|
||||
for (Pair<BlockPos, DiscoveredLocation> initial : getEnds(reader, pos, state, ignoreTurns, null, axisFilter))
|
||||
frontier.add(new FrontierEntry(initial.getFirst(), pos, initial.getSecond()));
|
||||
private static void addInitialEndsOf(LevelAccessor reader, BlockPos pos, BlockState state, ITrackBlock track,
|
||||
List<FrontierEntry> frontier, boolean ignoreTurns) {
|
||||
for (DiscoveredLocation initial : track.getConnected(reader, pos, state, ignoreTurns, null)) {
|
||||
frontier.add(new FrontierEntry(null, null, initial));
|
||||
}
|
||||
|
||||
private static List<Pair<BlockPos, DiscoveredLocation>> findReachableEnds(LevelAccessor reader,
|
||||
FrontierEntry entry) {
|
||||
BlockState currentState = reader.getBlockState(entry.currentPos);
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends = new ArrayList<>();
|
||||
|
||||
if (entry.prevNode != null) {
|
||||
BlockPos prevPos = entry.prevPos;
|
||||
|
||||
// PrevPos correction after a turn
|
||||
if (entry.currentNode.connectedViaTurn()) {
|
||||
boolean slope = false;
|
||||
if (currentState.getBlock()instanceof ITrackBlock track)
|
||||
slope = track.isSlope(reader, entry.currentPos, currentState);
|
||||
BlockPos offset = new BlockPos(VecHelper.getCenterOf(entry.currentPos)
|
||||
.subtract(entry.currentNode.getLocation()
|
||||
.add(0, slope ? 0 : .5f, 0))
|
||||
.scale(-2));
|
||||
prevPos = entry.currentPos.offset(offset);
|
||||
}
|
||||
|
||||
for (Pair<BlockPos, DiscoveredLocation> pair : getEnds(reader, prevPos, reader.getBlockState(prevPos),
|
||||
false, entry.currentNode, null))
|
||||
if (!pair.getSecond()
|
||||
.equals(entry.prevNode))
|
||||
ends.add(pair);
|
||||
}
|
||||
|
||||
ends.addAll(getEnds(reader, entry.currentPos, currentState, false, entry.currentNode, null));
|
||||
return ends;
|
||||
}
|
||||
|
||||
private static void continueSearch(List<FrontierEntry> frontier, Set<DiscoveredLocation> visited,
|
||||
FrontierEntry entry, List<Pair<BlockPos, DiscoveredLocation>> ends) {
|
||||
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
|
||||
if (visited.add(pair.getSecond()))
|
||||
frontier.add(
|
||||
new FrontierEntry(null, entry.currentPos, entry.currentNode, pair.getFirst(), pair.getSecond()));
|
||||
FrontierEntry entry, Collection<DiscoveredLocation> ends) {
|
||||
for (DiscoveredLocation location : ends)
|
||||
if (visited.add(location))
|
||||
frontier.add(new FrontierEntry(null, entry.currentNode, location));
|
||||
}
|
||||
|
||||
private static void continueSearchWithParent(List<FrontierEntry> frontier, FrontierEntry entry,
|
||||
DiscoveredLocation parentNode, List<Pair<BlockPos, DiscoveredLocation>> ends) {
|
||||
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
|
||||
frontier.add(
|
||||
new FrontierEntry(parentNode, entry.currentPos, entry.currentNode, pair.getFirst(), pair.getSecond()));
|
||||
DiscoveredLocation parentNode, Collection<DiscoveredLocation> ends) {
|
||||
for (DiscoveredLocation location : ends)
|
||||
frontier.add(new FrontierEntry(parentNode, entry.currentNode, location));
|
||||
}
|
||||
|
||||
public static boolean isValidGraphNodeLocation(DiscoveredLocation location,
|
||||
List<Pair<BlockPos, DiscoveredLocation>> next) {
|
||||
if (next.size() != 1)
|
||||
public static boolean isValidGraphNodeLocation(DiscoveredLocation location, Collection<DiscoveredLocation> next,
|
||||
boolean first) {
|
||||
int size = next.size() - (first ? 1 : 0);
|
||||
if (size != 1)
|
||||
return true;
|
||||
if (location.connectedViaTurn())
|
||||
if (location.shouldForceNode())
|
||||
return true;
|
||||
|
||||
DiscoveredLocation nextLocation = next.iterator()
|
||||
.next()
|
||||
.getSecond();
|
||||
|
||||
if (nextLocation.connectedViaTurn())
|
||||
if (next.stream()
|
||||
.anyMatch(DiscoveredLocation::connectedViaTurn))
|
||||
return true;
|
||||
|
||||
Vec3 vec = location.getLocation();
|
||||
|
@ -308,86 +246,4 @@ public class TrackPropagator {
|
|||
return ((int) Math.round(vec.x)) % 16 == 0;
|
||||
}
|
||||
|
||||
// TODO ITrackBlock
|
||||
public static List<Pair<BlockPos, DiscoveredLocation>> getEnds(LevelReader reader, BlockPos pos, BlockState state,
|
||||
boolean ignoreTurns, @Nullable DiscoveredLocation fromEnd, @Nullable Vec3 axisFilter) {
|
||||
Vec3 center = VecHelper.getCenterOf(pos);
|
||||
List<Pair<BlockPos, DiscoveredLocation>> list = new ArrayList<>();
|
||||
|
||||
if (!(state.getBlock() instanceof TrackBlock))
|
||||
return list;
|
||||
|
||||
BlockEntity blockEntity = reader.getBlockEntity(pos);
|
||||
if (state.getValue(TrackBlock.HAS_TURN) && blockEntity instanceof TrackTileEntity && !ignoreTurns) {
|
||||
TrackTileEntity trackTileEntity = (TrackTileEntity) blockEntity;
|
||||
trackTileEntity.getConnections()
|
||||
.forEach((connectedPos, bc) -> {
|
||||
Vec3 curveHandle = bc.axes.getFirst();
|
||||
if (axisFilter != null && !testAxisFilter(curveHandle.normalize(), axisFilter))
|
||||
return;
|
||||
addToSet(fromEnd, list,
|
||||
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b),
|
||||
bc.normals::get, bc);
|
||||
});
|
||||
}
|
||||
|
||||
TrackShape shape = state.getValue(TrackBlock.SHAPE);
|
||||
if (shape == TrackShape.NONE)
|
||||
return list;
|
||||
|
||||
shape.getAxes()
|
||||
.forEach(axis -> {
|
||||
if (axisFilter != null && !testAxisFilter(axis.normalize(), axisFilter))
|
||||
return;
|
||||
addToSet(fromEnd, list, (d, b) -> axis.scale(b ? d : -d)
|
||||
.add(center)
|
||||
.add(0, axis.y == 0 ? -.5 : 0, 0), b -> shape.getNormal(), null);
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static boolean testAxisFilter(Vec3 axis, Vec3 filter) {
|
||||
return Mth.equal(axis.distanceToSqr(filter), 0) || Mth.equal(axis.distanceToSqr(filter.scale(-1)), 0);
|
||||
}
|
||||
|
||||
private static void addToSet(DiscoveredLocation fromEnd, List<Pair<BlockPos, DiscoveredLocation>> list,
|
||||
BiFunction<Double, Boolean, Vec3> offsetFactory, Function<Boolean, Vec3> normalFactory,
|
||||
BezierConnection viaTurn) {
|
||||
|
||||
DiscoveredLocation firstLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, true));
|
||||
DiscoveredLocation secondLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, false));
|
||||
|
||||
Pair<BlockPos, DiscoveredLocation> firstNode =
|
||||
Pair.of(new BlockPos(offsetFactory.apply(1.0d, true)), firstLocation.viaTurn(viaTurn)
|
||||
.withNormal(normalFactory.apply(true)));
|
||||
Pair<BlockPos, DiscoveredLocation> secondNode =
|
||||
Pair.of(new BlockPos(offsetFactory.apply(1.0d, false)), secondLocation.viaTurn(viaTurn)
|
||||
.withNormal(normalFactory.apply(false)));
|
||||
|
||||
boolean skipFirst = false;
|
||||
boolean skipSecond = false;
|
||||
|
||||
if (fromEnd != null) {
|
||||
boolean equalsFirst = firstNode.getSecond()
|
||||
.equals(fromEnd);
|
||||
boolean equalsSecond = secondNode.getSecond()
|
||||
.equals(fromEnd);
|
||||
|
||||
// not reachable from this end, crossover rail
|
||||
if (!equalsFirst && !equalsSecond)
|
||||
return;
|
||||
|
||||
if (equalsFirst)
|
||||
skipFirst = true;
|
||||
if (equalsSecond)
|
||||
skipSecond = true;
|
||||
}
|
||||
|
||||
if (!skipFirst)
|
||||
list.add(firstNode);
|
||||
if (!skipSecond)
|
||||
list.add(secondNode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -338,7 +338,7 @@ public class Train {
|
|||
offset += carriage.bogeySpacing;
|
||||
|
||||
if (i < carriageSpacing.size())
|
||||
offset += carriageSpacing.get(carriageSpacing.size() - i - 1);
|
||||
offset += carriageSpacing.get(backwards ? carriageSpacing.size() - i - 1 : i);
|
||||
}
|
||||
|
||||
GlobalStation currentStation = getCurrentStation();
|
||||
|
|
|
@ -159,7 +159,7 @@ public class TravellingPoint {
|
|||
TrackEdge newEdge = entry.getValue();
|
||||
Vec3 currentDirection = edge.getDirection(node1, node2, false);
|
||||
Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
|
||||
if (currentDirection.dot(newDirection) < 0)
|
||||
if (currentDirection.dot(newDirection) < 3 / 4f)
|
||||
continue;
|
||||
|
||||
validTargets.add(entry);
|
||||
|
@ -197,7 +197,7 @@ public class TravellingPoint {
|
|||
.get(node1);
|
||||
Vec3 currentDirection = edge.getDirection(node1, node2, true);
|
||||
Vec3 newDirection = newEdge.getDirection(newNode, node1, false);
|
||||
if (currentDirection.dot(newDirection) < 0)
|
||||
if (currentDirection.dot(newDirection) < 3 / 4f)
|
||||
continue;
|
||||
|
||||
validTargets.add(entry);
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.management;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -16,7 +17,6 @@ import com.simibubi.create.content.logistics.trains.TrackEdge;
|
|||
import com.simibubi.create.content.logistics.trains.TrackGraph;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNode;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||
import com.simibubi.create.content.logistics.trains.TrackPropagator;
|
||||
import com.simibubi.create.content.logistics.trains.entity.Carriage;
|
||||
import com.simibubi.create.content.logistics.trains.entity.Carriage.CarriageBogey;
|
||||
import com.simibubi.create.content.logistics.trains.entity.CarriageContraption;
|
||||
|
@ -26,7 +26,6 @@ import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
|||
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||
import com.simibubi.create.foundation.utility.Iterate;
|
||||
import com.simibubi.create.foundation.utility.Lang;
|
||||
import com.simibubi.create.foundation.utility.Pair;
|
||||
import com.simibubi.create.foundation.utility.WorldAttached;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@ -325,12 +324,15 @@ public class StationTileEntity extends SmartTileEntity {
|
|||
BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, trackPosition, trackState));
|
||||
|
||||
DiscoveredLocation location = null;
|
||||
List<Pair<BlockPos, DiscoveredLocation>> ends =
|
||||
TrackPropagator.getEnds(level, trackPosition, trackState, true, null, null);
|
||||
for (Pair<BlockPos, DiscoveredLocation> pair : ends)
|
||||
if (trackPosition.relative(assemblyDirection)
|
||||
.equals(pair.getFirst()))
|
||||
location = pair.getSecond();
|
||||
Vec3 centre = Vec3.atBottomCenterOf(trackPosition)
|
||||
.add(0, track.getElevationAtCenter(level, trackPosition, trackState), 0);
|
||||
Collection<DiscoveredLocation> ends = track.getConnected(level, trackPosition, trackState, true, null);
|
||||
Vec3 targetOffset = Vec3.atLowerCornerOf(assemblyDirection.getNormal());
|
||||
for (DiscoveredLocation end : ends)
|
||||
if (Mth.equal(0, targetOffset.distanceToSqr(end.getLocation()
|
||||
.subtract(centre)
|
||||
.normalize())))
|
||||
location = end;
|
||||
if (location == null)
|
||||
return;
|
||||
|
||||
|
@ -349,13 +351,14 @@ public class StationTileEntity extends SmartTileEntity {
|
|||
TrackGraph graph = null;
|
||||
TrackNode secondNode = null;
|
||||
|
||||
for (int i = 0; i < assemblyLength + 20; i++) {
|
||||
for (int j = 0; j < assemblyLength * 2 + 40; j++) {
|
||||
double i = j / 2d;
|
||||
if (points.size() == pointOffsets.size())
|
||||
break;
|
||||
|
||||
DiscoveredLocation currentLocation = location;
|
||||
location = new DiscoveredLocation(location.getLocation()
|
||||
.add(directionVec));
|
||||
.add(directionVec.scale(.5)));
|
||||
|
||||
if (graph == null)
|
||||
graph = Create.RAILWAYS.getGraph(level, currentLocation);
|
||||
|
|
|
@ -99,7 +99,7 @@ public class StandardBogeyBlock extends Block implements IBogeyBlock, ITE<Standa
|
|||
ms.mulPose(Vector3f.YP.rotationDegrees(90));
|
||||
}
|
||||
|
||||
ms.translate(0, -1.5, 0);
|
||||
ms.translate(0, -1.5 - 1 / 128f, 0);
|
||||
|
||||
VertexConsumer vb = buffers.getBuffer(RenderType.cutoutMipped());
|
||||
BlockState air = Blocks.AIR.defaultBlockState();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.simibubi.create.content.logistics.trains.track;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
|
||||
|
@ -15,7 +18,10 @@ import com.simibubi.create.Create;
|
|||
import com.simibubi.create.CreateClient;
|
||||
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
|
||||
import com.simibubi.create.content.curiosities.girder.GirderBlock;
|
||||
import com.simibubi.create.content.logistics.trains.BezierConnection;
|
||||
import com.simibubi.create.content.logistics.trains.ITrackBlock;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation;
|
||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||
import com.simibubi.create.content.logistics.trains.TrackPropagator;
|
||||
import com.simibubi.create.content.logistics.trains.management.StationTileEntity;
|
||||
import com.simibubi.create.foundation.utility.AngleHelper;
|
||||
|
@ -144,8 +150,41 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac
|
|||
|
||||
@Override
|
||||
public void tick(BlockState p_60462_, ServerLevel p_60463_, BlockPos p_60464_, Random p_60465_) {
|
||||
for (Vec3 axis : getTrackAxes(p_60463_, p_60464_, p_60462_))
|
||||
TrackPropagator.onRailAdded(p_60463_, p_60464_, p_60462_, axis.normalize());
|
||||
TrackPropagator.onRailAdded(p_60463_, p_60464_, p_60462_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<DiscoveredLocation> getConnected(BlockGetter world, BlockPos pos, BlockState state,
|
||||
boolean linear, TrackNodeLocation connectedTo) {
|
||||
Collection<DiscoveredLocation> list;
|
||||
|
||||
if (getTrackAxes(world, pos, state).size() > 1) {
|
||||
Vec3 center = Vec3.atBottomCenterOf(pos)
|
||||
.add(0, getElevationAtCenter(world, pos, state), 0);
|
||||
TrackShape shape = state.getValue(TrackBlock.SHAPE);
|
||||
list = new ArrayList<>();
|
||||
for (Vec3 axis : getTrackAxes(world, pos, state))
|
||||
for (boolean fromCenter : Iterate.trueAndFalse)
|
||||
ITrackBlock.addToListIfConnected(connectedTo, list,
|
||||
(d, b) -> axis.scale(b ? 0 : fromCenter ? -d : d)
|
||||
.add(center),
|
||||
b -> shape.getNormal(), null);
|
||||
} else
|
||||
list = ITrackBlock.super.getConnected(world, pos, state, linear, connectedTo);
|
||||
|
||||
if (!state.getValue(HAS_TURN))
|
||||
return list;
|
||||
if (linear)
|
||||
return list;
|
||||
|
||||
BlockEntity blockEntity = world.getBlockEntity(pos);
|
||||
if (!(blockEntity instanceof TrackTileEntity trackTE))
|
||||
return list;
|
||||
|
||||
Map<BlockPos, BezierConnection> connections = trackTE.getConnections();
|
||||
connections.forEach((connectedPos, bc) -> ITrackBlock.addToListIfConnected(connectedTo, list,
|
||||
(d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get, bc));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue