1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include "MapInfo.h"
5
6 #include "Sim/Misc/GlobalConstants.h"
7 #include "Rendering/GlobalRendering.h"
8
9 #include "MapParser.h"
10 #include "Lua/LuaParser.h"
11 #include "System/Log/ILog.h"
12 #include "System/Exceptions.h"
13 #include "System/myMath.h"
14
15 #include "System/Sound/OpenAL/EFX.h"
16 #include "System/Sound/OpenAL/EFXPresets.h"
17
18 #include <cassert>
19 #include <cfloat>
20 #include <sstream>
21
22 using std::max;
23 using std::min;
24
25
26 // Before delete, the const is const_cast'ed away. There are
27 // no (other) situations where mapInfo may be modified, except
28 // LuaUnsyncedCtrl may change water
29 // LuaSyncedCtrl may change terrainTypes
30 const CMapInfo* mapInfo = NULL;
31
32
CMapInfo(const std::string & mapInfoFile,const string & mapName)33 CMapInfo::CMapInfo(const std::string& mapInfoFile, const string& mapName)
34 {
35 map.name = mapName;
36
37 parser = new MapParser(mapInfoFile);
38 if (!parser->IsValid()) {
39 throw content_error("MapInfo: " + parser->GetErrorLog());
40 }
41
42 LuaParser resParser("gamedata/resources.lua", SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
43 LuaTable resTable;
44
45 if (!resParser.Execute()) {
46 LOG_L(L_ERROR, "%s", resParser.GetErrorLog().c_str());
47 }
48
49 resTable = resParser.GetRoot();
50 resRoot = &resTable;
51
52 ReadGlobal();
53 ReadAtmosphere();
54 ReadGui();
55 ReadSplats();
56 ReadGrass();
57 ReadLight();
58 ReadWater();
59 ReadSMF();
60 ReadSM3();
61 ReadTerrainTypes();
62 ReadPFSConstants();
63 ReadSound();
64
65 //FIXME save all data in an array, so we can destroy the lua context (to save mem)?
66 //delete parser;
67 }
68
~CMapInfo()69 CMapInfo::~CMapInfo()
70 {
71 #if !defined(HEADLESS) && !defined(NO_SOUND)
72 delete efxprops;
73 #endif
74 delete parser;
75 }
76
GetStringValue(const std::string & key) const77 std::string CMapInfo::GetStringValue(const std::string& key) const
78 {
79 assert(parser->GetRoot().IsValid());
80 return parser->GetRoot().GetString(key, "");
81 }
82
ReadGlobal()83 void CMapInfo::ReadGlobal()
84 {
85 const LuaTable& topTable = parser->GetRoot();
86
87 map.description = topTable.GetString("description", map.name);
88 map.author = topTable.GetString("author", "");
89
90 map.hardness = topTable.GetFloat("maphardness", 100.0f);
91 map.notDeformable = topTable.GetBool("notDeformable", false);
92
93 map.gravity = topTable.GetFloat("gravity", 130.0f);
94 map.gravity = max(0.001f, map.gravity);
95 map.gravity = -map.gravity / (GAME_SPEED * GAME_SPEED);
96
97 map.tidalStrength = topTable.GetFloat("tidalStrength", 0.0f);
98 map.maxMetal = topTable.GetFloat("maxMetal", 0.02f);
99 map.extractorRadius = topTable.GetFloat("extractorRadius", 500.0f);
100 map.voidAlphaMin = topTable.GetFloat("voidAlphaMin", 0.9f);
101 map.voidWater = topTable.GetBool("voidWater", false);
102 map.voidGround = topTable.GetBool("voidGround", false);
103
104 // clamps
105 if (-0.001f < map.hardness && map.hardness <= 0.0f)
106 map.hardness = -0.001f;
107 else if (0.0f <= map.hardness && map.hardness < 0.001f)
108 map.hardness = 0.001f;
109 map.tidalStrength = max(0.000f, map.tidalStrength);
110 map.maxMetal = max(0.000f, map.maxMetal);
111 map.extractorRadius = max(0.000f, map.extractorRadius);
112 }
113
114
ReadGui()115 void CMapInfo::ReadGui()
116 {
117 // GUI
118 gui.autoShowMetal = parser->GetRoot().GetBool("autoShowMetal", true);
119 }
120
121
ReadAtmosphere()122 void CMapInfo::ReadAtmosphere()
123 {
124 // MAP\ATMOSPHERE
125 const LuaTable& atmoTable = parser->GetRoot().SubTable("atmosphere");
126 atmosphere_t& atmo = atmosphere;
127
128 atmo.minWind = atmoTable.GetFloat("minWind", 5.0f);
129 atmo.maxWind = atmoTable.GetFloat("maxWind", 25.0f);
130
131 atmo.fogStart = atmoTable.GetFloat("fogStart", 0.1f);
132 atmo.fogEnd = atmoTable.GetFloat("fogEnd", 1.0f);
133 atmo.fogColor = atmoTable.GetFloat3("fogColor", float3(0.7f, 0.7f, 0.8f));
134
135 atmo.skyBox = atmoTable.GetString("skyBox", "");
136 atmo.skyColor = atmoTable.GetFloat3("skyColor", float3(0.1f, 0.15f, 0.7f));
137 atmo.skyDir = atmoTable.GetFloat3("skyDir", -FwdVector);
138 atmo.skyDir.ANormalize();
139 atmo.sunColor = atmoTable.GetFloat3("sunColor", float3(1.0f, 1.0f, 1.0f));
140 atmo.cloudColor = atmoTable.GetFloat3("cloudColor", float3(1.0f, 1.0f, 1.0f));
141 atmo.fluidDensity = atmoTable.GetFloat("fluidDensity", 1.2f * 0.25f);
142 atmo.cloudDensity = atmoTable.GetFloat("cloudDensity", 0.5f);
143
144 // clamps
145 atmo.cloudDensity = max(0.0f, atmo.cloudDensity);
146 atmo.maxWind = max(0.0f, atmo.maxWind);
147 atmo.minWind = max(0.0f, atmo.minWind);
148 atmo.minWind = min(atmo.maxWind, atmo.minWind);
149 }
150
151
ReadSplats()152 void CMapInfo::ReadSplats()
153 {
154 const LuaTable& splatsTable = parser->GetRoot().SubTable("splats");
155
156 splats.texScales = splatsTable.GetFloat4("texScales", float4(0.02f, 0.02f, 0.02f, 0.02f));
157 splats.texMults = splatsTable.GetFloat4("texMults", float4(1.0f, 1.0f, 1.0f, 1.0f));
158 }
159
ReadGrass()160 void CMapInfo::ReadGrass()
161 {
162 const LuaTable& grassTable = parser->GetRoot().SubTable("grass");
163 const LuaTable& mapResTable = parser->GetRoot().SubTable("resources");
164
165 grass.bladeWaveScale = grassTable.GetFloat("bladeWaveScale", 1.0f);
166 grass.bladeWidth = grassTable.GetFloat("bladeWidth", 0.7f);
167 grass.bladeHeight = grassTable.GetFloat("bladeHeight", 4.5f);
168 grass.bladeAngle = grassTable.GetFloat("bladeAngle", 1.0f);
169 grass.maxStrawsPerTurf = grassTable.GetInt("maxStrawsPerTurf", 150);
170 grass.color = grassTable.GetFloat3("bladeColor", float3(0.10f, 0.40f, 0.10f));
171
172 grass.bladeTexName = mapResTable.GetString("grassBladeTex", "");
173 if (!grass.bladeTexName.empty()) { //FIXME only do when file doesn't exists under that path
174 grass.bladeTexName = "maps/" + grass.bladeTexName;
175 }
176 }
177
ReadLight()178 void CMapInfo::ReadLight()
179 {
180 const LuaTable& lightTable = parser->GetRoot().SubTable("lighting");
181
182 light.sunStartAngle = lightTable.GetFloat("sunStartAngle", 0.0f);
183 light.sunOrbitTime = lightTable.GetFloat("sunOrbitTime", 1440.0f);
184 light.sunDir = lightTable.GetFloat4("sunDir", float4(0.0f, 1.0f, 2.0f, FLT_MAX));
185
186 if (light.sunDir.w == FLT_MAX) {
187 // if four params are not specified for sundir, fallback to the old three param format
188 light.sunDir = lightTable.GetFloat3("sunDir", float3(0.0f, 1.0f, 2.0f));
189 light.sunDir.w = FLT_MAX;
190 }
191
192 light.sunDir.ANormalize();
193
194 light.groundAmbientColor = lightTable.GetFloat3("groundAmbientColor", float3(0.5f, 0.5f, 0.5f));
195 light.groundSunColor = lightTable.GetFloat3("groundDiffuseColor", float3(0.5f, 0.5f, 0.5f));
196 light.groundSpecularColor = lightTable.GetFloat3("groundSpecularColor", float3(0.1f, 0.1f, 0.1f));
197 light.groundShadowDensity = lightTable.GetFloat("groundShadowDensity", 0.8f);
198
199 light.unitAmbientColor = lightTable.GetFloat3("unitAmbientColor", float3(0.4f, 0.4f, 0.4f));
200 light.unitSunColor = lightTable.GetFloat3("unitDiffuseColor", float3(0.7f, 0.7f, 0.7f));
201 light.unitSpecularColor = lightTable.GetFloat3("unitSpecularColor", light.unitSunColor);
202 light.unitShadowDensity = lightTable.GetFloat("unitShadowDensity", 0.8f);
203
204 light.specularExponent = lightTable.GetFloat("specularExponent", 100.0f);
205
206 if (light.groundShadowDensity > 1.0 || light.groundShadowDensity < 0.0) {
207 LOG_L(L_WARNING, "MapInfo.lua: Incorrect value \"groundShadowDensity=%f\"! Clamping to 0..1 range!!", light.groundShadowDensity);
208 light.groundShadowDensity = Clamp(light.groundShadowDensity, 0.0f, 1.0f);
209 }
210 if (light.unitShadowDensity > 1.0 || light.unitShadowDensity < 0.0) {
211 LOG_L(L_WARNING, "MapInfo.lua: Incorrect value \"unitShadowDensity=%f\"! Clamping to 0..1 range!!", light.unitShadowDensity);
212 light.unitShadowDensity = Clamp(light.unitShadowDensity, 0.0f, 1.0f);
213 }
214 }
215
216
ReadWater()217 void CMapInfo::ReadWater()
218 {
219 const LuaTable& wt = parser->GetRoot().SubTable("water");
220
221 water.fluidDensity = wt.GetFloat("fluidDensity", 960.0f * 0.25f);
222 water.repeatX = wt.GetFloat("repeatX", 0.0f);
223 water.repeatY = wt.GetFloat("repeatY", 0.0f);
224 water.damage = wt.GetFloat("damage", 0.0f) * (16.0f / GAME_SPEED);
225
226 water.absorb = wt.GetFloat3("absorb", float3(0.0f, 0.0f, 0.0f));
227 water.baseColor = wt.GetFloat3("baseColor", float3(0.0f, 0.0f, 0.0f));
228 water.minColor = wt.GetFloat3("minColor", float3(0.0f, 0.0f, 0.0f));
229
230 water.ambientFactor = wt.GetFloat("ambientFactor", 1.0f);
231 water.diffuseFactor = wt.GetFloat("diffuseFactor", 1.0f);
232 water.specularFactor= wt.GetFloat("specularFactor",1.0f);
233 water.specularPower = wt.GetFloat("specularPower", 20.0f);
234
235 water.planeColor = wt.GetFloat3("planeColor", float3(0.0f, 0.4f, 0.0f));
236 water.hasWaterPlane = wt.KeyExists("planeColor");
237
238 water.surfaceColor = wt.GetFloat3("surfaceColor", float3(0.75f, 0.8f, 0.85f));
239 water.surfaceAlpha = wt.GetFloat("surfaceAlpha", 0.55f);
240 water.diffuseColor = wt.GetFloat3("diffuseColor", float3(1.0f, 1.0f, 1.0f));
241 water.specularColor = wt.GetFloat3("specularColor", light.groundSunColor);
242
243 water.fresnelMin = wt.GetFloat("fresnelMin", 0.2f);
244 water.fresnelMax = wt.GetFloat("fresnelMax", 0.8f);
245 water.fresnelPower = wt.GetFloat("fresnelPower", 4.0f);
246
247 water.reflDistortion = wt.GetFloat("reflectionDistortion", 1.0f);
248
249 water.blurBase = wt.GetFloat("blurBase", 2.0f);
250 water.blurExponent = wt.GetFloat("blurExponent", 1.5f);
251
252 water.perlinStartFreq = wt.GetFloat("perlinStartFreq", 8.0f);
253 water.perlinLacunarity = wt.GetFloat("perlinLacunarity", 3.0f);
254 water.perlinAmplitude = wt.GetFloat("perlinAmplitude", 0.9f);
255 water.windSpeed = wt.GetFloat("windSpeed", 1.0f);
256
257 water.texture = wt.GetString("texture", "");
258 water.foamTexture = wt.GetString("foamTexture", "");
259 water.normalTexture = wt.GetString("normalTexture", "");
260
261 water.shoreWaves = wt.GetBool("shoreWaves", true);
262
263 water.forceRendering = wt.GetBool("forceRendering", false);
264
265 // use 'resources.lua' for missing fields (our the engine defaults)
266 const LuaTable& resGfxMaps = resRoot->SubTable("graphics").SubTable("maps");
267
268 if (!water.texture.empty()) {
269 water.texture = "maps/" + water.texture;
270 } else {
271 water.texture = "bitmaps/" + resGfxMaps.GetString("watertex", "ocean.jpg");
272 }
273
274 if (!water.foamTexture.empty()) {
275 water.foamTexture = "maps/" + water.foamTexture;
276 } else {
277 water.foamTexture = "bitmaps/" + resGfxMaps.GetString("waterfoamtex", "foam.jpg");
278 }
279
280 if (!water.normalTexture.empty()) {
281 water.normalTexture = "maps/" + water.normalTexture;
282 water.numTiles = std::min(16,std::max(1,wt.GetInt("numTiles",1)));
283 } else {
284 water.normalTexture = "bitmaps/" + resGfxMaps.GetString("waternormaltex", "waterbump.png");
285 if (resGfxMaps.KeyExists("waternormaltex")) {
286 water.numTiles = std::min(16,std::max(1,resGfxMaps.GetInt("numTiles",1)));
287 }else{
288 // default texture is a TileSet of 3x3
289 // user-defined textures are expected to be 1x1 (no DynWaves possible)
290 water.numTiles = 3;
291 }
292 }
293
294 // water caustic textures
295 LuaTable caustics = wt.SubTable("caustics");
296 string causticPrefix = "maps/";
297 if (!caustics.IsValid()) {
298 caustics = resRoot->SubTable("graphics").SubTable("caustics");
299 causticPrefix = "bitmaps/";
300 }
301 if (caustics.IsValid()) {
302 for (int i = 1; true; i++) {
303 const string texName = caustics.GetString(i, "");
304 if (texName.empty()) {
305 break;
306 }
307 water.causticTextures.push_back(causticPrefix + texName);
308 }
309 } else {
310 // load the default 32 textures
311 for (int i = 0; i < 32; i++) {
312 char defTex[256];
313 sprintf(defTex, "bitmaps/caustics/caustic%02i.jpg", i);
314 water.causticTextures.push_back(defTex);
315 }
316 }
317 }
318
319
ReadSMF()320 void CMapInfo::ReadSMF()
321 {
322 // SMF specific settings
323 const LuaTable& mapResTable = parser->GetRoot().SubTable("resources");
324
325 smf.detailTexName = mapResTable.GetString("detailTex", "");
326 smf.specularTexName = mapResTable.GetString("specularTex", "");
327 smf.splatDetailTexName = mapResTable.GetString("splatDetailTex", "");
328 smf.splatDistrTexName = mapResTable.GetString("splatDistrTex", "");
329
330 smf.grassShadingTexName = mapResTable.GetString("grassShadingTex", "");
331
332 smf.skyReflectModTexName = mapResTable.GetString("skyReflectModTex", "");
333 smf.detailNormalTexName = mapResTable.GetString("detailNormalTex", "");
334 smf.lightEmissionTexName = mapResTable.GetString("lightEmissionTex", "");
335 smf.parallaxHeightTexName = mapResTable.GetString("parallaxHeightTex", "");
336
337 if (!smf.detailTexName.empty()) {
338 smf.detailTexName = "maps/" + smf.detailTexName;
339 } else {
340 const LuaTable& resGfxMaps = resRoot->SubTable("graphics").SubTable("maps");
341 smf.detailTexName = resGfxMaps.GetString("detailtex", "detailtex2.bmp");
342 smf.detailTexName = "bitmaps/" + smf.detailTexName;
343 }
344
345 if (!smf.specularTexName.empty()) { smf.specularTexName = "maps/" + smf.specularTexName; }
346 if (!smf.splatDetailTexName.empty()) { smf.splatDetailTexName = "maps/" + smf.splatDetailTexName; }
347 if (!smf.splatDistrTexName.empty()) { smf.splatDistrTexName = "maps/" + smf.splatDistrTexName; }
348 if (!smf.grassShadingTexName.empty()) { smf.grassShadingTexName = "maps/" + smf.grassShadingTexName; }
349 if (!smf.skyReflectModTexName.empty()) { smf.skyReflectModTexName = "maps/" + smf.skyReflectModTexName; }
350 if (!smf.detailNormalTexName.empty()) { smf.detailNormalTexName = "maps/" + smf.detailNormalTexName; }
351 if (!smf.lightEmissionTexName.empty()) { smf.lightEmissionTexName = "maps/" + smf.lightEmissionTexName; }
352 if (!smf.parallaxHeightTexName.empty()) { smf.parallaxHeightTexName = "maps/" + smf.parallaxHeightTexName; }
353
354 // height overrides
355 const LuaTable& smfTable = parser->GetRoot().SubTable("smf");
356
357 smf.minHeightOverride = smfTable.KeyExists("minHeight");
358 smf.maxHeightOverride = smfTable.KeyExists("maxHeight");
359 smf.minHeight = smfTable.GetFloat("minHeight", 0.0f);
360 smf.maxHeight = smfTable.GetFloat("maxHeight", 0.0f);
361
362
363 std::stringstream ss;
364
365 for (int i = 0; /* no test */; i++) {
366 ss.str("");
367 ss << "smtFileName" << i;
368
369 if (smfTable.KeyExists(ss.str())) {
370 smf.smtFileNames.push_back(smfTable.GetString(ss.str(), ".smt"));
371 } else {
372 break;
373 }
374 }
375 }
376
377
ReadSM3()378 void CMapInfo::ReadSM3()
379 {
380 // SM3 specific settings
381 sm3.minimap = parser->GetRoot().GetString("minimap", "");
382 }
383
384
ReadTerrainTypes()385 void CMapInfo::ReadTerrainTypes()
386 {
387 const LuaTable& terrTypeTable = parser->GetRoot().SubTable("terrainTypes");
388
389 for (int tt = 0; tt < NUM_TERRAIN_TYPES; tt++) {
390 TerrainType& terrType = terrainTypes[tt];
391 const LuaTable& terrain = terrTypeTable.SubTable(tt);
392 const LuaTable& moveTable = terrain.SubTable("moveSpeeds");
393
394 terrType.name = terrain.GetString("name", "Default");
395 terrType.hardness = terrain.GetFloat("hardness", 1.0f);
396 terrType.receiveTracks = terrain.GetBool("receiveTracks", true);
397
398 terrType.tankSpeed = moveTable.GetFloat("tank", 1.0f);
399 terrType.kbotSpeed = moveTable.GetFloat("kbot", 1.0f);
400 terrType.hoverSpeed = moveTable.GetFloat("hover", 1.0f);
401 terrType.shipSpeed = moveTable.GetFloat("ship", 1.0f);
402
403 // clamps
404 terrType.hardness = max(0.001f, terrType.hardness);
405 terrType.tankSpeed = max(0.000f, terrType.tankSpeed);
406 terrType.kbotSpeed = max(0.000f, terrType.kbotSpeed);
407 terrType.hoverSpeed = max(0.000f, terrType.hoverSpeed);
408 terrType.shipSpeed = max(0.000f, terrType.shipSpeed);
409 }
410 }
411
ReadPFSConstants()412 void CMapInfo::ReadPFSConstants()
413 {
414 const LuaTable& pfsTable = (parser->GetRoot()).SubTable("pfs");
415 // const LuaTable& legacyTable = pfsTable.SubTable("legacyConstants");
416 const LuaTable& qtpfsTable = pfsTable.SubTable("qtpfsConstants");
417
418 // pfs_t::legacy_constants_t& legacyConsts = pfs.legacy_constants;
419 pfs_t::qtpfs_constants_t& qtpfsConsts = pfs.qtpfs_constants;
420
421 qtpfsConsts.layersPerUpdate = qtpfsTable.GetInt("layersPerUpdate", 5);
422 qtpfsConsts.maxTeamSearches = qtpfsTable.GetInt("maxTeamSearches", 25);
423 qtpfsConsts.minNodeSizeX = qtpfsTable.GetInt("minNodeSizeX", 8);
424 qtpfsConsts.minNodeSizeZ = qtpfsTable.GetInt("minNodeSizeZ", 8);
425 qtpfsConsts.maxNodeDepth = qtpfsTable.GetInt("maxNodeDepth", 16);
426 qtpfsConsts.numSpeedModBins = qtpfsTable.GetInt("numSpeedModBins", 10);
427 qtpfsConsts.minSpeedModVal = std::max( 0.0f, qtpfsTable.GetFloat("minSpeedModVal", 0.0f));
428 qtpfsConsts.maxSpeedModVal = std::max(qtpfsConsts.minSpeedModVal, qtpfsTable.GetFloat("maxSpeedModVal", 2.0f));
429 }
430
ReadSound()431 void CMapInfo::ReadSound()
432 {
433 #if !defined(HEADLESS) && !defined(NO_SOUND)
434 const LuaTable& soundTable = parser->GetRoot().SubTable("sound");
435
436 efxprops = new EAXSfxProps();
437
438 const std::string presetname = soundTable.GetString("preset", "default");
439 std::map<std::string, EAXSfxProps>::const_iterator et = eaxPresets.find(presetname);
440 if (et != eaxPresets.end()) {
441 *efxprops = et->second;
442 }
443
444 std::map<std::string, ALuint>::const_iterator it;
445
446 const LuaTable& filterTable = soundTable.SubTable("passfilter");
447 for (it = nameToALFilterParam.begin(); it != nameToALFilterParam.end(); ++it) {
448 const std::string& name = it->first;
449 const int luaType = filterTable.GetType(name);
450
451 if (luaType == LuaTable::NIL)
452 continue;
453
454 const ALuint param = it->second;
455 const unsigned& type = alParamType[param];
456 switch (type) {
457 case EFXParamTypes::FLOAT:
458 if (luaType == LuaTable::NUMBER)
459 efxprops->filter_properties_f[param] = filterTable.GetFloat(name, 0.f);
460 break;
461 }
462 }
463
464 soundTable.SubTable("reverb");
465 for (it = nameToALParam.begin(); it != nameToALParam.end(); ++it) {
466 const std::string& name = it->first;
467 const int luaType = filterTable.GetType(name);
468
469 if (luaType == LuaTable::NIL)
470 continue;
471
472 const ALuint param = it->second;
473 const unsigned& type = alParamType[param];
474 switch (type) {
475 case EFXParamTypes::VECTOR:
476 if (luaType == LuaTable::TABLE)
477 efxprops->properties_v[param] = filterTable.GetFloat3(name, ZeroVector);
478 break;
479 case EFXParamTypes::FLOAT:
480 if (luaType == LuaTable::NUMBER)
481 efxprops->properties_f[param] = filterTable.GetFloat(name, 0.f);
482 break;
483 case EFXParamTypes::BOOL:
484 if (luaType == LuaTable::BOOLEAN)
485 efxprops->properties_i[param] = filterTable.GetBool(name, false);
486 break;
487 }
488 }
489 #endif
490 }
491