Allow mapgens like Valleys to affect the Voronoi diagram.

Add notification system to the Voronoi diagram, to update it with the new mapgen when the mapgen is changed.
master
Treer 2018-09-09 03:36:12 +10:00
parent 8858dda70f
commit 365508f7a6
13 changed files with 324 additions and 26 deletions

View File

@ -27,6 +27,7 @@ import amidst.gui.main.viewer.ViewerFacade;
import amidst.gui.seedsearcher.SeedSearcherWindow;
import amidst.logging.AmidstLogger;
import amidst.minetest.world.mapgen.DefaultBiomes;
import amidst.minetest.world.mapgen.MapgenRelay;
import amidst.mojangapi.world.WorldSeed;
import amidst.mojangapi.world.WorldType;
import amidst.mojangapi.world.coordinates.CoordinatesInWorld;
@ -47,6 +48,7 @@ public class Actions {
private final BiomeAuthority biomeAuthority;
private final GameEngineDetails gameEngineDetails;
private final AmidstVersion amidstVersion;
private final MapgenRelay mapgenRelay;
@CalledOnlyBy(AmidstThread.EDT)
public Actions(
@ -66,6 +68,8 @@ public class Actions {
this.biomeAuthority = biomeAuthority;
this.gameEngineDetails = gameEngineDetails;
this.amidstVersion = amidstVersion;
this.mapgenRelay = new MapgenRelay(worldSwitcher);
}
@CalledOnlyBy(AmidstThread.EDT)
@ -339,7 +343,7 @@ public class Actions {
// only one implemented anyway.
// It might be worth passing this value once Amidstest is reading game profiles,
// as then it will at least know if the current world DOESN'T use the default climate settings.
dialogs.displayVoronoiDiagram(biomeAuthority.getBiomeProfileSelection(), null);
dialogs.displayVoronoiDiagram(biomeAuthority.getBiomeProfileSelection(), mapgenRelay, null);
} else {
dialogs.displayInfo("Biome profile Voronoi diagram", "Minecraft biomes are not determined by heat and humidity.");
}

View File

@ -17,6 +17,7 @@ import amidst.gui.text.TextWindow;
import amidst.gui.voronoi.VoronoiWindow;
import amidst.logging.AmidstMessageBox;
import amidst.minetest.world.mapgen.IHistogram2D;
import amidst.minetest.world.mapgen.MapgenRelay;
import amidst.mojangapi.RunningLauncherProfile;
import amidst.mojangapi.world.WorldSeed;
import amidst.mojangapi.world.WorldType;
@ -119,8 +120,8 @@ public class MainWindowDialogs {
}
@CalledOnlyBy(AmidstThread.EDT)
public void displayVoronoiDiagram(BiomeProfileSelection biome_profile_selection, IHistogram2D climate_histogram) {
VoronoiWindow.showDiagram(frame, biome_profile_selection, climate_histogram);
public void displayVoronoiDiagram(BiomeProfileSelection biome_profile_selection, MapgenRelay mapgen, IHistogram2D climate_histogram) {
VoronoiWindow.showDiagram(frame, biome_profile_selection, mapgen, climate_histogram);
}
@CalledOnlyBy(AmidstThread.EDT)

View File

@ -0,0 +1,7 @@
package amidst.gui.main;
import amidst.gui.main.viewer.ViewerFacade;
public interface WorldSwitchedListener {
void onWorldSwitched(ViewerFacade viewerFacade);
}

View File

@ -4,10 +4,12 @@ import java.awt.BorderLayout;
import java.awt.Container;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import amidst.dependency.injection.Factory1;
import amidst.documentation.AmidstThread;
@ -25,7 +27,6 @@ import amidst.mojangapi.world.WorldType;
import amidst.mojangapi.world.player.MovablePlayerList;
import amidst.mojangapi.world.player.WorldPlayerType;
import amidst.parsing.FormatException;
import amidst.settings.biomeprofile.BiomeProfile;
import amidst.settings.biomeprofile.BiomeProfileSelection;
import amidst.threading.ThreadMaster;
@ -40,6 +41,7 @@ public class WorldSwitcher {
private final AtomicReference<ViewerFacade> viewerFacadeReference;
private final MainWindowDialogs dialogs;
private final Supplier<AmidstMenu> menuBarSupplier;
private final ArrayList<WorldSwitchedListener> listeners = new ArrayList<WorldSwitchedListener>();
@CalledOnlyBy(AmidstThread.EDT)
public WorldSwitcher(
@ -62,6 +64,14 @@ public class WorldSwitcher {
this.dialogs = dialogs;
this.menuBarSupplier = menuBarSupplier;
}
public ViewerFacade addWorldSwitchedListener(WorldSwitchedListener listener) {
listeners.add(listener);
return viewerFacadeReference.get();
}
public void removeWorldSwitchedListener(WorldSwitchedListener listener) {
listeners.remove(listener);
}
@CalledOnlyBy(AmidstThread.EDT)
public void displayWorld(WorldSeed worldSeed, WorldType worldType, BiomeProfileSelection biomeProfileSelection) {
@ -131,6 +141,12 @@ public class WorldSwitcher {
threadMaster.setOnRepaintTick(viewerFacade.getOnRepainterTick());
threadMaster.setOnFragmentLoadTick(viewerFacade.getOnFragmentLoaderTick());
viewerFacadeReference.set(viewerFacade);
SwingUtilities.invokeLater(() -> {
for (WorldSwitchedListener listener: listeners) {
listener.onWorldSwitched(viewerFacade);
}
});
}
@CalledOnlyBy(AmidstThread.EDT)

View File

@ -10,6 +10,7 @@ import amidst.documentation.AmidstThread;
import amidst.documentation.CalledOnlyBy;
import amidst.documentation.NotThreadSafe;
import amidst.fragment.FragmentGraph;
import amidst.fragment.IBiomeDataOracle;
import amidst.fragment.layer.LayerManager;
import amidst.fragment.layer.LayerReloader;
import amidst.mojangapi.world.Dimension;
@ -138,6 +139,11 @@ public class ViewerFacade {
worldIconSelection.select(worldIcon);
}
@CalledOnlyBy(AmidstThread.EDT)
public IBiomeDataOracle getBiomeDataOracle() {
return world.getBiomeDataOracle();
}
@CalledOnlyBy(AmidstThread.EDT)
public WorldSeed getWorldSeed() {
return world.getWorldSeed();

View File

@ -25,6 +25,10 @@ public class VoronoiGraph {
}
}
public void setHistogram(IHistogram2D histogram) {
climateHistogram = histogram;
}
/**
* Returns an image of the Vonoroi graph, and if a climateHistogram has been provided then
* it calculates and sets the occurrenceFrequency field in each of the GraphNodes.

View File

@ -23,9 +23,11 @@ import javax.swing.SwingUtilities;
import amidst.documentation.AmidstThread;
import amidst.documentation.CalledByAny;
import amidst.documentation.CalledOnlyBy;
import amidst.fragment.IBiomeDataOracle;
import amidst.gameengineabstraction.world.biome.IBiome;
import amidst.minetest.world.mapgen.ClimateHistogram;
import amidst.minetest.world.mapgen.IHistogram2D;
import amidst.minetest.world.mapgen.IHistogram2DTransformationProvider;
import amidst.minetest.world.mapgen.MinetestBiome;
import amidst.minetest.world.mapgen.MinetestBiomeProfileImpl;
@ -60,6 +62,9 @@ public class VoronoiPanel extends JPanel {
public int graph_resolution = 1000;
private IHistogram2D climateHistogram = null;
private IHistogram2D adjustedClimateHistogram = null;
private float adjustedClimateHistogramArgument = Float.NaN;
private IHistogram2DTransformationProvider mapgenClimateHistogramAdjustment = null;
ArrayList<MinetestBiome> biomes = new ArrayList<MinetestBiome>();
private List<GraphNode> graphNodes = null;
private VoronoiGraph voronoiGraph = null;
@ -73,8 +78,12 @@ public class VoronoiPanel extends JPanel {
super.paintComponent(g);
boolean frequencyGraphWasNull = frequencyGraph == null;
if (climateHistogram == null) climateHistogram = new ClimateHistogram();
if (voronoiGraph == null) voronoiGraph = new VoronoiGraph(graph_resolution, graph_resolution, climateHistogram);
if (adjustedClimateHistogram == null) {
if (climateHistogram == null) climateHistogram = new ClimateHistogram();
adjustedClimateHistogram = (mapgenClimateHistogramAdjustment == null) ? climateHistogram : mapgenClimateHistogramAdjustment.getTransformedHistogram(climateHistogram, adjustedClimateHistogramArgument);
if (voronoiGraph != null) voronoiGraph.setHistogram(adjustedClimateHistogram);
}
if (voronoiGraph == null) voronoiGraph = new VoronoiGraph(graph_resolution, graph_resolution, adjustedClimateHistogram);
if (frequencyGraph == null) frequencyGraph = new FrequencyGraph(graph_resolution, graph_resolution);
this.setBorder(null);
@ -132,7 +141,7 @@ public class VoronoiPanel extends JPanel {
try {
if (graphNodes != null) {
g2d.drawImage(
frequencyGraph.render(climateHistogram, axis_min, axis_max, axis_min, axis_max),
frequencyGraph.render(adjustedClimateHistogram, axis_min, axis_max, axis_min, axis_max),
axis_min, axis_min, desiredAxisLength, desiredAxisLength, null, null
);
}
@ -150,7 +159,7 @@ public class VoronoiPanel extends JPanel {
// In order to prevent a delay when the user enables the frequencyGraph,
// Make it pre-render after the UI has finished updating.
SwingUtilities.invokeLater(() -> {
frequencyGraph.render(climateHistogram, axis_min, axis_max, axis_min, axis_max);
frequencyGraph.render(adjustedClimateHistogram, axis_min, axis_max, axis_min, axis_max);
});
}
}
@ -328,10 +337,13 @@ public class VoronoiPanel extends JPanel {
* If the world doesn't use Minetest's default Heat&Humidity algorithm, then pass
* a histogram of it here.
*/
public void setClimateHistogram(IHistogram2D climate_histogram) { this.climateHistogram = climate_histogram; }
public void setClimateHistogram(IHistogram2D climate_histogram) {
this.climateHistogram = climate_histogram;
this.adjustedClimateHistogram = null; // force adjustedClimateHistogram to be recalculated with the new climateHistogram
}
@CalledOnlyBy(AmidstThread.EDT)
public void Update(MinetestBiomeProfileImpl biomeProfile, int altitude, float frequency_graph_opacity, int flags) {
public void Update(MinetestBiomeProfileImpl biomeProfile, IBiomeDataOracle mapgen, int altitude, float frequency_graph_opacity, int flags) {
ArrayList<MinetestBiome> newBiomes = new ArrayList<MinetestBiome>();
if (biomeProfile != null) for (IBiome biome : biomeProfile.allBiomes()) {
@ -350,7 +362,23 @@ public class VoronoiPanel extends JPanel {
if (!alreadyOccupied) newBiomes.add(mtBiome);
}
}
IHistogram2DTransformationProvider oldAjustment = mapgenClimateHistogramAdjustment;
float oldArgument = adjustedClimateHistogramArgument;
if (mapgen instanceof IHistogram2DTransformationProvider) {
mapgenClimateHistogramAdjustment = (IHistogram2DTransformationProvider)mapgen;
adjustedClimateHistogramArgument = altitude;
} else {
mapgenClimateHistogramAdjustment = null;
adjustedClimateHistogramArgument = Float.NaN;
}
boolean histogramAdjusted = oldAjustment != mapgenClimateHistogramAdjustment || oldArgument != adjustedClimateHistogramArgument;
if (histogramAdjusted) {
// something changed, force adjustedClimateHistogram to be recalculated.
this.adjustedClimateHistogram = null;
}
if (flags != this.renderFlags || !newBiomes.equals(biomes) || frequencyGraphOpacity != frequency_graph_opacity) {
this.renderFlags = flags;
biomes = newBiomes;
@ -360,6 +388,8 @@ public class VoronoiPanel extends JPanel {
graphNodes.add(new GraphNode(biome.heat_point, biome.humidity_point, biome.getDefaultColor().getRGB()));
}
repaint();
} else if (histogramAdjusted) {
repaint();
}
}

View File

@ -5,8 +5,11 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Image;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Hashtable;
@ -32,14 +35,19 @@ import net.miginfocom.swing.MigLayout;
import amidst.ResourceLoader;
import amidst.documentation.AmidstThread;
import amidst.documentation.CalledOnlyBy;
import amidst.fragment.IBiomeDataOracle;
import amidst.gui.text.TextWindow;
import amidst.minetest.world.mapgen.IHistogram2D;
import amidst.minetest.world.mapgen.IHistogram2DTransformationProvider;
import amidst.minetest.world.mapgen.MapgenRelay;
import amidst.minetest.world.mapgen.MapgenUpdatedListener;
import amidst.minetest.world.mapgen.MinetestBiomeProfileImpl;
import amidst.minetest.world.oracle.MinetestBiomeDataOracle;
import amidst.settings.biomeprofile.BiomeProfile;
import amidst.settings.biomeprofile.BiomeProfileSelection;
import amidst.settings.biomeprofile.BiomeProfileUpdateListener;
public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener {
public class VoronoiWindow implements BiomeProfileUpdateListener, MapgenUpdatedListener, ChangeListener, KeyEventDispatcher {
private static final int ALTITUDESLIDER_DEFAULT_LOW = -40;
private static final int ALTITUDESLIDER_DEFAULT_HIGH = 200;
@ -47,6 +55,7 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
private static VoronoiWindow voronoiWindow = null;
private BiomeProfileSelection biomeProfileSelection;
private MapgenRelay mapgenRelay;
private final JFrame windowFrame;
private VoronoiPanel voronoiPanel;
@ -60,6 +69,7 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
private JCheckBox option_showCoverage;
private MinetestBiomeProfileImpl selectedProfile = null;
private MinetestBiomeDataOracle selectedMapgen = null;
@CalledOnlyBy(AmidstThread.EDT)
@ -212,6 +222,22 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
UpdateSelectedBiomeProfile(newBiomeProfile);
}
@Override
public void onMapgenUpdated(IBiomeDataOracle biomeDataOracle) {
UpdateSelectedMapgen(biomeDataOracle);
}
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.VK_F5) {
// TODO: Reload the biome from disk and update the Voronoi diagram
// (Use case: biome-editing using a text editor, since Amidst doesn't provide
// a Voronoi editor. Auto update when file updates would be even better)
}
// return false to pass the KeyEvent to the next KeyEventDispatcher in the chain
return false;
}
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() == altitudeOffset) {
@ -223,7 +249,12 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
private void InfoButtonClicked() {
StringBuilder data = new StringBuilder();
data.append("Biome profile: ");
data.append(selectedProfile.getName());
data.append(selectedProfile.getName());
if (selectedMapgen instanceof IHistogram2DTransformationProvider) {
data.append(" with ");
data.append(selectedMapgen.getName());
data.append(" mapgen");
}
data.append("\r\n\r\n");
data.append("At altitude " + getAltitudeFromDialog() + ", the world is composed of the following biomes:\r\n\r\n");
data.append(voronoiPanel.getDistributionData());
@ -273,7 +304,27 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
selectedProfile = minetestProfile;
if (changed) updateVoronoiDiagram();
this.windowFrame.setTitle(selectedProfile == null ? "Biome profile Voronoi diagram" : "Voronoi diagram for " + selectedProfile.getName());
this.windowFrame.setTitle(getTitle(selectedProfile, selectedMapgen));
}
private void UpdateSelectedMapgen(IBiomeDataOracle newMapgen) {
MinetestBiomeDataOracle minetestMapgen = (newMapgen instanceof MinetestBiomeDataOracle) ? (MinetestBiomeDataOracle)newMapgen : null;
boolean changed = (minetestMapgen == null) ? (selectedMapgen != null) : (selectedMapgen == null || (minetestMapgen.getClass() != selectedMapgen.getClass()));
selectedMapgen = minetestMapgen;
if (changed) updateVoronoiDiagram();
this.windowFrame.setTitle(getTitle(selectedProfile, selectedMapgen));
}
private String getTitle(MinetestBiomeProfileImpl biome_profile, MinetestBiomeDataOracle mapgen) {
String result = biome_profile == null ? "Biome profile Voronoi diagram" : "Voronoi diagram for " + biome_profile.getName();
if (mapgen instanceof IHistogram2DTransformationProvider) {
result += " with " + mapgen.getName() + " mapgen";
}
return result;
}
private void updateVoronoiDiagram() {
@ -281,7 +332,7 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
() -> {
int altitude = getAltitudeFromDialog();
float freqGraphOpacity = freqGraphSlider.getValue() / 100f;
voronoiPanel.Update(selectedProfile, altitude, freqGraphOpacity, getOptionFlagsFromDialog());
voronoiPanel.Update(selectedProfile, selectedMapgen, altitude, freqGraphOpacity, getOptionFlagsFromDialog());
graphHeading.setText("Biomes at altitude " + altitude);
}
);
@ -319,33 +370,44 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
}
@CalledOnlyBy(AmidstThread.EDT)
private void show(BiomeProfileSelection biomeProfileSelection) {
private void show(BiomeProfileSelection biomeProfileSelection, MapgenRelay mapgen_relay) {
if (this.biomeProfileSelection != null) {
this.biomeProfileSelection.removeUpdateListener(this);
}
if (this.mapgenRelay != null) {
this.mapgenRelay.removeMapgenUpdatedListener(this);
}
this.biomeProfileSelection = biomeProfileSelection;
this.mapgenRelay = mapgen_relay;
BiomeProfile newProfile = null;
if (biomeProfileSelection != null) {
this.biomeProfileSelection.addUpdateListener(this);
newProfile = this.biomeProfileSelection.getCurrentBiomeProfile();
}
IBiomeDataOracle newMapgen = null;
if (mapgen_relay != null) {
this.mapgenRelay.addMapgenUpdatedListener(this);
newMapgen = this.mapgenRelay.getBiomeDataOracle();
}
UpdateSelectedBiomeProfile(newProfile);
UpdateSelectedMapgen(newMapgen);
this.windowFrame.setVisible(true);
}
/** Creates and displays the Voronoi diagram window */
public static void showDiagram(Component parent, BiomeProfileSelection biome_profile_selection, IHistogram2D climate_histogram) {
public static void showDiagram(Component parent, BiomeProfileSelection biome_profile_selection, MapgenRelay mapgen, IHistogram2D climate_histogram) {
if (voronoiWindow == null) {
voronoiWindow = new VoronoiWindow(parent);
}
SwingUtilities.invokeLater(() -> {
voronoiWindow.voronoiPanel.setClimateHistogram(climate_histogram);
voronoiWindow.show(biome_profile_selection);
voronoiWindow.show(biome_profile_selection, mapgen);
});
}
@ -362,5 +424,7 @@ public class VoronoiWindow implements BiomeProfileUpdateListener, ChangeListener
VoronoiPanel.FLAG_SHOWNODES |
VoronoiPanel.FLAG_SHOWCOVERAGE
);
//KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
}
}

View File

@ -0,0 +1,11 @@
package amidst.minetest.world.mapgen;
public interface IHistogram2DTransformationProvider {
/**
* Returns an adjusted histogram (scaled, translated, etc.)
* @param source is the histogram to be transformed
* @param argument is used if the transformation requires an argument (such
* as altitude, for a climate histogram).
*/
IHistogram2D getTransformedHistogram(IHistogram2D source, float argument);
}

View File

@ -0,0 +1,45 @@
package amidst.minetest.world.mapgen;
import java.util.ArrayList;
import amidst.fragment.IBiomeDataOracle;
import amidst.gui.main.WorldSwitchedListener;
import amidst.gui.main.WorldSwitcher;
import amidst.gui.main.viewer.ViewerFacade;
/**
* Adapts the WorldSwitcher to provides notifications for when the mapgen has changed
*/
public class MapgenRelay implements WorldSwitchedListener {
private IBiomeDataOracle biomeDataOracle;
private final ArrayList<MapgenUpdatedListener> listeners = new ArrayList<MapgenUpdatedListener>();
@Override
public void onWorldSwitched(ViewerFacade viewerFacade) {
biomeDataOracle = null;
if (viewerFacade != null) biomeDataOracle = viewerFacade.getBiomeDataOracle();
for (MapgenUpdatedListener listener: listeners) {
listener.onMapgenUpdated(biomeDataOracle);
}
}
public void addMapgenUpdatedListener(MapgenUpdatedListener listener) {
listeners.add(listener);
}
public void removeMapgenUpdatedListener(MapgenUpdatedListener listener) {
listeners.remove(listener);
}
public IBiomeDataOracle getBiomeDataOracle() { return biomeDataOracle; }
public MapgenRelay(WorldSwitcher worldSwitcher) {
ViewerFacade currentViewerFacade = worldSwitcher.addWorldSwitchedListener(this);
// Force a switched event to bring the instance's state up to date.
onWorldSwitched(currentViewerFacade);
}
}

View File

@ -0,0 +1,7 @@
package amidst.minetest.world.mapgen;
import amidst.fragment.IBiomeDataOracle;
public interface MapgenUpdatedListener {
void onMapgenUpdated(IBiomeDataOracle biomeDataOracle);
}

View File

@ -1,9 +1,12 @@
package amidst.minetest.world.oracle;
import javax.vecmath.Point2d;
import amidst.documentation.Immutable;
import amidst.logging.AmidstLogger;
import amidst.logging.AmidstMessageBox;
import amidst.minetest.world.mapgen.Constants;
import amidst.minetest.world.mapgen.IHistogram2D;
import amidst.minetest.world.mapgen.IHistogram2DTransformationProvider;
import amidst.minetest.world.mapgen.InvalidNoiseParamsException;
import amidst.minetest.world.mapgen.MapgenParams;
import amidst.minetest.world.mapgen.MapgenValleysParams;
@ -14,15 +17,15 @@ import amidst.mojangapi.world.coordinates.Resolution;
import amidst.settings.biomeprofile.BiomeProfileSelection;
@Immutable
public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
public class BiomeDataOracleValleys extends MinetestBiomeDataOracle implements IHistogram2DTransformationProvider {
private final MapgenValleysParams valleysParams;
// Raising this reduces the rate of evaporation
/** Raising this reduces the rate of evaporation */
static final float evaporation = 300.0f;
static final float humidity_dropoff = 4.0f;
// Constant to convert altitude chill to heat
/** Constant to convert altitude chill to heat */
static final float alt_to_heat = 20.0f;
// Humidity reduction by altitude
/** Humidity reduction by altitude */
static final float alt_to_humid = 10.0f;
@ -66,7 +69,77 @@ public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
boolean isRiver;
};
/**
* Provides the climate histogram for a Valleys map at the given altitude
* (Valleys mapgen adjusts the climate with altitude and around rivers)
*/
class ValleysClimateHistogram implements IHistogram2D {
IHistogram2D sourceHistogram;
float altitude;
Point2d sampleMean = null;
@Override
public double frequencyOfOccurance(float temperature, float humidity) {
if (altitude > 0.0f) {
if (use_altitude_dry) humidity += alt_to_humid * altitude / altitude_chill;
if (use_altitude_chill) temperature += alt_to_heat * altitude / altitude_chill;
}
// TODO Crazy math to add river humidity distribution to sourceHistogram
// Average heat was increased to balance against altitude chill.
if (use_altitude_chill) temperature -= 5.0f;
// Humidity was scaled down to balance the effect of rivers.
if (humid_rivers) humidity /= 0.8f;
// Now that we've performed the opposite of every transformation Valleys mapgen
// makes to the humidity and temperature, we can obtain the frequencyOfOccurance.
double result = sourceHistogram.frequencyOfOccurance(temperature, humidity);
// results outside the sampling range return NaN rather than zero, but the
// sampling range covers all non-zero values, so we can treat NaN as zero.
return Double.isNaN(result) ? 0 : result;
}
@Override
/**
* Returns the "FrequencyOfOccurance" value at which 'percentile' amount of
* samples will fall beneath.
* So if percentile was 10, then a value between 0 and 1 would be returned such
* that 10% of results from FrequencyOfOccurance() would fall below it.
*/
public double frequencyAtPercentile(double percentile) {
// TODO Auto-generated method stub
return sourceHistogram.frequencyAtPercentile(percentile);
}
@Override
public Point2d getSampleMean() {
if (sampleMean == null) {
sampleMean = new Point2d(sourceHistogram.getSampleMean());
// Average heat was increased to balance against altitude chill.
if (use_altitude_chill) sampleMean.x += 5.0f;
// Humidity was scaled down to balance the effect of rivers.
if (humid_rivers) sampleMean.y *= 0.8f;
// TODO Crazy math to add river humidity distribution to sourceHistogram
if (altitude > 0.0f) {
if (use_altitude_dry) sampleMean.y -= alt_to_humid * altitude / altitude_chill;
if (use_altitude_chill) sampleMean.x -= alt_to_heat * altitude / altitude_chill;
}
}
return sampleMean;
}
public ValleysClimateHistogram(IHistogram2D source_histogram, float altitude) {
this.sourceHistogram = source_histogram;
this.altitude = altitude;
}
}
/**
* @param mapgenCarpathianParams
* @param biomeProfileSelection - if null then a default biomeprofile will be used
@ -115,6 +188,11 @@ public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
use_altitude_dry = (valleysParams.spflags & MapgenValleysParams.FLAG_VALLEYS_ALT_DRY) > 0;
vary_driver_depth = (valleysParams.spflags & MapgenValleysParams.FLAG_VALLEYS_VARY_RIVER_DEPTH) > 0;
}
@Override
public IHistogram2D getTransformedHistogram(IHistogram2D climate_histogram, float altitude) {
return new ValleysClimateHistogram(climate_histogram, altitude);
}
float terrainLevelAtPoint(int x, int z)
{
@ -143,6 +221,11 @@ public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
// Note that tempTerrainNoise.slope, tempTerrainNoise.rivers, and
// tempTerrainNoise.valley have now been updated with new values.
//
// If there is a river then terrain_height is the height of the bottom of the
// river, otherwise it's the height above the water-table.
// tempTerrainNoise.rivers is the water table (and the height of rivers), it's
// greater than terrain_height if there is a river.
// Ground height ignoring riverbeds
float t_alt = Math.max(tempTerrainNoise.rivers, terrain_height);
@ -150,6 +233,7 @@ public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
if (humid_rivers) {
float river_y = tempTerrainNoise.rivers;
if (vary_driver_depth) {
// get a heat value that includes altitude chill
float heat = (use_altitude_chill && (terrain_height > 0.0f || river_y > 0.0f)) ?
tempTerrainNoise.heat - alt_to_heat * Math.max(terrain_height, river_y) / altitude_chill :
tempTerrainNoise.heat;
@ -159,8 +243,11 @@ public class BiomeDataOracleValleys extends MinetestBiomeDataOracle {
river_y += delta * Math.max(t_evap, 0.08f);
}
}
float water_depth = (t_alt - river_y) / humidity_dropoff;
tempTerrainNoise.humidity *= 1.0f + Math.pow(0.5f, Math.max(water_depth, 1.0f));
float water_depth = (t_alt - river_y) / humidity_dropoff; // depth of water-table from surface, not depth of the river
double humidityScale = 1.0f + Math.pow(0.5f, Math.max(water_depth, 1.0f));
tempTerrainNoise.humidity *= humidityScale;
//AmidstLogger.info("*" + humidityScale);
}
if (use_altitude_dry) {
if (t_alt > 0.0f) tempTerrainNoise.humidity -= alt_to_humid * t_alt / altitude_chill;

View File

@ -8,6 +8,7 @@ import amidst.gameengineabstraction.world.biome.IBiome;
import amidst.logging.AmidstLogger;
import amidst.minetest.world.mapgen.ClimateHistogram;
import amidst.minetest.world.mapgen.IHistogram2D;
import amidst.minetest.world.mapgen.IHistogram2DTransformationProvider;
import amidst.minetest.world.mapgen.MapgenParams;
import amidst.minetest.world.mapgen.MinetestBiome;
import amidst.minetest.world.mapgen.MinetestBiomeProfileImpl;
@ -147,6 +148,21 @@ public abstract class MinetestBiomeDataOracle implements IBiomeDataOracle, Biome
return climateHistogram;
}
/**
* Feel free to override this with the name of the mapgen in subclasses.
* (Currently it's only needed by Oracles that implement IHistogram2DTransformationProvider)
* Normally you could get a name from the WorldType, but in some places
* the worldType hasn't been stored.
*/
public String getName() {
String result = this.getClass().getName();
int pos = result.lastIndexOf("DataOracle");
if (pos >= 0 && (pos + 10) < result.length()) {
result = result.substring(pos + 10);
}
return result;
}
@Override
public void onBiomeProfileUpdate(BiomeProfile newBiomeProfile) {
this.biomeProfile = newBiomeProfile;