package org.osm2world.core.world.modules; import static com.google.common.collect.Iterables.any; import static org.osm2world.core.map_elevation.creation.EleConstraintEnforcer.ConstraintType.MIN; import static org.osm2world.core.target.common.material.NamedTexCoordFunction.GLOBAL_X_Z; import static org.osm2world.core.target.common.material.TexCoordUtil.texCoordLists; import static org.osm2world.core.util.Predicates.hasType; import static org.osm2world.core.world.modules.common.WorldModuleGeometryUtil.createTriangleStripBetween; import static org.osm2world.core.world.modules.common.WorldModuleParseUtil.*; import java.util.List; import org.osm2world.core.map_data.data.MapData; import org.osm2world.core.map_data.data.MapNode; import org.osm2world.core.map_data.data.MapWaySegment; import org.osm2world.core.map_elevation.creation.EleConstraintEnforcer; import org.osm2world.core.map_elevation.data.EleConnector; import org.osm2world.core.map_elevation.data.GroundState; import org.osm2world.core.math.VectorXYZ; import org.osm2world.core.target.RenderableToAllTargets; import org.osm2world.core.target.Target; import org.osm2world.core.target.common.material.Material; import org.osm2world.core.target.common.material.Materials; import org.osm2world.core.world.data.TerrainBoundaryWorldObject; import org.osm2world.core.world.modules.common.ConfigurableWorldModule; import org.osm2world.core.world.network.AbstractNetworkWaySegmentWorldObject; /** * adds cliffs and retaining walls to the world. * Their common property is that they offset terrain elevation. */ public class CliffModule extends ConfigurableWorldModule { @Override public void applyTo(MapData grid) { for (MapWaySegment segment : grid.getMapWaySegments()) { if (segment.getTags().contains("natural", "cliff")) { segment.addRepresentation(new Cliff(segment)); } else if (segment.getTags().contains("barrier", "retaining_wall")) { segment.addRepresentation(new RetainingWall(segment)); } } } private static int getConnectedCliffs(MapNode node) { int result = 0; for (MapWaySegment segment : node.getConnectedWaySegments()) { if (any(segment.getRepresentations(), hasType(Cliff.class))) { result += 1; } } return result; } private abstract static class AbstractCliff extends AbstractNetworkWaySegmentWorldObject implements TerrainBoundaryWorldObject, RenderableToAllTargets { protected AbstractCliff(MapWaySegment segment) { super(segment); } protected abstract float getDefaultWidth(); protected abstract Material getMaterial(); @Override public float getWidth() { return parseWidth(segment.getTags(), getDefaultWidth()); } @Override public GroundState getGroundState() { return GroundState.ON; } @Override public void defineEleConstraints(EleConstraintEnforcer enforcer) { double height = parseHeight(segment.getTags(), 5); if (isBroken()) return; /* add vertical offset between left and right connectors */ List<EleConnector> center = getCenterlineEleConnectors(); List<EleConnector> left = connectors.getConnectors(getOutlineXZ(false)); List<EleConnector> right = connectors.getConnectors(getOutlineXZ(true)); for (int i = 0; i < center.size(); i++) { // the ends of the cliff may be much lower if ((i != 0 || getConnectedCliffs(segment.getStartNode()) > 1) && (i != center.size() - 1 || getConnectedCliffs(segment.getEndNode()) > 1)) { enforcer.requireVerticalDistance( MIN, height, left.get(i), right.get(i)); } } } @Override public void renderTo(Target<?> target) { List<VectorXYZ> groundVs = createTriangleStripBetween( getOutline(false), getOutline(true)); target.drawTriangleStrip(getMaterial(), groundVs, texCoordLists(groundVs, Materials.RAIL_BALLAST_DEFAULT, GLOBAL_X_Z)); } } public static class Cliff extends AbstractCliff { protected Cliff(MapWaySegment segment) { super(segment); } @Override protected float getDefaultWidth() { return 1.0f; } @Override protected Material getMaterial() { return Materials.EARTH; } } public static class RetainingWall extends AbstractCliff { protected RetainingWall(MapWaySegment segment) { super(segment); } @Override protected float getDefaultWidth() { return 1.0f; } @Override protected Material getMaterial() { return Materials.CONCRETE; } } }