/* * Copyright 2015 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.polyworld.elevation; import java.util.Collection; import java.util.Deque; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; import java.util.function.Predicate; import org.terasology.polyworld.graph.Corner; import org.terasology.polyworld.graph.Graph; import org.terasology.polyworld.graph.GraphFacet; import org.terasology.polyworld.graph.Region; import org.terasology.polyworld.water.WaterModel; import org.terasology.polyworld.water.WaterModelFacet; import org.terasology.world.generation.Facet; import org.terasology.world.generation.FacetProvider; import org.terasology.world.generation.GeneratingRegion; import org.terasology.world.generation.Requires; import org.terasology.world.generation.Updates; import com.google.common.collect.Sets; /** * Makes sure that all corners of lake polygons have that same height. */ @Updates(@Facet(ElevationModelFacet.class)) @Requires({ @Facet(GraphFacet.class), @Facet(WaterModelFacet.class) }) public class FlatLakeProvider implements FacetProvider { @Override public void setSeed(long seed) { // ignore } @Override public void process(GeneratingRegion region) { ElevationModelFacet elevationModelFacet = region.getRegionFacet(ElevationModelFacet.class); GraphFacet graphFacet = region.getRegionFacet(GraphFacet.class); WaterModelFacet waterModelFacet = region.getRegionFacet(WaterModelFacet.class); for (Graph g : graphFacet.getAllGraphs()) { ElevationModel elevationModel = elevationModelFacet.get(g); WaterModel waterModel = waterModelFacet.get(g); ElevationModel flattenedElevationModel = flattenLakes(g, elevationModel, waterModel); elevationModelFacet.set(g, flattenedElevationModel); } } private ElevationModel flattenLakes(Graph graph, ElevationModel elevationModel, WaterModel waterModel) { Set<Region> found = Sets.newHashSet(); Predicate<Region> isLake = r -> waterModel.isWater(r) && !waterModel.isOcean(r); FlatLakeElevationModel flatModel = new FlatLakeElevationModel(elevationModel); for (Region r : graph.getRegions()) { if (isLake.test(r) && !found.contains(r)) { Collection<Region> lake = floodFill(r, isLake); flattenLake(flatModel, lake); found.addAll(lake); } } return flatModel; } private Collection<Region> floodFill(Region start, Predicate<Region> pred) { Collection<Region> lake = new HashSet<Region>(); lake.add(start); Deque<Region> queue = new LinkedList<>(); queue.add(start); while (!queue.isEmpty()) { Region next = queue.pop(); for (Region n : next.getNeighbors()) { if (pred.test(n) && !lake.contains(n) && !queue.contains(n)) { lake.add(n); queue.add(n); } } } return lake; } private void flattenLake(FlatLakeElevationModel elevationModel, Collection<Region> lake) { float minHeight = Float.POSITIVE_INFINITY; for (Region r : lake) { float elevation = elevationModel.getElevation(r); if (minHeight >= elevation) { minHeight = elevation; } } // assign target height to all corners for (Region r : lake) { for (Corner c : r.getCorners()) { elevationModel.setElevation(c, minHeight); } } } }