1 /*
2 biome.cpp
3 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "mg_biome.h"
24 #include "gamedef.h"
25 #include "nodedef.h"
26 #include "map.h" //for ManualMapVoxelManipulator
27 #include "log_types.h"
28 #include "util/numeric.h"
29 #include "main.h"
30 #include "util/mathconstants.h"
31 #include "porting.h"
32 #include "settings.h"
33 
34 const char *BiomeManager::ELEMENT_TITLE = "biome";
35 
36 NoiseParams nparams_biome_def_heat(15, 30, v3f(500.0, 500.0, 500.0), 5349, 2, 0.65);
37 NoiseParams nparams_biome_def_humidity(50, 50, v3f(500.0, 500.0, 500.0), 842, 3, 0.50);
38 
39 
40 ///////////////////////////////////////////////////////////////////////////////
41 
42 
BiomeManager(IGameDef * gamedef)43 BiomeManager::BiomeManager(IGameDef *gamedef)
44 {
45 	NodeResolver *resolver = gamedef->getNodeDefManager()->getResolver();
46 
47 	np_heat     = &nparams_biome_def_heat;
48 	np_humidity = &nparams_biome_def_humidity;
49 
50 	// Create default biome to be used in case none exist
51 	Biome *b = new Biome;
52 
53 	b->id             = 0;
54 	b->name           = "Default";
55 	b->flags          = 0;
56 	b->depth_top      = 0;
57 	b->depth_filler   = 0;
58 	b->height_min     = -MAP_GENERATION_LIMIT;
59 	b->height_max     = MAP_GENERATION_LIMIT;
60 	b->heat_point     = 0.0;
61 	b->humidity_point = 0.0;
62 
63 	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_top);
64 	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_filler);
65 	resolver->addNode("mapgen_water_source", "", CONTENT_AIR, &b->c_water);
66 	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_dust);
67 	resolver->addNode("mapgen_water_source", "", CONTENT_AIR, &b->c_dust_water);
68 	resolver->addNode("mapgen_ice",          "mapgen_water_source", b->c_water, &b->c_ice);
69 
70 	g_settings->getNoiseParams("mgv7_np_heat", nparams_biome_def_heat);
71 	g_settings->getNoiseParams("mgv7_np_humidity", nparams_biome_def_humidity);
72 	year_days = g_settings->getS16("year_days");
73 	weather_heat_season = g_settings->getS16("weather_heat_season");
74 	weather_heat_daily = g_settings->getS16("weather_heat_daily");
75 	weather_heat_width = g_settings->getS16("weather_heat_width");
76 	weather_heat_height = g_settings->getS16("weather_heat_height");
77 	weather_humidity_season = g_settings->getS16("weather_humidity_season");
78 	weather_humidity_daily = g_settings->getS16("weather_humidity_daily");
79 	weather_humidity_width = g_settings->getS16("weather_humidity_width");
80 	weather_humidity_days = g_settings->getS16("weather_humidity_days");
81 	weather_hot_core = g_settings->getS16("weather_hot_core");
82 
83 	add(b);
84 }
85 
86 
87 
~BiomeManager()88 BiomeManager::~BiomeManager()
89 {
90 	//if (biomecache)
91 	//	delete[] biomecache;
92 }
93 
94 
95 
96 // just a PoC, obviously needs optimization later on (precalculate this)
calcBiomes(s16 sx,s16 sy,float * heat_map,float * humidity_map,s16 * height_map,u8 * biomeid_map)97 void BiomeManager::calcBiomes(s16 sx, s16 sy, float *heat_map,
98 	float *humidity_map, s16 *height_map, u8 *biomeid_map)
99 {
100 	int i = 0;
101 	for (int y = 0; y != sy; y++) {
102 		for (int x = 0; x != sx; x++, i++) {
103 			float heat     = (heat_map[i] + 1) * 50;
104 			float humidity = (humidity_map[i] + 1) * 50;
105 			biomeid_map[i] = getBiome(heat, humidity, height_map[i])->id;
106 		}
107 	}
108 }
109 
110 
getBiome(float heat,float humidity,s16 y)111 Biome *BiomeManager::getBiome(float heat, float humidity, s16 y)
112 {
113 	Biome *b, *biome_closest = NULL;
114 	float dist_min = FLT_MAX;
115 
116 	for (size_t i = 1; i < m_elements.size(); i++) {
117 		b = (Biome *)m_elements[i];
118 		if (!b || y > b->height_max || y < b->height_min)
119 			continue;
120 
121 		float d_heat     = heat     - b->heat_point;
122 		float d_humidity = humidity - b->humidity_point;
123 		float dist = (d_heat * d_heat) +
124 					 (d_humidity * d_humidity);
125 		if (dist < dist_min) {
126 			dist_min = dist;
127 			biome_closest = b;
128 		}
129 	}
130 
131 	return biome_closest ? biome_closest : (Biome *)m_elements[0];
132 }
133 
134 
135 ///////////////////////////// Weather
136 
137 
calcBlockHeat(v3POS p,uint64_t seed,float timeofday,float totaltime,bool use_weather)138 s16 BiomeManager::calcBlockHeat(v3POS p, uint64_t seed, float timeofday, float totaltime, bool use_weather) {
139 	//variant 1: full random
140 	//f32 heat = NoisePerlin3D(np_heat, p.X, env->getGameTime()/100, p.Z, seed);
141 
142 	//variant 2: season change based on default heat map
143 	auto heat = NoisePerlin2D(np_heat, p.X, p.Z, seed); // -30..20..70
144 
145 	if (use_weather) {
146 		f32 seasonv = totaltime;
147 		seasonv /= 86400 * year_days; // season change speed
148 		seasonv += (f32)p.X / weather_heat_width; // you can walk to area with other season
149 		seasonv = sin(seasonv * M_PI);
150 		//heat += (weather_heat_season * (heat < offset ? 2 : 0.5)) * seasonv; // -60..0..30
151 		heat += (weather_heat_season) * seasonv; // -60..0..30
152 
153 		// daily change, hotter at sun +4, colder at night -4
154 		heat += weather_heat_daily * (sin(cycle_shift(timeofday, -0.25) * M_PI) - 0.5); //-64..0..34
155 	}
156 	heat += p.Y / weather_heat_height; // upper=colder, lower=hotter, 3c per 1000
157 
158 	if (weather_hot_core && p.Y < -(MAP_GENERATION_LIMIT-weather_hot_core))
159 		heat += 6000 * (1-((float)(p.Y - -MAP_GENERATION_LIMIT)/weather_hot_core)); //hot core, later via realms
160 
161 	return heat;
162 }
163 
164 
calcBlockHumidity(v3POS p,uint64_t seed,float timeofday,float totaltime,bool use_weather)165 s16 BiomeManager::calcBlockHumidity(v3POS p, uint64_t seed, float timeofday, float totaltime, bool use_weather) {
166 
167 	auto humidity = NoisePerlin2D(np_humidity, p.X, p.Z, seed);
168 
169 	if (use_weather) {
170 		f32 seasonv = totaltime;
171 		seasonv /= 86400 * weather_humidity_days; // bad weather change speed (2 days)
172 		seasonv += (f32)p.Z / weather_humidity_width;
173 		humidity += weather_humidity_season * sin(seasonv * M_PI);
174 		humidity += weather_humidity_daily * (sin(cycle_shift(timeofday, -0.1) * M_PI) - 0.5);
175 	}
176 
177 	humidity = rangelim(humidity, 0, 100);
178 
179 	return humidity;
180 }
181