#include "precompiled.h" #include "MapReader.h" #include "graphics/Camera.h" #include "graphics/GameView.h" #include "graphics/Model.h" #include "graphics/ObjectManager.h" #include "graphics/Patch.h" #include "graphics/Terrain.h" #include "graphics/TextureEntry.h" #include "graphics/TextureManager.h" #include "graphics/Unit.h" #include "graphics/UnitManager.h" #include "lib/timer.h" #include "lib/types.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/Game.h" #include "ps/Loader.h" #include "ps/LoaderThunks.h" #include "ps/XML/Xeromyces.h" #include "renderer/SkyManager.h" #include "renderer/WaterManager.h" #include "simulation/Entity.h" #include "simulation/EntityManager.h" #include "simulation/EntityTemplate.h" #include "simulation/EntityTemplateCollection.h" #define LOG_CATEGORY "graphics" CMapReader::CMapReader() : xml_reader(0) { cur_terrain_tex = 0; // important - resets generator state } // LoadMap: try to load the map from given file; reinitialise the scene to new data if successful void CMapReader::LoadMap(const char* filename, CTerrain *pTerrain_, CUnitManager *pUnitMan_, WaterManager* pWaterMan_, SkyManager* pSkyMan_, CLightEnv *pLightEnv_, CCamera *pCamera_, CCinemaManager* pCinema_) { // latch parameters (held until DelayedLoadFinished) pTerrain = pTerrain_; pUnitMan = pUnitMan_; pLightEnv = pLightEnv_; pCamera = pCamera_; pWaterMan = pWaterMan_; pSkyMan = pSkyMan_; pCinema = pCinema_; // [25ms] unpacker.Read(filename, "PSMP"); // check version if (unpacker.GetVersion() < FILE_READ_VERSION) { throw PSERROR_File_InvalidVersion(); } // delete all existing entities g_EntityManager.deleteAll(); // delete all remaining non-entity units pUnitMan->DeleteAll(); g_UnitMan.SetNextID(0); // unpack the data RegMemFun(this, &CMapReader::UnpackMap, L"CMapReader::UnpackMap", 1200); if (unpacker.GetVersion() >= 3) { // read the corresponding XML file filename_xml = filename; filename_xml = filename_xml.Left(filename_xml.Length()-4) + ".xml"; RegMemFun(this, &CMapReader::ReadXML, L"CMapReader::ReadXML", 5800); } // apply data to the world RegMemFun(this, &CMapReader::ApplyData, L"CMapReader::ApplyData", 5); RegMemFun(this, &CMapReader::DelayLoadFinished, L"CMapReader::DelayLoadFinished", 5); } // UnpackMap: unpack the given data from the raw data stream into local variables int CMapReader::UnpackMap() { // now unpack everything into local data int ret = UnpackTerrain(); if(ret != 0) // failed or timed out return ret; if (unpacker.GetVersion() < 4) UnpackObjects(); if (unpacker.GetVersion() >= 2 && unpacker.GetVersion() < 4) UnpackLightEnv(); return 0; } // UnpackLightEnv: unpack lighting parameters from input stream void CMapReader::UnpackLightEnv() { unpacker.UnpackRaw(&m_LightEnv.m_SunColor, sizeof(m_LightEnv.m_SunColor)); unpacker.UnpackRaw(&m_LightEnv.m_Elevation, sizeof(m_LightEnv.m_Elevation)); unpacker.UnpackRaw(&m_LightEnv.m_Rotation, sizeof(m_LightEnv.m_Rotation)); unpacker.UnpackRaw(&m_LightEnv.m_TerrainAmbientColor, sizeof(m_LightEnv.m_TerrainAmbientColor)); unpacker.UnpackRaw(&m_LightEnv.m_UnitsAmbientColor, sizeof(m_LightEnv.m_UnitsAmbientColor)); m_LightEnv.CalculateSunDirection(); } // UnpackObjects: unpack world objects from input stream void CMapReader::UnpackObjects() { // unpack object types u32 numObjTypes; unpacker.UnpackRaw(&numObjTypes, sizeof(numObjTypes)); m_ObjectTypes.resize(numObjTypes); for (u32 i=0; iGetHandle(); m_TerrainTextures.push_back(handle); cur_terrain_tex++; LDR_CHECK_TIMEOUT(cur_terrain_tex, num_terrain_tex); } // unpack tile data [3ms] u32 tilesPerSide = m_MapSize*PATCH_SIZE; m_Tiles.resize(SQR(tilesPerSide)); unpacker.UnpackRaw(&m_Tiles[0], (u32)(sizeof(STileDesc)*m_Tiles.size())); // reset generator state. cur_terrain_tex = 0; return 0; } int CMapReader::UnpackCinema() { size_t numTracks; unpacker.UnpackRaw(&numTracks, (u32)sizeof(size_t)); for ( size_t track=0; track < numTracks; ++track ) { CCinemaTrack trackObj; std::vector paths; CStr name; size_t numPaths; CVector3D startRotation; float timescale; unpacker.UnpackString(name); unpacker.UnpackRaw(×cale, sizeof(float)); unpacker.UnpackRaw(&numPaths, sizeof(size_t)); unpacker.UnpackRaw(&startRotation, sizeof(CVector3D)); trackObj.SetStartRotation(startRotation); trackObj.SetTimescale(timescale); for ( size_t i=0; iInitialize(m_MapSize, &m_Heightmap[0]); // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (u32 j=0; jGetPatch(i,j)->m_MiniPatches[m][k]; mp.Tex1 = m_TerrainTextures[tileptr->m_Tex1Index]; mp.Tex1Priority = tileptr->m_Priority; tileptr++; } } } } // add new objects for (size_t i = 0; i < m_Objects.size(); ++i) { if (unpacker.GetVersion() < 3) { debug_warn("Old unsupported map version - objects will be missing"); // (getTemplateByActor doesn't work, since entity templates are now // loaded on demand) } std::set selections; // TODO: read from file CUnit* unit = g_UnitMan.CreateUnit(m_ObjectTypes.at(m_Objects[i].m_ObjectIndex), NULL, selections); if (unit) { CMatrix3D transform; memcpy2(&transform._11, m_Objects[i].m_Transform, sizeof(float)*16); unit->GetModel()->SetTransform(transform); } } //Make units start out conforming correctly g_EntityManager.conformAll(); if (unpacker.GetVersion() >= 2) { // copy over the lighting parameters *pLightEnv = m_LightEnv; } return 0; } // Holds various state data while reading maps, so that loading can be // interrupted (e.g. to update the progress display) then later resumed. class CXMLReader { public: CXMLReader(const CStr& xml_filename, CMapReader& mapReader) : m_MapReader(mapReader) { Init(xml_filename); } // return semantics: see Loader.cpp!LoadFunc. int ProgressiveRead(); private: CXeromyces xmb_file; CMapReader& m_MapReader; int el_entity; int el_tracks; int el_template, el_player; int el_position, el_orientation; int el_nonentity; int el_actor; int at_x, at_y, at_z; int at_angle; XMBElementList nodes; // children of root // loop counters int node_idx; int entity_idx, nonentity_idx; // # entities+nonentities processed and total (for progress calc) int completed_jobs, total_jobs; void Init(const CStr& xml_filename); void ReadEnvironment(XMBElement parent); void ReadCamera(XMBElement parent); void ReadCinema(XMBElement parent); int ReadEntities(XMBElement parent, double end_time); int ReadNonEntities(XMBElement parent, double end_time); NO_COPY_CTOR(CXMLReader); }; void CXMLReader::Init(const CStr& xml_filename) { // must only assign once, so do it here node_idx = entity_idx = nonentity_idx = 0; if (xmb_file.Load(xml_filename) != PSRETURN_OK) throw PSERROR_File_ReadFailed(); // define the elements and attributes that are frequently used in the XML file, // so we don't need to do lots of string construction and comparison when // reading the data. // (Needs to be synchronised with the list in CXMLReader - ugh) #define EL(x) el_##x = xmb_file.getElementID(#x) #define AT(x) at_##x = xmb_file.getAttributeID(#x) EL(entity); EL(tracks); EL(template); EL(player); EL(position); EL(orientation); EL(nonentity); EL(actor); AT(x); AT(y); AT(z); AT(angle); #undef AT #undef EL XMBElement root = xmb_file.getRoot(); debug_assert(xmb_file.getElementString(root.getNodeName()) == "Scenario"); nodes = root.getChildNodes(); // find out total number of entities+nonentities // (used when calculating progress) completed_jobs = 0; total_jobs = 0; for (int i = 0; i < nodes.Count; i++) total_jobs += nodes.item(i).getChildNodes().Count; } void CXMLReader::ReadEnvironment(XMBElement parent) { #define EL(x) int el_##x = xmb_file.getElementID(#x) #define AT(x) int at_##x = xmb_file.getAttributeID(#x) EL(skyset); EL(suncolour); EL(sunelevation); EL(sunrotation); EL(terrainambientcolour); EL(unitsambientcolour); EL(terrainshadowtransparency); EL(water); EL(waterbody); EL(type); EL(colour); EL(height); EL(shininess); EL(waviness); AT(r); AT(g); AT(b); #undef AT #undef EL XERO_ITER_EL(parent, element) { int element_name = element.getNodeName(); XMBAttributeList attrs = element.getAttributes(); if (element_name == el_skyset) { m_MapReader.pSkyMan->SetSkySet(element.getText()); } else if (element_name == el_suncolour) { m_MapReader.m_LightEnv.m_SunColor = RGBColor( CStr(attrs.getNamedItem(at_r)).ToFloat(), CStr(attrs.getNamedItem(at_g)).ToFloat(), CStr(attrs.getNamedItem(at_b)).ToFloat()); } else if (element_name == el_sunelevation) { m_MapReader.m_LightEnv.m_Elevation = CStr(attrs.getNamedItem(at_angle)).ToFloat(); } else if (element_name == el_sunrotation) { m_MapReader.m_LightEnv.m_Rotation = CStr(attrs.getNamedItem(at_angle)).ToFloat(); } else if (element_name == el_terrainambientcolour) { m_MapReader.m_LightEnv.m_TerrainAmbientColor = RGBColor( CStr(attrs.getNamedItem(at_r)).ToFloat(), CStr(attrs.getNamedItem(at_g)).ToFloat(), CStr(attrs.getNamedItem(at_b)).ToFloat()); } else if (element_name == el_unitsambientcolour) { m_MapReader.m_LightEnv.m_UnitsAmbientColor = RGBColor( CStr(attrs.getNamedItem(at_r)).ToFloat(), CStr(attrs.getNamedItem(at_g)).ToFloat(), CStr(attrs.getNamedItem(at_b)).ToFloat()); } else if (element_name == el_terrainshadowtransparency) { m_MapReader.m_LightEnv.SetTerrainShadowTransparency(CStr(element.getText()).ToFloat()); } else if (element_name == el_water) { XERO_ITER_EL(element, waterbody) { debug_assert(waterbody.getNodeName() == el_waterbody); XERO_ITER_EL(waterbody, waterelement) { int element_name = waterelement.getNodeName(); if (element_name == el_type) { // TODO: implement this, when WaterManager supports it } else if (element_name == el_colour) { XMBAttributeList attrs = waterelement.getAttributes(); m_MapReader.pWaterMan->m_WaterColor = CColor( CStr(attrs.getNamedItem(at_r)).ToFloat(), CStr(attrs.getNamedItem(at_g)).ToFloat(), CStr(attrs.getNamedItem(at_b)).ToFloat(), 1.f); } else if (element_name == el_height) { m_MapReader.pWaterMan->m_WaterHeight = CStr(waterelement.getText()).ToFloat(); } else if (element_name == el_shininess) { m_MapReader.pWaterMan->m_Shininess = CStr(waterelement.getText()).ToFloat(); } else if (element_name == el_waviness) { m_MapReader.pWaterMan->m_Waviness = CStr(waterelement.getText()).ToFloat(); } else debug_warn("Invalid map XML data"); } } } else debug_warn("Invalid map XML data"); } m_MapReader.m_LightEnv.CalculateSunDirection(); } void CXMLReader::ReadCamera(XMBElement parent) { #define EL(x) int el_##x = xmb_file.getElementID(#x) #define AT(x) int at_##x = xmb_file.getAttributeID(#x) EL(declination); EL(rotation); EL(position); AT(angle); AT(x); AT(y); AT(z); #undef AT #undef EL float declination = DEGTORAD(30.f), rotation = DEGTORAD(-45.f); CVector3D translation = CVector3D(100, 150, -100); XERO_ITER_EL(parent, element) { int element_name = element.getNodeName(); XMBAttributeList attrs = element.getAttributes(); if (element_name == el_declination) { declination = CStr(attrs.getNamedItem(at_angle)).ToFloat(); } else if (element_name == el_rotation) { rotation = CStr(attrs.getNamedItem(at_angle)).ToFloat(); } else if (element_name == el_position) { translation = CVector3D( CStr(attrs.getNamedItem(at_x)).ToFloat(), CStr(attrs.getNamedItem(at_y)).ToFloat(), CStr(attrs.getNamedItem(at_z)).ToFloat()); } else debug_warn("Invalid map XML data"); } m_MapReader.pCamera->m_Orientation.SetXRotation(declination); m_MapReader.pCamera->m_Orientation.RotateY(rotation); m_MapReader.pCamera->m_Orientation.Translate(translation); m_MapReader.pCamera->UpdateFrustum(); } void CXMLReader::ReadCinema(XMBElement parent) { #define EL(x) int el_##x = xmb_file.getElementID(#x) #define AT(x) int at_##x = xmb_file.getAttributeID(#x) EL(track); EL(startrotation); EL(path); EL(rotation); EL(distortion); EL(node); AT(name); AT(timescale); AT(mode); AT(style); AT(growth); AT(switch); AT(x); AT(y); AT(z); AT(t); #undef EL #undef AT std::map trackList; XERO_ITER_EL(parent, element) { int elementName = element.getNodeName(); if ( elementName == el_track ) { CCinemaTrack track; XMBAttributeList attrs = element.getAttributes(); CStrW name( CStr(attrs.getNamedItem(at_name)) ); float timescale = CStr(attrs.getNamedItem(at_timescale)).ToFloat(); track.SetTimescale(timescale); XERO_ITER_EL(element, trackChild) { elementName = trackChild.getNodeName(); if ( elementName == el_startrotation ) { attrs = trackChild.getAttributes(); float x = CStr(attrs.getNamedItem(at_x)).ToFloat(); float y = CStr(attrs.getNamedItem(at_y)).ToFloat(); float z = CStr(attrs.getNamedItem(at_z)).ToFloat(); track.SetStartRotation(CVector3D(x, y, z)); } else if ( elementName == el_path ) { CCinemaData pathData; TNSpline spline, backwardSpline; XERO_ITER_EL(trackChild, pathChild) { elementName = pathChild.getNodeName(); attrs = pathChild.getAttributes(); if ( elementName == el_rotation ) { float x = CStr(attrs.getNamedItem(at_x)).ToFloat(); float y = CStr(attrs.getNamedItem(at_y)).ToFloat(); float z = CStr(attrs.getNamedItem(at_z)).ToFloat(); pathData.m_TotalRotation = CVector3D(x, y, z); } else if ( elementName == el_distortion ) { pathData.m_Mode = CStr(attrs.getNamedItem(at_mode)).ToInt(); pathData.m_Style = CStr(attrs.getNamedItem(at_style)).ToInt(); pathData.m_Growth = CStr(attrs.getNamedItem(at_growth)).ToInt(); pathData.m_Switch = CStr(attrs.getNamedItem(at_switch)).ToInt(); } else if ( elementName == el_node ) { SplineData data; data.Position.X = CStr(attrs.getNamedItem(at_x)).ToFloat(); data.Position.Y = CStr(attrs.getNamedItem(at_y)).ToFloat(); data.Position.Z = CStr(attrs.getNamedItem(at_z)).ToFloat(); data.Distance = CStr(attrs.getNamedItem(at_t)).ToFloat(); backwardSpline.AddNode(data.Position, data.Distance); } else debug_warn("Invalid cinematic element for path child"); } //node loop CCinemaPath temp(pathData, backwardSpline); const std::vector& nodes = temp.GetAllNodes(); if ( nodes.empty() ) { debug_warn("Failure loading cinematics"); return; } for ( std::vector::const_reverse_iterator it=nodes.rbegin(); it != nodes.rend(); ++it ) { spline.AddNode(it->Position, it->Distance); } track.AddPath(pathData, spline); } // == el_path else debug_warn("Invalid cinematic element for track child"); } trackList[name] = track; } else debug_warn("Invalid cinematic element for root track child"); } g_Game->GetView()->GetCinema()->SetAllTracks(trackList); } int CXMLReader::ReadEntities(XMBElement parent, double end_time) { XMBElementList entities = parent.getChildNodes(); while (entity_idx < entities.Count) { // all new state at this scope and below doesn't need to be // wrapped, since we only yield after a complete iteration. XMBElement entity = entities.item(entity_idx++); debug_assert(entity.getNodeName() == el_entity); CStrW TemplateName; int PlayerID = 0; CVector3D Position; float Orientation = 0.f; XERO_ITER_EL(entity, setting) { int element_name = setting.getNodeName(); //