/* * Copyright 2014 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.moisture; import java.util.Collections; import java.util.Comparator; import java.util.Deque; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.terasology.polyworld.graph.Corner; import org.terasology.polyworld.graph.Graph; import org.terasology.polyworld.graph.Region; import org.terasology.polyworld.rivers.RiverModel; import org.terasology.polyworld.water.WaterModel; import org.terasology.utilities.random.FastRandom; import org.terasology.utilities.random.Random; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /** * Uses rivers and lakes as sources of moisture and distributes it over the entire island. */ public class DefaultMoistureModel implements MoistureModel { private final Graph graph; private final Map<Corner, Float> moisture = Maps.newHashMap(); private final RiverModel riverModel; private WaterModel waterModel; public DefaultMoistureModel(Graph graph, RiverModel riverModel, WaterModel waterModel) { this.graph = graph; this.riverModel = riverModel; this.waterModel = waterModel; assignRiverAndLakeMoisture(); assignRandomSeedMoisture(); spreadMoisture(); assignOceanMoisture(); assignRemaining(0f); redistributeMoisture(); } private void assignRiverAndLakeMoisture() { for (Corner c : graph.getCorners()) { int riverValue = riverModel.getRiverValue(c); if ((waterModel.isWater(c) || riverValue > 0) && !waterModel.isOcean(c)) { moisture.put(c, riverValue > 0 ? Math.min(3.0f, (0.2f * riverValue)) : 1.0f); } } } private void assignRandomSeedMoisture() { int cornerCount = graph.getCorners().size(); int count = cornerCount / 50; Random r = new FastRandom(133353); for (int i = 0; i < count; i++) { Corner c = graph.getCorners().get(r.nextInt(cornerCount)); // the seed value should be below the normalization threshold in redistributeMoisture float seedValue = 0.25f; moisture.put(c, seedValue); } } private void spreadMoisture() { Deque<Corner> queue = new LinkedList<>(moisture.keySet()); while (!queue.isEmpty()) { Corner c = queue.pop(); float cm = getMoisture(c); for (Corner a : c.getAdjacent()) { float newM = .9f * cm; if (newM > moisture.getOrDefault(a, 0f)) { moisture.put(a, newM); queue.add(a); } } } } private void assignOceanMoisture() { for (Corner c : graph.getCorners()) { if (waterModel.isOcean(c) || waterModel.isCoast(c)) { moisture.put(c, 1.0f); } } } private void assignRemaining(float value) { for (Corner c : graph.getCorners()) { if (!moisture.containsKey(c)) { moisture.put(c, value); } } } private void redistributeMoisture() { List<Corner> landCorners = Lists.newArrayList(); for (Corner c : graph.getCorners()) { if (!waterModel.isOcean(c) && !waterModel.isCoast(c)) { landCorners.add(c); } } int size = landCorners.size(); if (size == 0) { return; } Collections.sort(landCorners, new Comparator<Corner>() { @Override public int compare(Corner o1, Corner o2) { float m1 = getMoisture(o1); float m2 = getMoisture(o2); return Double.compare(m1, m2); } }); // the list is sorted now, so the last entry has the largest number float maximum = getMoisture(landCorners.get(size - 1)); // if there is no real moisture then don't scale up to max, which is around lakes and rivers float v = (maximum < 0.3) ? 0.3f : 1f; float scale = v / size; for (int i = 0; i < size; i++) { moisture.put(landCorners.get(i), i * scale); } } @Override public float getMoisture(Region r) { float total = 0; for (Corner c : r.getCorners()) { total += getMoisture(c); } return total / r.getCorners().size(); } @Override public float getMoisture(Corner c) { return moisture.get(c); } }