[Gameplay] Reduce loom cost
[0ad.git] / source / graphics / MapWriter.cpp
blob0e56393f19e45fb2edccf9f07d082dba8b52f6bd
1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "Camera.h"
21 #include "CinemaManager.h"
22 #include "GameView.h"
23 #include "LightEnv.h"
24 #include "MapReader.h"
25 #include "MapWriter.h"
26 #include "Patch.h"
27 #include "Terrain.h"
28 #include "TerrainTextureEntry.h"
29 #include "TerrainTextureManager.h"
31 #include "maths/MathUtil.h"
32 #include "maths/NUSpline.h"
33 #include "ps/CLogger.h"
34 #include "ps/Loader.h"
35 #include "ps/Filesystem.h"
36 #include "ps/XML/XMLWriter.h"
37 #include "renderer/PostprocManager.h"
38 #include "renderer/SkyManager.h"
39 #include "renderer/WaterManager.h"
40 #include "simulation2/Simulation2.h"
41 #include "simulation2/components/ICmpCinemaManager.h"
42 #include "simulation2/components/ICmpGarrisonHolder.h"
43 #include "simulation2/components/ICmpObstruction.h"
44 #include "simulation2/components/ICmpOwnership.h"
45 #include "simulation2/components/ICmpPosition.h"
46 #include "simulation2/components/ICmpTemplateManager.h"
47 #include "simulation2/components/ICmpTurretHolder.h"
48 #include "simulation2/components/ICmpVisual.h"
49 #include "simulation2/components/ICmpWaterManager.h"
51 ///////////////////////////////////////////////////////////////////////////////////////////////////
52 // CMapWriter constructor: nothing to do at the minute
53 CMapWriter::CMapWriter()
57 ///////////////////////////////////////////////////////////////////////////////////////////////////
58 // SaveMap: try to save the current map to the given file
59 void CMapWriter::SaveMap(const VfsPath& pathname, CTerrain* pTerrain,
60 WaterManager* pWaterMan, SkyManager* pSkyMan,
61 CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* UNUSED(pCinema),
62 CPostprocManager* pPostproc,
63 CSimulation2* pSimulation2)
65 CFilePacker packer(FILE_VERSION, "PSMP");
67 // build necessary data
68 PackMap(packer, pTerrain);
70 try
72 // write it out
73 packer.Write(pathname);
75 catch (PSERROR_File_WriteFailed&)
77 LOGERROR("Failed to write map '%s'", pathname.string8());
78 return;
81 VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
82 WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pPostproc, pSimulation2);
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
86 // GetHandleIndex: return the index of the given handle in the given list; or 0xFFFF if
87 // handle isn't in list
88 static u16 GetEntryIndex(const CTerrainTextureEntry* entry, const std::vector<CTerrainTextureEntry*>& entries)
90 const size_t limit = std::min(entries.size(), size_t(0xFFFEu)); // paranoia
91 for (size_t i=0;i<limit;i++) {
92 if (entries[i]==entry) {
93 return (u16)i;
97 return 0xFFFF;
100 ///////////////////////////////////////////////////////////////////////////////////////////////////
101 // EnumTerrainTextures: build lists of textures used by map, and tile descriptions for
102 // each tile on the terrain
103 void CMapWriter::EnumTerrainTextures(CTerrain *pTerrain,
104 std::vector<CStr>& textures,
105 std::vector<STileDesc>& tiles)
107 // the list of all handles in use
108 std::vector<CTerrainTextureEntry*> entries;
110 // resize tile array to required size
111 tiles.resize(SQR(pTerrain->GetVerticesPerSide()-1));
112 STileDesc* tileptr=&tiles[0];
114 // now iterate through all the tiles
115 const ssize_t patchesPerSide=pTerrain->GetPatchesPerSide();
116 for (ssize_t j=0;j<patchesPerSide;j++) {
117 for (ssize_t i=0;i<patchesPerSide;i++) {
118 for (ssize_t m=0;m<PATCH_SIZE;m++) {
119 for (ssize_t k=0;k<PATCH_SIZE;k++) {
120 CMiniPatch& mp=pTerrain->GetPatch(i,j)->m_MiniPatches[m][k]; // can't fail
121 u16 index=u16(GetEntryIndex(mp.GetTextureEntry(),entries));
122 if (index==0xFFFF) {
123 index=(u16)entries.size();
124 entries.push_back(mp.GetTextureEntry());
127 tileptr->m_Tex1Index=index;
128 tileptr->m_Tex2Index=0xFFFF;
129 tileptr->m_Priority=mp.GetPriority();
130 tileptr++;
136 // now find the texture names for each handle
137 for (size_t i=0;i<entries.size();i++) {
138 CStr texturename;
139 CTerrainTextureEntry* texentry=entries[i];
140 if (!texentry) {
141 // uh-oh, this shouldn't happen; set texturename to empty string
142 texturename="";
143 } else {
144 texturename=texentry->GetTag();
146 textures.push_back(texturename);
150 ///////////////////////////////////////////////////////////////////////////////////////////////////
151 // PackMap: pack the current world into a raw data stream
152 void CMapWriter::PackMap(CFilePacker& packer, CTerrain* pTerrain)
154 // now pack everything up
155 PackTerrain(packer, pTerrain);
158 ///////////////////////////////////////////////////////////////////////////////////////////////////
159 // PackTerrain: pack the terrain onto the end of the output data stream
160 // - data: map size, heightmap, list of textures used by map, texture tile assignments
161 void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
163 // pack map size
164 const ssize_t mapsize = pTerrain->GetPatchesPerSide();
165 packer.PackSize(mapsize);
167 // pack heightmap
168 packer.PackRaw(pTerrain->GetHeightMap(),sizeof(u16)*SQR(pTerrain->GetVerticesPerSide()));
170 // the list of textures used by map
171 std::vector<CStr> terrainTextures;
172 // descriptions of each tile
173 std::vector<STileDesc> tiles;
175 // build lists by scanning through the terrain
176 EnumTerrainTextures(pTerrain, terrainTextures, tiles);
178 // pack texture names
179 const size_t numTextures = terrainTextures.size();
180 packer.PackSize(numTextures);
181 for (size_t i=0;i<numTextures;i++)
182 packer.PackString(terrainTextures[i]);
184 // pack tile data
185 packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());
188 void CMapWriter::WriteXML(const VfsPath& filename,
189 WaterManager* pWaterMan, SkyManager* pSkyMan,
190 CLightEnv* pLightEnv, CCamera* pCamera,
191 CPostprocManager* pPostproc,
192 CSimulation2* pSimulation2)
194 XMLWriter_File xmlMapFile;
196 XMLWriter_Element scenarioTag(xmlMapFile, "Scenario");
197 scenarioTag.Attribute("version", static_cast<int>(FILE_VERSION));
199 ENSURE(pSimulation2);
200 CSimulation2& sim = *pSimulation2;
202 if (!sim.GetStartupScript().empty())
204 XMLWriter_Element scriptTag(xmlMapFile, "Script");
205 scriptTag.Text(sim.GetStartupScript().c_str(), true);
209 XMLWriter_Element environmentTag(xmlMapFile, "Environment");
210 environmentTag.Setting("SkySet", pSkyMan->GetSkySet());
212 XMLWriter_Element sunColorTag(xmlMapFile, "SunColor");
213 sunColorTag.Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...
214 sunColorTag.Attribute("g", pLightEnv->m_SunColor.Y);
215 sunColorTag.Attribute("b", pLightEnv->m_SunColor.Z);
218 XMLWriter_Element SunElevationTag(xmlMapFile, "SunElevation");
219 SunElevationTag.Attribute("angle", pLightEnv->m_Elevation);
222 XMLWriter_Element sunRotationTag(xmlMapFile, "SunRotation");
223 sunRotationTag.Attribute("angle", pLightEnv->m_Rotation);
226 XMLWriter_Element ambientColorTag(xmlMapFile, "AmbientColor");
227 ambientColorTag.Attribute("r", pLightEnv->m_AmbientColor.X);
228 ambientColorTag.Attribute("g", pLightEnv->m_AmbientColor.Y);
229 ambientColorTag.Attribute("b", pLightEnv->m_AmbientColor.Z);
232 XMLWriter_Element fogTag(xmlMapFile, "Fog");
233 fogTag.Setting("FogFactor", pLightEnv->m_FogFactor);
234 fogTag.Setting("FogThickness", pLightEnv->m_FogMax);
236 XMLWriter_Element fogColorTag(xmlMapFile, "FogColor");
237 fogColorTag.Attribute("r", pLightEnv->m_FogColor.X);
238 fogColorTag.Attribute("g", pLightEnv->m_FogColor.Y);
239 fogColorTag.Attribute("b", pLightEnv->m_FogColor.Z);
244 XMLWriter_Element waterTag(xmlMapFile, "Water");
246 XMLWriter_Element waterBodyTag(xmlMapFile, "WaterBody");
247 CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY);
248 ENSURE(cmpWaterManager);
249 waterBodyTag.Setting("Type", pWaterMan->m_WaterType);
251 XMLWriter_Element colorTag(xmlMapFile, "Color");
252 colorTag.Attribute("r", pWaterMan->m_WaterColor.r);
253 colorTag.Attribute("g", pWaterMan->m_WaterColor.g);
254 colorTag.Attribute("b", pWaterMan->m_WaterColor.b);
257 XMLWriter_Element tintTag(xmlMapFile, "Tint");
258 tintTag.Attribute("r", pWaterMan->m_WaterTint.r);
259 tintTag.Attribute("g", pWaterMan->m_WaterTint.g);
260 tintTag.Attribute("b", pWaterMan->m_WaterTint.b);
262 waterBodyTag.Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0));
263 waterBodyTag.Setting("Waviness", pWaterMan->m_Waviness);
264 waterBodyTag.Setting("Murkiness", pWaterMan->m_Murkiness);
265 waterBodyTag.Setting("WindAngle", pWaterMan->m_WindAngle);
270 XMLWriter_Element postProcTag(xmlMapFile, "Postproc");
272 postProcTag.Setting("Brightness", pLightEnv->m_Brightness);
273 postProcTag.Setting("Contrast", pLightEnv->m_Contrast);
274 postProcTag.Setting("Saturation", pLightEnv->m_Saturation);
275 postProcTag.Setting("Bloom", pLightEnv->m_Bloom);
276 postProcTag.Setting("PostEffect", pPostproc->GetPostEffect());
282 XMLWriter_Element cameraTag(xmlMapFile, "Camera");
284 XMLWriter_Element positionTag(xmlMapFile, "Position");
285 CVector3D pos = pCamera->GetOrientation().GetTranslation();
286 positionTag.Attribute("x", pos.X);
287 positionTag.Attribute("y", pos.Y);
288 positionTag.Attribute("z", pos.Z);
291 CVector3D in = pCamera->GetOrientation().GetIn();
292 // Convert to spherical coordinates
293 float rotation = atan2(in.X, in.Z);
294 float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - static_cast<float>(M_PI / 2);
297 XMLWriter_Element rotationTag(xmlMapFile, "Rotation");
298 rotationTag.Attribute("angle", rotation);
301 XMLWriter_Element declinationTag(xmlMapFile, "Declination");
302 declinationTag.Attribute("angle", declination);
307 std::string settings = sim.GetMapSettingsString();
308 if (!settings.empty())
310 XMLWriter_Element scriptSettingsTag(xmlMapFile, "ScriptSettings");
311 scriptSettingsTag.Text(("\n" + settings + "\n").c_str(), true);
316 XMLWriter_Element entitiesTag(xmlMapFile, "Entities");
317 CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
318 ENSURE(cmpTemplateManager);
320 // This will probably need to be changed in the future, but for now we'll
321 // just save all entities that have a position
322 CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);
323 for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
325 entity_id_t ent = it->first;
327 // Don't save local entities (placement previews etc)
328 if (ENTITY_IS_LOCAL(ent))
329 continue;
331 XMLWriter_Element entityTag(xmlMapFile, "Entity");
332 entityTag.Attribute("uid", ent);
334 entityTag.Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent));
336 CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
337 if (cmpOwnership)
338 entityTag.Setting("Player", static_cast<int>(cmpOwnership->GetOwner()));
340 CmpPtr<ICmpGarrisonHolder> cmpGarrisonHolder(sim, ent);
341 if (cmpGarrisonHolder)
343 std::vector<entity_id_t> garrison = cmpGarrisonHolder->GetEntities();
344 if (!garrison.empty())
346 XMLWriter_Element garrisonTag(xmlMapFile, "Garrison");
347 for (const entity_id_t garr_ent_id : garrison)
349 XMLWriter_Element garrisonedEntityTag(xmlMapFile, "GarrisonedEntity");
350 garrisonedEntityTag.Attribute("uid", static_cast<int>(garr_ent_id));
355 CmpPtr<ICmpTurretHolder> cmpTurretHolder(sim, ent);
356 if (cmpTurretHolder)
358 std::vector<std::pair<std::string, entity_id_t> > turrets = cmpTurretHolder->GetTurrets();
359 if (!turrets.empty())
361 XMLWriter_Element turretTag(xmlMapFile, "Turrets");
362 for (const std::pair<std::string, entity_id_t>& turret : turrets)
364 XMLWriter_Element turretedEntityTag(xmlMapFile, "Turret");
365 turretedEntityTag.Attribute("turret", turret.first);
366 turretedEntityTag.Attribute("uid", static_cast<int>(turret.second));
371 CmpPtr<ICmpPosition> cmpPosition(sim, ent);
372 if (cmpPosition)
374 CFixedVector3D pos;
375 if (cmpPosition->IsInWorld())
376 pos = cmpPosition->GetPosition();
378 CFixedVector3D rot = cmpPosition->GetRotation();
380 XMLWriter_Element positionTag(xmlMapFile, "Position");
381 positionTag.Attribute("x", pos.X);
382 positionTag.Attribute("z", pos.Z);
383 // TODO: height offset etc
386 XMLWriter_Element orientationTag(xmlMapFile, "Orientation");
387 orientationTag.Attribute("y", rot.Y);
388 // TODO: X, Z maybe
392 CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
393 if (cmpObstruction)
395 // TODO: Currently only necessary because Atlas
396 // does not set up control groups for its walls.
397 cmpObstruction->ResolveFoundationCollisions();
399 entity_id_t group = cmpObstruction->GetControlGroup();
400 entity_id_t group2 = cmpObstruction->GetControlGroup2();
402 // Don't waste space writing the default control groups.
403 if (group != ent || group2 != INVALID_ENTITY)
405 XMLWriter_Element obstructionTag(xmlMapFile, "Obstruction");
406 if (group != ent)
407 obstructionTag.Attribute("group", group);
408 if (group2 != INVALID_ENTITY)
409 obstructionTag.Attribute("group2", group2);
413 CmpPtr<ICmpVisual> cmpVisual(sim, ent);
414 if (cmpVisual)
416 entity_id_t seed = static_cast<entity_id_t>(cmpVisual->GetActorSeed());
417 if (seed != ent)
419 XMLWriter_Element actorTag(xmlMapFile, "Actor");
420 actorTag.Attribute("seed",seed);
422 // TODO: variation/selection strings
428 CmpPtr<ICmpCinemaManager> cmpCinemaManager(sim, SYSTEM_ENTITY);
429 if (cmpCinemaManager)
431 const std::map<CStrW, CCinemaPath>& paths = cmpCinemaManager->GetPaths();
432 std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin();
433 XMLWriter_Element pathsTag(xmlMapFile, "Paths");
435 for ( ; it != paths.end(); ++it )
437 fixed timescale = it->second.GetTimescale();
438 const std::vector<SplineData>& position_nodes = it->second.GetAllNodes();
439 const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes();
440 const CCinemaData* data = it->second.GetData();
442 XMLWriter_Element pathTag(xmlMapFile, "Path");
443 pathTag.Attribute("name", data->m_Name);
444 pathTag.Attribute("timescale", timescale);
445 pathTag.Attribute("orientation", data->m_Orientation);
446 pathTag.Attribute("mode", data->m_Mode);
447 pathTag.Attribute("style", data->m_Style);
449 struct SEvent
451 fixed time;
452 const char* type;
453 CFixedVector3D value;
454 SEvent(fixed time, const char* type, CFixedVector3D value)
455 : time(time), type(type), value(value)
457 bool operator<(const SEvent& another) const
459 return time < another.time;
463 // All events of a manipulating of camera (position/rotation/target)
464 std::vector<SEvent> events;
465 events.reserve(position_nodes.size() + target_nodes.size());
467 fixed last_position = fixed::Zero();
468 for (size_t i = 0; i < position_nodes.size(); ++i)
470 fixed distance = i > 0 ? position_nodes[i - 1].Distance : fixed::Zero();
471 last_position += distance;
472 events.emplace_back(last_position, "Position", position_nodes[i].Position);
475 fixed last_target = fixed::Zero();
476 for (size_t i = 0; i < target_nodes.size(); ++i)
478 fixed distance = i > 0 ? target_nodes[i - 1].Distance : fixed::Zero();
479 last_target += distance;
480 events.emplace_back(last_target, "Target", target_nodes[i].Position);
483 std::sort(events.begin(), events.end());
484 for (size_t i = 0; i < events.size();)
486 XMLWriter_Element nodeTag(xmlMapFile, "Node");
487 fixed deltatime = i > 0 ? (events[i].time - events[i - 1].time) : fixed::Zero();
488 nodeTag.Attribute("deltatime", deltatime);
489 size_t j = i;
490 for (; j < events.size() && events[j].time == events[i].time; ++j)
492 // Types: Position/Rotation/Target
493 XMLWriter_Element eventTypeTag(xmlMapFile, events[j].type);
494 eventTypeTag.Attribute("x", events[j].value.X);
495 eventTypeTag.Attribute("y", events[j].value.Y);
496 eventTypeTag.Attribute("z", events[j].value.Z);
498 i = j;
503 if (!xmlMapFile.StoreVFS(g_VFS, filename))
504 LOGERROR("Failed to write map '%s'", filename.string8());