1
0
forked from 0ad/0ad
0ad/source/graphics/MapReader.cpp
janwas 73683b6109 # SwEng
. the massive renaming undertaking: camelCase functions -> PascalCase.
. add some cppdoc.
. minor additional renaming improvements: e.g. GetIsClosed -> IsClosed
. in entity code, replace constructs like "pvec = new vector; return
pvec; use *pvec; delete pvec" with a simple stack variable passed as
output parameter (avoid unnecessary dynamic allocs)
. timer: simpler handling of raw ticks vs normal timer (less #if)

This was SVN commit r5017.
2007-05-02 12:07:08 +00:00

1037 lines
27 KiB
C++

#include "precompiled.h"
#include "MapReader.h"
#include "graphics/Camera.h"
#include "graphics/CinemaTrack.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 "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/TriggerManager.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();
pUnitMan->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; i<numObjTypes; i++) {
unpacker.UnpackString(m_ObjectTypes[i]);
}
// unpack object data
u32 numObjects;
unpacker.UnpackRaw(&numObjects, sizeof(numObjects));
m_Objects.resize(numObjects);
if (numObjects)
unpacker.UnpackRaw(&m_Objects[0], sizeof(SObjectDesc)*numObjects);
}
// UnpackTerrain: unpack the terrain from the end of the input data stream
// - data: map size, heightmap, list of textures used by map, texture tile assignments
int CMapReader::UnpackTerrain()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = get_time() + 200e-3;
// first call to generator (this is skipped after first call,
// i.e. when the loop below was interrupted)
if (cur_terrain_tex == 0)
{
// unpack map size
unpacker.UnpackRaw(&m_MapSize, sizeof(m_MapSize));
// unpack heightmap [600us]
u32 verticesPerSide = m_MapSize*PATCH_SIZE+1;
m_Heightmap.resize(SQR(verticesPerSide));
unpacker.UnpackRaw(&m_Heightmap[0], SQR(verticesPerSide)*sizeof(u16));
// unpack # textures
unpacker.UnpackRaw(&num_terrain_tex, sizeof(num_terrain_tex));
m_TerrainTextures.reserve(num_terrain_tex);
}
// unpack texture names; find handle for each texture.
// interruptible.
while (cur_terrain_tex < num_terrain_tex)
{
CStr texturename;
unpacker.UnpackString(texturename);
Handle handle;
CTextureEntry* texentry = g_TexMan.FindTexture(texturename);
// mismatch between texture datasets?
if (!texentry)
handle = 0;
else
handle = texentry->GetHandle();
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;
}
// ApplyData: take all the input data, and rebuild the scene from it
int CMapReader::ApplyData()
{
// initialise the terrain
pTerrain->Initialize(m_MapSize, &m_Heightmap[0]);
// setup the textures on the minipatches
STileDesc* tileptr = &m_Tiles[0];
for (u32 j=0; j<m_MapSize; j++) {
for (u32 i=0; i<m_MapSize; i++) {
for (u32 m=0; m<(u32)PATCH_SIZE; m++) {
for (u32 k=0; k<(u32)PATCH_SIZE; k++) {
CMiniPatch& mp = pTerrain->GetPatch(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<CStr> selections; // TODO: read from file
CUnit* unit = pUnitMan->CreateUnit(m_ObjectTypes.at(m_Objects[i].m_ObjectIndex), NULL, selections);
if (unit)
{
CMatrix3D transform;
cpu_memcpy(&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 : boost::noncopyable
{
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_id;
int at_angle;
int at_uid;
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);
void ReadTriggers(XMBElement parent);
void ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group);
int ReadEntities(XMBElement parent, double end_time);
int ReadNonEntities(XMBElement parent, double end_time);
};
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);
AT(uid);
#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);
EL(murkiness);
EL(tint);
EL(reflectiontint);
EL(reflectiontintstrength);
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
}
#define READ_COLOUR(el, out) \
else if (element_name == el) \
{ \
XMBAttributeList attrs = waterelement.GetAttributes(); \
out = CColor( \
CStr(attrs.GetNamedItem(at_r)).ToFloat(), \
CStr(attrs.GetNamedItem(at_g)).ToFloat(), \
CStr(attrs.GetNamedItem(at_b)).ToFloat(), \
1.f); \
}
#define READ_FLOAT(el, out) \
else if (element_name == el) \
{ \
out = CStr(waterelement.GetText()).ToFloat(); \
} \
READ_COLOUR(el_colour, m_MapReader.pWaterMan->m_WaterColor)
READ_FLOAT(el_height, m_MapReader.pWaterMan->m_WaterHeight)
READ_FLOAT(el_shininess, m_MapReader.pWaterMan->m_Shininess)
READ_FLOAT(el_waviness, m_MapReader.pWaterMan->m_Waviness)
READ_FLOAT(el_murkiness, m_MapReader.pWaterMan->m_Murkiness)
READ_COLOUR(el_tint, m_MapReader.pWaterMan->m_WaterTint)
READ_COLOUR(el_reflectiontint, m_MapReader.pWaterMan->m_ReflectionTint)
READ_FLOAT(el_reflectiontintstrength, m_MapReader.pWaterMan->m_ReflectionTintStrength)
#undef READ_FLOAT
#undef READ_COLOUR
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(path);
EL(rotation);
EL(distortion);
EL(node);
EL(position);
EL(time);
AT(name);
AT(timescale);
AT(mode);
AT(style);
AT(growth);
AT(switch);
AT(x);
AT(y);
AT(z);
#undef EL
#undef AT
std::map<CStrW, CCinemaPath> pathList;
XERO_ITER_EL(parent, element)
{
int elementName = element.GetNodeName();
if ( elementName == el_path )
{
XMBAttributeList attrs = element.GetAttributes();
CStrW name( CStr(attrs.GetNamedItem(at_name)) );
float timescale = CStr(attrs.GetNamedItem(at_timescale)).ToFloat();
CCinemaData pathData;
pathData.m_Timescale = timescale;
TNSpline spline, backwardSpline;
XERO_ITER_EL(element, pathChild)
{
elementName = pathChild.GetNodeName();
attrs = pathChild.GetAttributes();
//Load distortion attributes
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();
}
//Load node data used for spline
else if ( elementName == el_node )
{
SplineData data;
XERO_ITER_EL(pathChild, nodeChild)
{
elementName = nodeChild.GetNodeName();
attrs = nodeChild.GetAttributes();
//Fix?: assumes that time is last element
if ( elementName == el_position )
{
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();
continue;
}
else if ( elementName == el_rotation )
{
data.Rotation.X = CStr(attrs.GetNamedItem(at_x)).ToFloat();
data.Rotation.Y = CStr(attrs.GetNamedItem(at_y)).ToFloat();
data.Rotation.Z = CStr(attrs.GetNamedItem(at_z)).ToFloat();
continue;
}
else if ( elementName == el_time )
data.Distance = CStr( nodeChild.GetText() ).ToFloat();
else
debug_warn("Invalid cinematic element for node child");
backwardSpline.AddNode(data.Position, data.Rotation, data.Distance);
}
}
else
debug_warn("Invalid cinematic element for path child");
}
//Construct cinema path with data gathered
CCinemaPath temp(pathData, backwardSpline);
const std::vector<SplineData>& nodes = temp.GetAllNodes();
if ( nodes.empty() )
{
debug_warn("Failure loading cinematics");
return;
}
for ( std::vector<SplineData>::const_reverse_iterator it = nodes.rbegin();
it != nodes.rend(); ++it )
{
spline.AddNode(it->Position, it->Rotation, it->Distance);
}
CCinemaPath path(pathData, spline);
pathList[name] = path;
}
else
debug_assert("Invalid cinema child");
}
g_Game->GetView()->GetCinema()->SetAllPaths(pathList);
}
void CXMLReader::ReadTriggers(XMBElement parent)
{
MapTriggerGroup rootGroup( CStrW(L"Triggers"), CStrW(L"") );
g_TriggerManager.DestroyEngineTriggers();
ReadTriggerGroup(parent, rootGroup);
}
void CXMLReader::ReadTriggerGroup(XMBElement parent, MapTriggerGroup& group)
{
#define EL(x) int el_##x = xmb_file.GetElementID(#x)
#define AT(x) int at_##x = xmb_file.GetAttributeID(#x)
EL(group);
EL(trigger);
EL(active);
EL(delay);
EL(maxruncount);
EL(conditions);
EL(logicblock);
EL(logicblockend);
EL(condition);
EL(parameter);
EL(linklogic);
EL(effects);
EL(effect);
EL(function);
EL(display);
AT(name);
AT(function);
AT(display);
AT(not);
#undef EL
#undef AT
CStrW name = parent.GetAttributes().GetNamedItem(at_name), parentName = group.parentName;
if ( group.name == L"Triggers" )
name = group.name;
MapTriggerGroup mapGroup(name, parentName);
XERO_ITER_EL(parent, groupChild)
{
int elementName = groupChild.GetNodeName();
if ( elementName == el_group )
ReadTriggerGroup(groupChild, mapGroup);
else if ( elementName == el_trigger )
{
MapTrigger mapTrigger;
mapTrigger.name = CStrW( groupChild.GetAttributes().GetNamedItem(at_name) );
//Read everything in this trigger
XERO_ITER_EL(groupChild, triggerChild)
{
elementName = triggerChild.GetNodeName();
if ( elementName == el_active )
{
if ( CStr("false") == CStr( triggerChild.GetText() ) )
mapTrigger.active = false;
else
mapTrigger.active = true;
}
else if ( elementName == el_maxruncount )
mapTrigger.maxRunCount = CStr( triggerChild.GetText() ).ToInt();
else if ( elementName == el_delay )
mapTrigger.timeValue = CStr( triggerChild.GetText() ).ToFloat();
else if ( elementName == el_conditions )
{
//Read in all conditions for this trigger
XERO_ITER_EL(triggerChild, condition)
{
elementName = condition.GetNodeName();
if ( elementName == el_condition )
{
MapTriggerCondition mapCondition;
mapCondition.name = condition.GetAttributes().GetNamedItem(at_name);
mapCondition.functionName = condition.GetAttributes().GetNamedItem(at_function);
mapCondition.displayName = condition.GetAttributes().GetNamedItem(at_display);
CStr notAtt(condition.GetAttributes().GetNamedItem(at_not));
if ( notAtt == CStr("true") )
mapCondition.negated = true;
//Read in each condition child
XERO_ITER_EL(condition, conditionChild)
{
elementName = conditionChild.GetNodeName();
if ( elementName == el_function )
mapCondition.functionName = CStrW(conditionChild.GetText());
else if ( elementName == el_display )
mapCondition.displayName = CStrW(conditionChild.GetText());
else if ( elementName == el_parameter )
mapCondition.parameters.push_back( conditionChild.GetText() );
else if ( elementName == el_linklogic )
{
CStr logic = conditionChild.GetText();
if ( logic == CStr("AND") )
mapCondition.linkLogic = 1;
else
mapCondition.linkLogic = 2;
}
}
mapTrigger.conditions.push_back(mapCondition);
} //Read all conditions
else if ( elementName == el_logicblock)
{
if ( CStr(condition.GetAttributes().GetNamedItem(at_not)) == CStr("true") )
mapTrigger.AddLogicBlock(true);
else
mapTrigger.AddLogicBlock(false);
}
else if ( elementName == el_logicblockend)
mapTrigger.AddLogicBlockEnd();
} //Read all conditions
}
else if ( elementName == el_effects )
{
//Read all effects
XERO_ITER_EL(triggerChild, effect)
{
if ( effect.GetNodeName() != el_effect )
{
debug_warn("Invalid effect tag in trigger XML file");
return;
}
MapTriggerEffect mapEffect;
mapEffect.name = effect.GetAttributes().GetNamedItem(at_name);
//Read parameters
XERO_ITER_EL(effect, effectChild)
{
elementName = effectChild.GetNodeName();
if ( elementName == el_function )
mapEffect.functionName = effectChild.GetText();
else if ( elementName == el_display )
mapEffect.displayName = effectChild.GetText();
else if ( elementName == el_parameter )
mapEffect.parameters.push_back( effectChild.GetText() );
else
{
debug_warn("Invalid parameter tag in trigger XML file");
return;
}
}
mapTrigger.effects.push_back(mapEffect);
}
}
else
debug_warn("Invalid trigger node child in trigger XML file");
} //Read trigger children
g_TriggerManager.AddTrigger(mapGroup, mapTrigger);
}
else
debug_warn("Invalid group node child in XML file");
} //Read group children
g_TriggerManager.AddGroup(mapGroup);
}
int CXMLReader::ReadEntities(XMBElement parent, double end_time)
{
XMBElementList entities = parent.GetChildNodes();
// If this is the first time in ReadEntities, find the next free ID number
// in case we need to allocate new ones in the future
if (entity_idx == 0)
{
int maxUnitID = -1;
XERO_ITER_EL(parent, entity)
{
debug_assert(entity.GetNodeName() == el_entity);
XMBAttributeList attrs = entity.GetAttributes();
utf16string uid = attrs.GetNamedItem(at_uid);
int unitId = uid.empty() ? -1 : CStr(uid).ToInt();
maxUnitID = std::max(maxUnitID, unitId);
}
m_MapReader.pUnitMan->SetNextID(maxUnitID + 1);
}
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);
XMBAttributeList attrs = entity.GetAttributes();
utf16string uid = attrs.GetNamedItem(at_uid);
int unitId = uid.empty() ? -1 : CStr(uid).ToInt();
CStrW TemplateName;
int PlayerID = 0;
CVector3D Position;
float Orientation = 0.f;
XERO_ITER_EL(entity, setting)
{
int element_name = setting.GetNodeName();
// <template>
if (element_name == el_template)
{
TemplateName = setting.GetText();
}
// <player>
else if (element_name == el_player)
{
PlayerID = CStr(setting.GetText()).ToInt();
}
// <position>
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CVector3D(
CStr(attrs.GetNamedItem(at_x)).ToFloat(),
CStr(attrs.GetNamedItem(at_y)).ToFloat(),
CStr(attrs.GetNamedItem(at_z)).ToFloat());
}
// <orientation>
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = CStr(attrs.GetNamedItem(at_angle)).ToFloat();
}
else
debug_warn("Invalid map XML data");
}
CEntityTemplate* base = g_EntityTemplateCollection.GetTemplate(TemplateName, g_Game->GetPlayer(PlayerID));
if (! base)
LOG(ERROR, LOG_CATEGORY, "Failed to load entity template '%ls'", TemplateName.c_str());
else
{
std::set<CStr> selections; // TODO: read from file
HEntity ent = g_EntityManager.Create(base, Position, Orientation, selections);
if (! ent)
LOG(ERROR, LOG_CATEGORY, "Failed to create entity of type '%ls'", TemplateName.c_str());
else
{
ent->m_actor->SetPlayerID(PlayerID);
g_EntityManager.AddEntityClassData(ent);
if (unitId < 0)
ent->m_actor->SetID(m_MapReader.pUnitMan->GetNewID());
else
ent->m_actor->SetID(unitId);
}
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ReadNonEntities(XMBElement parent, double end_time)
{
XMBElementList nonentities = parent.GetChildNodes();
while (nonentity_idx < nonentities.Count)
{
// all new state at this scope and below doesn't need to be
// wrapped, since we only yield after a complete iteration.
XMBElement nonentity = nonentities.Item(nonentity_idx++);
debug_assert(nonentity.GetNodeName() == el_nonentity);
CStr ActorName;
CVector3D Position;
float Orientation = 0.f;
XERO_ITER_EL(nonentity, setting)
{
int element_name = setting.GetNodeName();
// <actor>
if (element_name == el_actor)
{
ActorName = setting.GetText();
}
// <position>
else if (element_name == el_position)
{
XMBAttributeList attrs = setting.GetAttributes();
Position = CVector3D(
CStr(attrs.GetNamedItem(at_x)).ToFloat(),
CStr(attrs.GetNamedItem(at_y)).ToFloat(),
CStr(attrs.GetNamedItem(at_z)).ToFloat());
}
// <orientation>
else if (element_name == el_orientation)
{
XMBAttributeList attrs = setting.GetAttributes();
Orientation = CStr(attrs.GetNamedItem(at_angle)).ToFloat();
}
else
debug_warn("Invalid map XML data");
}
std::set<CStr> selections; // TODO: read from file
CUnit* unit = m_MapReader.pUnitMan->CreateUnit(ActorName, NULL, selections);
if (unit)
{
CMatrix3D m;
m.SetYRotation(Orientation + PI);
m.Translate(Position);
unit->GetModel()->SetTransform(m);
// TODO: save object IDs in the map file, and load them again,
// so that triggers have a persistent identifier for objects
unit->SetID(m_MapReader.pUnitMan->GetNewID());
}
completed_jobs++;
LDR_CHECK_TIMEOUT(completed_jobs, total_jobs);
}
return 0;
}
int CXMLReader::ProgressiveRead()
{
// yield after this time is reached. balances increased progress bar
// smoothness vs. slowing down loading.
const double end_time = get_time() + 200e-3;
int ret;
while (node_idx < nodes.Count)
{
XMBElement node = nodes.Item(node_idx);
CStr name = xmb_file.GetElementString(node.GetNodeName());
if (name == "Environment")
{
ReadEnvironment(node);
}
else if (name == "Camera")
{
ReadCamera(node);
}
else if (name == "Entities")
{
ret = ReadEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
else if (name == "Nonentities")
{
ret = ReadNonEntities(node, end_time);
if (ret != 0) // error or timed out
return ret;
}
else if (name == "Paths")
{
ReadCinema(node);
}
else if (name == "Triggers")
{
ReadTriggers(node);
}
else
{
debug_printf("Invalid XML element in map file: %s\n", name.c_str());
debug_warn("Invalid map XML data");
}
node_idx++;
}
return 0;
}
// progressive
int CMapReader::ReadXML()
{
if (!xml_reader)
xml_reader = new CXMLReader(filename_xml, *this);
int ret = xml_reader->ProgressiveRead();
// finished or failed
if (ret <= 0)
{
delete xml_reader;
xml_reader = 0;
}
return ret;
}
int CMapReader::DelayLoadFinished()
{
// we were dynamically allocated by CWorld::Initialize
delete this;
return 0;
}