Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
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"
30 :
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"
50 :
51 : ///////////////////////////////////////////////////////////////////////////////////////////////////
52 : // CMapWriter constructor: nothing to do at the minute
53 0 : CMapWriter::CMapWriter()
54 : {
55 0 : }
56 :
57 : ///////////////////////////////////////////////////////////////////////////////////////////////////
58 : // SaveMap: try to save the current map to the given file
59 0 : 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)
64 : {
65 0 : CFilePacker packer(FILE_VERSION, "PSMP");
66 :
67 : // build necessary data
68 0 : PackMap(packer, pTerrain);
69 :
70 : try
71 : {
72 : // write it out
73 0 : packer.Write(pathname);
74 : }
75 0 : catch (PSERROR_File_WriteFailed&)
76 : {
77 0 : LOGERROR("Failed to write map '%s'", pathname.string8());
78 0 : return;
79 : }
80 :
81 0 : VfsPath pathnameXML = pathname.ChangeExtension(L".xml");
82 0 : WriteXML(pathnameXML, pWaterMan, pSkyMan, pLightEnv, pCamera, pPostproc, pSimulation2);
83 : }
84 :
85 : ///////////////////////////////////////////////////////////////////////////////////////////////////
86 : // GetHandleIndex: return the index of the given handle in the given list; or 0xFFFF if
87 : // handle isn't in list
88 0 : static u16 GetEntryIndex(const CTerrainTextureEntry* entry, const std::vector<CTerrainTextureEntry*>& entries)
89 : {
90 0 : const size_t limit = std::min(entries.size(), size_t(0xFFFEu)); // paranoia
91 0 : for (size_t i=0;i<limit;i++) {
92 0 : if (entries[i]==entry) {
93 0 : return (u16)i;
94 : }
95 : }
96 :
97 0 : return 0xFFFF;
98 : }
99 :
100 : ///////////////////////////////////////////////////////////////////////////////////////////////////
101 : // EnumTerrainTextures: build lists of textures used by map, and tile descriptions for
102 : // each tile on the terrain
103 0 : void CMapWriter::EnumTerrainTextures(CTerrain *pTerrain,
104 : std::vector<CStr>& textures,
105 : std::vector<STileDesc>& tiles)
106 : {
107 : // the list of all handles in use
108 0 : std::vector<CTerrainTextureEntry*> entries;
109 :
110 : // resize tile array to required size
111 0 : tiles.resize(SQR(pTerrain->GetVerticesPerSide()-1));
112 0 : STileDesc* tileptr=&tiles[0];
113 :
114 : // now iterate through all the tiles
115 0 : const ssize_t patchesPerSide=pTerrain->GetPatchesPerSide();
116 0 : for (ssize_t j=0;j<patchesPerSide;j++) {
117 0 : for (ssize_t i=0;i<patchesPerSide;i++) {
118 0 : for (ssize_t m=0;m<PATCH_SIZE;m++) {
119 0 : for (ssize_t k=0;k<PATCH_SIZE;k++) {
120 0 : CMiniPatch& mp=pTerrain->GetPatch(i,j)->m_MiniPatches[m][k]; // can't fail
121 0 : u16 index=u16(GetEntryIndex(mp.GetTextureEntry(),entries));
122 0 : if (index==0xFFFF) {
123 0 : index=(u16)entries.size();
124 0 : entries.push_back(mp.GetTextureEntry());
125 : }
126 :
127 0 : tileptr->m_Tex1Index=index;
128 0 : tileptr->m_Tex2Index=0xFFFF;
129 0 : tileptr->m_Priority=mp.GetPriority();
130 0 : tileptr++;
131 : }
132 : }
133 : }
134 : }
135 :
136 : // now find the texture names for each handle
137 0 : for (size_t i=0;i<entries.size();i++) {
138 0 : CStr texturename;
139 0 : CTerrainTextureEntry* texentry=entries[i];
140 0 : if (!texentry) {
141 : // uh-oh, this shouldn't happen; set texturename to empty string
142 0 : texturename="";
143 : } else {
144 0 : texturename=texentry->GetTag();
145 : }
146 0 : textures.push_back(texturename);
147 : }
148 0 : }
149 :
150 : ///////////////////////////////////////////////////////////////////////////////////////////////////
151 : // PackMap: pack the current world into a raw data stream
152 0 : void CMapWriter::PackMap(CFilePacker& packer, CTerrain* pTerrain)
153 : {
154 : // now pack everything up
155 0 : PackTerrain(packer, pTerrain);
156 0 : }
157 :
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 0 : void CMapWriter::PackTerrain(CFilePacker& packer, CTerrain* pTerrain)
162 : {
163 : // pack map size
164 0 : const ssize_t mapsize = pTerrain->GetPatchesPerSide();
165 0 : packer.PackSize(mapsize);
166 :
167 : // pack heightmap
168 0 : packer.PackRaw(pTerrain->GetHeightMap(),sizeof(u16)*SQR(pTerrain->GetVerticesPerSide()));
169 :
170 : // the list of textures used by map
171 0 : std::vector<CStr> terrainTextures;
172 : // descriptions of each tile
173 0 : std::vector<STileDesc> tiles;
174 :
175 : // build lists by scanning through the terrain
176 0 : EnumTerrainTextures(pTerrain, terrainTextures, tiles);
177 :
178 : // pack texture names
179 0 : const size_t numTextures = terrainTextures.size();
180 0 : packer.PackSize(numTextures);
181 0 : for (size_t i=0;i<numTextures;i++)
182 0 : packer.PackString(terrainTextures[i]);
183 :
184 : // pack tile data
185 0 : packer.PackRaw(&tiles[0],sizeof(STileDesc)*tiles.size());
186 0 : }
187 :
188 0 : void CMapWriter::WriteXML(const VfsPath& filename,
189 : WaterManager* pWaterMan, SkyManager* pSkyMan,
190 : CLightEnv* pLightEnv, CCamera* pCamera,
191 : CPostprocManager* pPostproc,
192 : CSimulation2* pSimulation2)
193 : {
194 0 : XMLWriter_File xmlMapFile;
195 : {
196 0 : XMLWriter_Element scenarioTag(xmlMapFile, "Scenario");
197 0 : scenarioTag.Attribute("version", static_cast<int>(FILE_VERSION));
198 :
199 0 : ENSURE(pSimulation2);
200 0 : CSimulation2& sim = *pSimulation2;
201 :
202 0 : if (!sim.GetStartupScript().empty())
203 : {
204 0 : XMLWriter_Element scriptTag(xmlMapFile, "Script");
205 0 : scriptTag.Text(sim.GetStartupScript().c_str(), true);
206 : }
207 :
208 : {
209 0 : XMLWriter_Element environmentTag(xmlMapFile, "Environment");
210 0 : environmentTag.Setting("SkySet", pSkyMan->GetSkySet());
211 : {
212 0 : XMLWriter_Element sunColorTag(xmlMapFile, "SunColor");
213 0 : sunColorTag.Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...
214 0 : sunColorTag.Attribute("g", pLightEnv->m_SunColor.Y);
215 0 : sunColorTag.Attribute("b", pLightEnv->m_SunColor.Z);
216 : }
217 : {
218 0 : XMLWriter_Element SunElevationTag(xmlMapFile, "SunElevation");
219 0 : SunElevationTag.Attribute("angle", pLightEnv->m_Elevation);
220 : }
221 : {
222 0 : XMLWriter_Element sunRotationTag(xmlMapFile, "SunRotation");
223 0 : sunRotationTag.Attribute("angle", pLightEnv->m_Rotation);
224 : }
225 : {
226 0 : XMLWriter_Element ambientColorTag(xmlMapFile, "AmbientColor");
227 0 : ambientColorTag.Attribute("r", pLightEnv->m_AmbientColor.X);
228 0 : ambientColorTag.Attribute("g", pLightEnv->m_AmbientColor.Y);
229 0 : ambientColorTag.Attribute("b", pLightEnv->m_AmbientColor.Z);
230 : }
231 : {
232 0 : XMLWriter_Element fogTag(xmlMapFile, "Fog");
233 0 : fogTag.Setting("FogFactor", pLightEnv->m_FogFactor);
234 0 : fogTag.Setting("FogThickness", pLightEnv->m_FogMax);
235 : {
236 0 : XMLWriter_Element fogColorTag(xmlMapFile, "FogColor");
237 0 : fogColorTag.Attribute("r", pLightEnv->m_FogColor.X);
238 0 : fogColorTag.Attribute("g", pLightEnv->m_FogColor.Y);
239 0 : fogColorTag.Attribute("b", pLightEnv->m_FogColor.Z);
240 : }
241 : }
242 :
243 : {
244 0 : XMLWriter_Element waterTag(xmlMapFile, "Water");
245 : {
246 0 : XMLWriter_Element waterBodyTag(xmlMapFile, "WaterBody");
247 0 : CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY);
248 0 : ENSURE(cmpWaterManager);
249 0 : waterBodyTag.Setting("Type", pWaterMan->m_WaterType);
250 : {
251 0 : XMLWriter_Element colorTag(xmlMapFile, "Color");
252 0 : colorTag.Attribute("r", pWaterMan->m_WaterColor.r);
253 0 : colorTag.Attribute("g", pWaterMan->m_WaterColor.g);
254 0 : colorTag.Attribute("b", pWaterMan->m_WaterColor.b);
255 : }
256 : {
257 0 : XMLWriter_Element tintTag(xmlMapFile, "Tint");
258 0 : tintTag.Attribute("r", pWaterMan->m_WaterTint.r);
259 0 : tintTag.Attribute("g", pWaterMan->m_WaterTint.g);
260 0 : tintTag.Attribute("b", pWaterMan->m_WaterTint.b);
261 : }
262 0 : waterBodyTag.Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0));
263 0 : waterBodyTag.Setting("Waviness", pWaterMan->m_Waviness);
264 0 : waterBodyTag.Setting("Murkiness", pWaterMan->m_Murkiness);
265 0 : waterBodyTag.Setting("WindAngle", pWaterMan->m_WindAngle);
266 : }
267 : }
268 :
269 : {
270 0 : XMLWriter_Element postProcTag(xmlMapFile, "Postproc");
271 : {
272 0 : postProcTag.Setting("Brightness", pLightEnv->m_Brightness);
273 0 : postProcTag.Setting("Contrast", pLightEnv->m_Contrast);
274 0 : postProcTag.Setting("Saturation", pLightEnv->m_Saturation);
275 0 : postProcTag.Setting("Bloom", pLightEnv->m_Bloom);
276 0 : postProcTag.Setting("PostEffect", pPostproc->GetPostEffect());
277 : }
278 : }
279 : }
280 :
281 : {
282 0 : XMLWriter_Element cameraTag(xmlMapFile, "Camera");
283 : {
284 0 : XMLWriter_Element positionTag(xmlMapFile, "Position");
285 0 : CVector3D pos = pCamera->GetOrientation().GetTranslation();
286 0 : positionTag.Attribute("x", pos.X);
287 0 : positionTag.Attribute("y", pos.Y);
288 0 : positionTag.Attribute("z", pos.Z);
289 : }
290 :
291 0 : CVector3D in = pCamera->GetOrientation().GetIn();
292 : // Convert to spherical coordinates
293 0 : float rotation = atan2(in.X, in.Z);
294 0 : float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - static_cast<float>(M_PI / 2);
295 :
296 : {
297 0 : XMLWriter_Element rotationTag(xmlMapFile, "Rotation");
298 0 : rotationTag.Attribute("angle", rotation);
299 : }
300 : {
301 0 : XMLWriter_Element declinationTag(xmlMapFile, "Declination");
302 0 : declinationTag.Attribute("angle", declination);
303 : }
304 : }
305 :
306 : {
307 0 : std::string settings = sim.GetMapSettingsString();
308 0 : if (!settings.empty())
309 : {
310 0 : XMLWriter_Element scriptSettingsTag(xmlMapFile, "ScriptSettings");
311 0 : scriptSettingsTag.Text(("\n" + settings + "\n").c_str(), true);
312 : }
313 : }
314 :
315 : {
316 0 : XMLWriter_Element entitiesTag(xmlMapFile, "Entities");
317 0 : CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
318 0 : ENSURE(cmpTemplateManager);
319 :
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 0 : CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);
323 0 : for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
324 : {
325 0 : entity_id_t ent = it->first;
326 :
327 : // Don't save local entities (placement previews etc)
328 0 : if (ENTITY_IS_LOCAL(ent))
329 0 : continue;
330 :
331 0 : XMLWriter_Element entityTag(xmlMapFile, "Entity");
332 0 : entityTag.Attribute("uid", ent);
333 :
334 0 : entityTag.Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent));
335 :
336 0 : CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
337 0 : if (cmpOwnership)
338 0 : entityTag.Setting("Player", static_cast<int>(cmpOwnership->GetOwner()));
339 :
340 0 : CmpPtr<ICmpGarrisonHolder> cmpGarrisonHolder(sim, ent);
341 0 : if (cmpGarrisonHolder)
342 : {
343 0 : std::vector<entity_id_t> garrison = cmpGarrisonHolder->GetEntities();
344 0 : if (!garrison.empty())
345 : {
346 0 : XMLWriter_Element garrisonTag(xmlMapFile, "Garrison");
347 0 : for (const entity_id_t garr_ent_id : garrison)
348 : {
349 0 : XMLWriter_Element garrisonedEntityTag(xmlMapFile, "GarrisonedEntity");
350 0 : garrisonedEntityTag.Attribute("uid", static_cast<int>(garr_ent_id));
351 : }
352 : }
353 : }
354 :
355 0 : CmpPtr<ICmpTurretHolder> cmpTurretHolder(sim, ent);
356 0 : if (cmpTurretHolder)
357 : {
358 0 : std::vector<std::pair<std::string, entity_id_t> > turrets = cmpTurretHolder->GetTurrets();
359 0 : if (!turrets.empty())
360 : {
361 0 : XMLWriter_Element turretTag(xmlMapFile, "Turrets");
362 0 : for (const std::pair<std::string, entity_id_t>& turret : turrets)
363 : {
364 0 : XMLWriter_Element turretedEntityTag(xmlMapFile, "Turret");
365 0 : turretedEntityTag.Attribute("turret", turret.first);
366 0 : turretedEntityTag.Attribute("uid", static_cast<int>(turret.second));
367 : }
368 : }
369 : }
370 :
371 0 : CmpPtr<ICmpPosition> cmpPosition(sim, ent);
372 0 : if (cmpPosition)
373 : {
374 0 : CFixedVector3D pos;
375 0 : if (cmpPosition->IsInWorld())
376 0 : pos = cmpPosition->GetPosition();
377 :
378 0 : CFixedVector3D rot = cmpPosition->GetRotation();
379 : {
380 0 : XMLWriter_Element positionTag(xmlMapFile, "Position");
381 0 : positionTag.Attribute("x", pos.X);
382 0 : positionTag.Attribute("z", pos.Z);
383 : // TODO: height offset etc
384 : }
385 : {
386 0 : XMLWriter_Element orientationTag(xmlMapFile, "Orientation");
387 0 : orientationTag.Attribute("y", rot.Y);
388 : // TODO: X, Z maybe
389 : }
390 : }
391 :
392 0 : CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
393 0 : if (cmpObstruction)
394 : {
395 : // TODO: Currently only necessary because Atlas
396 : // does not set up control groups for its walls.
397 0 : cmpObstruction->ResolveFoundationCollisions();
398 :
399 0 : entity_id_t group = cmpObstruction->GetControlGroup();
400 0 : entity_id_t group2 = cmpObstruction->GetControlGroup2();
401 :
402 : // Don't waste space writing the default control groups.
403 0 : if (group != ent || group2 != INVALID_ENTITY)
404 : {
405 0 : XMLWriter_Element obstructionTag(xmlMapFile, "Obstruction");
406 0 : if (group != ent)
407 0 : obstructionTag.Attribute("group", group);
408 0 : if (group2 != INVALID_ENTITY)
409 0 : obstructionTag.Attribute("group2", group2);
410 : }
411 : }
412 :
413 0 : CmpPtr<ICmpVisual> cmpVisual(sim, ent);
414 0 : if (cmpVisual)
415 : {
416 0 : entity_id_t seed = static_cast<entity_id_t>(cmpVisual->GetActorSeed());
417 0 : if (seed != ent)
418 : {
419 0 : XMLWriter_Element actorTag(xmlMapFile, "Actor");
420 0 : actorTag.Attribute("seed",seed);
421 : }
422 : // TODO: variation/selection strings
423 : }
424 : }
425 : }
426 :
427 :
428 0 : CmpPtr<ICmpCinemaManager> cmpCinemaManager(sim, SYSTEM_ENTITY);
429 0 : if (cmpCinemaManager)
430 : {
431 0 : const std::map<CStrW, CCinemaPath>& paths = cmpCinemaManager->GetPaths();
432 0 : std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin();
433 0 : XMLWriter_Element pathsTag(xmlMapFile, "Paths");
434 :
435 0 : for ( ; it != paths.end(); ++it )
436 : {
437 0 : fixed timescale = it->second.GetTimescale();
438 0 : const std::vector<SplineData>& position_nodes = it->second.GetAllNodes();
439 0 : const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes();
440 0 : const CCinemaData* data = it->second.GetData();
441 :
442 0 : XMLWriter_Element pathTag(xmlMapFile, "Path");
443 0 : pathTag.Attribute("name", data->m_Name);
444 0 : pathTag.Attribute("timescale", timescale);
445 0 : pathTag.Attribute("orientation", data->m_Orientation);
446 0 : pathTag.Attribute("mode", data->m_Mode);
447 0 : pathTag.Attribute("style", data->m_Style);
448 :
449 : struct SEvent
450 : {
451 : fixed time;
452 : const char* type;
453 : CFixedVector3D value;
454 0 : SEvent(fixed time, const char* type, CFixedVector3D value)
455 0 : : time(time), type(type), value(value)
456 0 : {}
457 0 : bool operator<(const SEvent& another) const
458 : {
459 0 : return time < another.time;
460 : }
461 : };
462 :
463 : // All events of a manipulating of camera (position/rotation/target)
464 0 : std::vector<SEvent> events;
465 0 : events.reserve(position_nodes.size() + target_nodes.size());
466 :
467 0 : fixed last_position = fixed::Zero();
468 0 : for (size_t i = 0; i < position_nodes.size(); ++i)
469 : {
470 0 : fixed distance = i > 0 ? position_nodes[i - 1].Distance : fixed::Zero();
471 0 : last_position += distance;
472 0 : events.emplace_back(last_position, "Position", position_nodes[i].Position);
473 : }
474 :
475 0 : fixed last_target = fixed::Zero();
476 0 : for (size_t i = 0; i < target_nodes.size(); ++i)
477 : {
478 0 : fixed distance = i > 0 ? target_nodes[i - 1].Distance : fixed::Zero();
479 0 : last_target += distance;
480 0 : events.emplace_back(last_target, "Target", target_nodes[i].Position);
481 : }
482 :
483 0 : std::sort(events.begin(), events.end());
484 0 : for (size_t i = 0; i < events.size();)
485 : {
486 0 : XMLWriter_Element nodeTag(xmlMapFile, "Node");
487 0 : fixed deltatime = i > 0 ? (events[i].time - events[i - 1].time) : fixed::Zero();
488 0 : nodeTag.Attribute("deltatime", deltatime);
489 0 : size_t j = i;
490 0 : for (; j < events.size() && events[j].time == events[i].time; ++j)
491 : {
492 : // Types: Position/Rotation/Target
493 0 : XMLWriter_Element eventTypeTag(xmlMapFile, events[j].type);
494 0 : eventTypeTag.Attribute("x", events[j].value.X);
495 0 : eventTypeTag.Attribute("y", events[j].value.Y);
496 0 : eventTypeTag.Attribute("z", events[j].value.Z);
497 : }
498 0 : i = j;
499 : }
500 : }
501 : }
502 : }
503 0 : if (!xmlMapFile.StoreVFS(g_VFS, filename))
504 0 : LOGERROR("Failed to write map '%s'", filename.string8());
505 3 : }
|