[10165] New collission system (vmap) implementation

Important:
* You have to re-extract and assemble vmaps
* Update your config file, new option 'vmap.enableIndoorCheck' added

New features:
* Include WMO+DBC area information for correct subarea identification and indoor check
* Support for WMO liquid (fishing/swimming in cities, instances and oterh WMO based environments)

Technical changes:
* New Bounding Interval Hierarchy (BIH) data structure for better performance
* Referenced model data for reduced memory usage,
  needs more files, but reduces overall file size from ~1.9GB to ~550MB

Additional Authors:
arrai (DBC handling and indoor detection)
faramir118 (windows support and bug investigation)
And of course thanks Vladimir for a lot of patience and support!
This commit is contained in:
Lynx3d 2010-07-08 23:17:18 +02:00
parent c2bcfd0f18
commit 5e89098a61
57 changed files with 3472 additions and 5694 deletions

View file

@ -242,8 +242,8 @@ enum AreaFlags
AREA_FLAG_UNK7 = 0x00400000, // Warsong Hold, Acherus: The Ebon Hold, New Agamand Inn, Vengeance Landing Inn
AREA_FLAG_UNK8 = 0x00800000, // Westguard Inn, Acherus: The Ebon Hold, Valgarde
AREA_FLAG_OUTDOOR_PVP = 0x01000000, // Wintergrasp and it's subzones
AREA_FLAG_UNK9 = 0x02000000, // unknown
AREA_FLAG_UNK10 = 0x04000000, // unknown
AREA_FLAG_INSIDE = 0x02000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors
AREA_FLAG_OUTSIDE = 0x04000000, // used for determinating spell related inside/outside questions in Map::IsOutdoors
AREA_FLAG_CAN_HEARTH_AND_RES = 0x08000000 // Wintergrasp and it's subzones
// 0x20000000 not flyable?
};

View file

@ -30,11 +30,32 @@
typedef std::map<uint16,uint32> AreaFlagByAreaID;
typedef std::map<uint32,uint32> AreaFlagByMapID;
struct WMOAreaTableTripple
{
WMOAreaTableTripple(int32 r, int32 a, int32 g) : rootId(r), adtId(a), groupId(g)
{
}
bool operator <(const WMOAreaTableTripple& b) const
{
return memcmp(this, &b, sizeof(WMOAreaTableTripple))<0;
}
// ordered by entropy; that way memcmp will have a minimal medium runtime
int32 groupId;
int32 rootId;
int32 adtId;
};
typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const *> WMOAreaInfoByTripple;
DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt);
DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt);
static AreaFlagByAreaID sAreaFlagByAreaID;
static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files
static WMOAreaInfoByTripple sWMOAreaInfoByTripple;
DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt);
DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt);
DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt);
@ -157,6 +178,7 @@ static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt);
DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage <VehicleEntry> sVehicleStore(VehicleEntryfmt);
DBCStorage <VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt);
DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore(WMOAreaTableEntryfmt);
DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt);
DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt);
@ -614,6 +636,14 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleStore, dbcPath,"Vehicle.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleSeatStore, dbcPath,"VehicleSeat.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWMOAreaTableStore, dbcPath,"WMOAreaTable.dbc");
for(uint32 i = 0; i < sWMOAreaTableStore.GetNumRows(); ++i)
{
if(WMOAreaTableEntry const* entry = sWMOAreaTableStore.LookupEntry(i))
{
sWMOAreaInfoByTripple.insert(WMOAreaInfoByTripple::value_type(WMOAreaTableTripple(entry->rootId, entry->adtId, entry->groupId), entry));
}
}
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapOverlayStore, dbcPath,"WorldMapOverlay.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc");
@ -702,6 +732,15 @@ int32 GetAreaFlagByAreaID(uint32 area_id)
return i->second;
}
WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid)
{
WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid));
if(i == sWMOAreaInfoByTripple.end())
return NULL;
return i->second;
}
AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id)
{
int32 areaflag = GetAreaFlagByAreaID(area_id);

View file

@ -39,6 +39,8 @@ TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found
uint32 GetAreaFlagByMapId(uint32 mapid);
WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid);
MANGOS_DLL_SPEC AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id);
MANGOS_DLL_SPEC AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id);
@ -161,6 +163,7 @@ extern TaxiPathNodesByPath sTaxiPathNodesByPath;
extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore;
extern DBCStorage <VehicleEntry> sVehicleStore;
extern DBCStorage <VehicleSeatEntry> sVehicleSeatStore;
extern DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore;
//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates
extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore;
extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore;

View file

@ -1781,6 +1781,23 @@ struct VehicleSeatEntry
// 46-57 added in 3.1, floats mostly
};
struct WMOAreaTableEntry
{
uint32 Id; // 0 index
int32 rootId; // 1 used in root WMO
int32 adtId; // 2 used in adt file
int32 groupId; // 3 used in group WMO
//uint32 field4;
//uint32 field5;
//uint32 field6;
//uint32 field7;
//uint32 field8;
uint32 Flags; // 9 used for indoor/outdoor determination
uint32 areaId; // 10 link to AreaTableEntry.ID
//char *Name[16];
//uint32 nameFlags;
};
struct WorldMapAreaEntry
{
//uint32 ID; // 0

View file

@ -106,6 +106,7 @@ const char TaxiPathNodeEntryfmt[]="diiifffiiii";
const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
const char VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifixxx";
const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx";
const char WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx";
const char WorldMapAreaEntryfmt[]="xinxffffixx";
const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx";
const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";

View file

@ -313,6 +313,15 @@ bool ChatHandler::HandleGPSCommand(const char* args)
uint32 have_map = GridMap::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0;
uint32 have_vmap = GridMap::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0;
if(have_vmap)
{
if(map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()))
PSendSysMessage("You are OUTdoor");
else
PSendSysMessage("You are INdoor");
}
else PSendSysMessage("no VMAP available for area info");
PSendSysMessage(LANG_MAP_POSITION,
obj->GetMapId(), (mapEntry ? mapEntry->name[GetSessionDbcLocale()] : "<unknown>" ),
zone_id, (zoneEntry ? zoneEntry->area_name[GetSessionDbcLocale()] : "<unknown>" ),

View file

@ -1075,249 +1075,106 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
}
}
uint16 Map::GetAreaFlag(float x, float y, float z) const
inline bool IsOutdoorWMO(uint32 mogpFlags, int32 adtId, int32 rootId, int32 groupId,
WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry)
{
bool outdoor = true;
if(wmoEntry && atEntry)
{
if(atEntry->flags & AREA_FLAG_OUTSIDE)
return true;
if(atEntry->flags & AREA_FLAG_INSIDE)
return false;
}
outdoor = mogpFlags&0x8;
if(wmoEntry)
{
if(wmoEntry->Flags & 4)
return true;
if((wmoEntry->Flags & 2)!=0)
outdoor = false;
}
return outdoor;
}
bool Map::IsOutdoors(float x, float y, float z) const
{
uint32 mogpFlags;
int32 adtId, rootId, groupId;
// no wmo found? -> outside by default
if(!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
return true;
AreaTableEntry const* atEntry = 0;
WMOAreaTableEntry const* wmoEntry= GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
if(wmoEntry)
{
DEBUG_LOG("Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId);
atEntry = GetAreaEntryByAreaID(wmoEntry->areaId);
}
return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
}
bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
float vmap_z = z;
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
if (vmgr->getAreaInfo(GetId(), x, y, vmap_z, flags, adtId, rootId, groupId))
{
// check if there's terrain between player height and object height
if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
float _mapheight = gmap->getHeight(x,y);
// z + 2.0f condition taken from GetHeight(), not sure if it's such a great choice...
if(z + 2.0f > _mapheight && _mapheight > vmap_z)
return false;
}
return true;
}
return false;
}
uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const
{
uint32 mogpFlags;
int32 adtId, rootId, groupId;
WMOAreaTableEntry const* wmoEntry = 0;
AreaTableEntry const* atEntry = 0;
bool haveAreaInfo = false;
if(GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
{
haveAreaInfo = true;
if(wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId))
atEntry = GetAreaEntryByAreaID(wmoEntry->areaId);
}
uint16 areaflag;
if (atEntry)
areaflag = atEntry->exploreFlag;
else
{
if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
areaflag = gmap->getArea(x, y);
// this used while not all *.map files generated (instances)
else
areaflag = GetAreaFlagByMapId(i_id);
//FIXME: some hacks for areas above or underground for ground area
// required for area specific spells/etc, until map/vmap data
// not provided correct areaflag with this hacks
switch(areaflag)
{
case 1146: // Blade's Edge Mountains
case 1409: // Forge Camp: Wrath (Blade's Edge Mountains)
if (x > 3025.0f && x < 3207.0f && y > 6987.0f && y < 7165.0f && z < 183.0f)
areaflag = 1404; // Blackwing Coven (Blade's Edge Mountains)
break;
// Acherus: The Ebon Hold (Plaguelands: The Scarlet Enclave)
case 1984: // Plaguelands: The Scarlet Enclave
case 2076: // Death's Breach (Plaguelands: The Scarlet Enclave)
case 2745: // The Noxious Pass (Plaguelands: The Scarlet Enclave)
if(z > 350.0f) areaflag = 2048; break;
// Acherus: The Ebon Hold (Eastern Plaguelands)
case 856: // The Noxious Glade (Eastern Plaguelands)
case 2456: // Death's Breach (Eastern Plaguelands)
if(z > 350.0f) areaflag = 1950; break;
// Winterfin Caverns
case 1652: // Coldarra
case 1653: // The Westrift
case 1661: // Winterfin Village
if (x > 3823.0f && x < 4141.5f && y > 6247.0f && y < 64890.0f && z < 42.5f)
areaflag = 1723;
break;
// Moonrest Gardens
case 1787:
if (x > 3315.3f && x < 3361.6f && y > 2469.4f && y < 2565.8f && z > 197.0f)
areaflag = 1786; // Surge Needle (cords not entirely correct, will need round circle if this is really needed(see spell 47097 eff 77))
break;
// Dalaran
case 2492: // Forlorn Woods (Crystalsong Forest)
case 2371: // Valley of Echoes (Icecrown Glacier)
if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
{
// Krasus' Landing (Dalaran), fast check
if (x > 5758.77f && x < 5869.03f && y < 510.46f)
{
// Krasus' Landing (Dalaran), with open east side
if (y < 449.33f || (x-5813.9f)*(x-5813.9f)+(y-449.33f)*(y-449.33f) < 1864.0f)
{
areaflag = 2531; // Note: also 2633, possible one flight allowed and other not allowed case
break;
}
}
// Sunreaver's Sanctuary (Dalaran) (Enter,Left,Right-border lines)
if ((y-548.11f)/(568.80f-548.11f) <= (x-5849.30f)/(5866.57f-5849.30f) &&
(y-582.76f)/(622.97f-582.76f) <= (x-5961.64f)/(5886.09f-5961.64f) &&
(y-446.79f)/(508.22f-446.79f) >= (x-5867.66f)/(5846.12f-5867.66f))
if (isOutdoors)
{
// need exclude 2 shop rooms
if (((x-5862.26f)*(x-5862.26f)+(y-554.76f)*(y-554.76f) > 335.85f/*310.64f*/ || z > 671.12f) &&
((y-580.51f)/(608.37f-580.51f) <= (x-5896.23f)/(5859.79f-5896.23f) ||
(y-580.51f)/(610.49f-580.51f) <= (x-5896.23f)/(5919.15f-5896.23f) || z > 669.10f))
{
areaflag = 2687;
break;
}
}
// The Silver Enclave (Dalaran) (Enter,Left,Right-border lines)
if ((y-693.19f)/(737.41f-693.19f) >= (x-5732.80f)/(5769.38f-5732.80f) &&
(y-693.19f)/(787.00f-693.19f) >= (x-5732.80f)/(5624.17f-5732.80f) &&
(y-737.41f)/(831.30f-737.41f) <= (x-5769.38f)/(5671.15f-5769.38f))
{
// need exclude ground floor shop room
if ((x-5758.07f)*(x-5758.07f)+(y-738.18f)*(y-738.18f) > 83.30f || z > 650.00f)
{
areaflag = 3007;
break;
}
}
// Dalaran
areaflag = 2153;
}
break;
// The Violet Citadel (Dalaran) or Dalaran
case 2484: // The Twilight Rivulet (Crystalsong Forest)
case 1593: // Crystalsong Forest
// Dalaran
if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
{
// The Violet Citadel (Dalaran), fast check
if (x > 5721.1f && x < 5884.66f && y > 764.4f && y < 948.0f)
{
// The Violet Citadel (Dalaran)
if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
{
areaflag = 2696;
break;
}
}
// Sunreaver's Sanctuary (Dalaran) (Enter,Left,Right-border lines)
if ((y-581.27f)/(596.73f-581.27f) <= (x-5858.57f)/(5871.87f-5858.57f) &&
(y-582.76f)/(622.97f-582.76f) <= (x-5961.64f)/(5886.09f-5961.64f) &&
(y-446.79f)/(508.22f-446.79f) >= (x-5867.66f)/(5846.12f-5867.66f))
{
areaflag = 2687;
break;
}
// The Silver Enclave (Dalaran) (Enter,Left,Right-border lines)
if ((y-693.19f)/(737.41f-693.19f) >= (x-5732.80f)/(5769.38f-5732.80f) &&
(y-693.19f)/(787.00f-693.19f) >= (x-5732.80f)/(5624.17f-5732.80f) &&
(y-737.41f)/(831.30f-737.41f) <= (x-5769.38f)/(5671.15f-5769.38f))
{
// need exclude ground floor shop room
if ((x-5758.07f)*(x-5758.07f)+(y-738.18f)*(y-738.18f) > 64.30f || z > 650.00f)
{
areaflag = 3007;
break;
}
}
// Dalaran
areaflag = 2153;
}
break;
// Vargoth's Retreat (Dalaran) or The Violet Citadel (Dalaran) or Dalaran
case 2504: // Violet Stand (Crystalsong Forest)
// Dalaran
if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
{
// The Violet Citadel (Dalaran), fast check
if (x > 5721.1f && x < 5884.66f && y > 764.4f && y < 948.0f)
{
// Vargoth's Retreat (Dalaran), nice slow circle with upper limit
if (z < 898.0f && (x-5765.0f)*(x-5765.0f)+(y-862.4f)*(y-862.4f) < 262.0f)
{
areaflag = 2748;
break;
}
// The Violet Citadel (Dalaran)
if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
{
areaflag = 2696;
break;
}
}
// Dalaran
areaflag = 2153;
}
break;
// Maw of Neltharion (cave)
case 164: // Dragonblight
case 1797: // Obsidian Dragonshrine (Dragonblight)
case 1827: // Wintergrasp
case 2591: // The Cauldron of Flames (Wintergrasp)
if (x > 4364.0f && x < 4632.0f && y > 1545.0f && y < 1886.0f && z < 200.0f) areaflag = 1853; break;
// Undercity (sewers enter and path)
case 179: // Tirisfal Glades
if (x > 1595.0f && x < 1699.0f && y > 535.0f && y < 643.5f && z < 30.5f) areaflag = 685; break;
// Undercity (Royal Quarter)
case 210: // Silverpine Forest
case 316: // The Shining Strand (Silverpine Forest)
case 438: // Lordamere Lake (Silverpine Forest)
if (x > 1237.0f && x < 1401.0f && y > 284.0f && y < 440.0f && z < -40.0f) areaflag = 685; break;
// Undercity (cave and ground zone, part of royal quarter)
case 607: // Ruins of Lordaeron (Tirisfal Glades)
// ground and near to ground (by city walls)
if(z > 0.0f)
{
if (x > 1510.0f && x < 1839.0f && y > 29.77f && y < 433.0f) areaflag = 685;
}
// more wide underground, part of royal quarter
if (haveAreaInfo)
*isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
else
{
if (x > 1299.0f && x < 1839.0f && y > 10.0f && y < 440.0f) areaflag = 685;
*isOutdoors = true;
}
break;
// The Makers' Perch (ground) and Makers' Overlook (ground and cave)
case 1335: // Sholazar Basin
// The Makers' Perch ground (fast box)
if (x > 6100.0f && x < 6250.0f && y > 5650.0f && y < 5800.0f)
{
// nice slow circle
if ((x-6183.0f)*(x-6183.0f)+(y-5717.0f)*(y-5717.0f) < 2500.0f)
areaflag = 2189;
}
// Makers' Overlook (ground and cave)
else if (x > 5634.48f && x < 5774.53f && y < 3475.0f && z > 300.0f)
{
if (y > 3380.26f || (y > 3265.0f && z < 360.0f))
areaflag = 2187;
}
break;
// The Makers' Perch (underground)
case 2147: // The Stormwright's Shelf (Sholazar Basin)
if (x > 6199.0f && x < 6283.0f && y > 5705.0f && y < 5817.0f && z < 38.0f) areaflag = 2189; break;
// Makers' Overlook (deep cave)
case 267: // Icecrown
if (x > 5684.0f && x < 5798.0f && y > 3035.0f && y < 3367.0f && z < 358.0f) areaflag = 2187; break;
// Wyrmrest Temple (Dragonblight)
case 1814: // Path of the Titans (Dragonblight)
case 1897: // The Dragon Wastes (Dragonblight)
// fast box
if (x > 3400.0f && x < 3700.0f && y > 130.0f && y < 420.0f)
{
// nice slow circle
if ((x-3546.87f)*(x-3546.87f)+(y-272.71f)*(y-272.71f) < 19600.0f) areaflag = 1791;
}
break;
// The Forlorn Mine (The Storm Peaks)
case 166: // The Storm Peaks
case 2207: // Brunnhildar Village (The Storm Peaks)
case 2209: // Sifreldar Village (The Storm Peaks)
case 2227: // The Foot Steppes (The Storm Peaks)
// fast big box
if (x > 6812.0f && x < 7049.5f && y > -1474.5f && y < -1162.5f && z < 866.15f)
{
// east, avoid ground east-south corner wrong detection
if (x > 6925.0f && y > -1474.5f && y < -1290.0f)
areaflag = 2213;
// east middle, wide part
else if (x > 6812.0f && y > -1400.0f && y < -1290.0f)
areaflag = 2213;
// west middle, avoid ground west-south corner wrong detection
else if (x > 6833.0f && y > -1474.5f && y < -1233.0f)
areaflag = 2213;
// west, avoid ground west-south corner wrong detection
else if (x > 6885.0f && y > -1474.5f && y < -1162.5f)
areaflag = 2213;
}
break;
default:
break;
}
return areaflag;
}
@ -1331,10 +1188,50 @@ uint8 Map::GetTerrainType(float x, float y ) const
GridMapLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData *data) const
{
GridMapLiquidStatus result = LIQUID_MAP_NO_WATER;
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
float liquid_level, ground_level = INVALID_HEIGHT;
uint32 liquid_type;
if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type))
{
DEBUG_LOG("getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type);
// Check water level and ground level
if (liquid_level > ground_level && z > ground_level - 2)
{
// All ok in water -> store data
if (data)
{
data->type = liquid_type;
data->level = liquid_level;
data->depth_level = ground_level;
}
// For speed check as int values
int delta = int((liquid_level - z) * 10);
// Get position delta
if (delta > 20) // Under water
return LIQUID_MAP_UNDER_WATER;
if (delta > 0 ) // In water
return LIQUID_MAP_IN_WATER;
if (delta > -1) // Walk on water
return LIQUID_MAP_WATER_WALK;
result = LIQUID_MAP_ABOVE_WATER;
}
}
if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
return gmap->getLiquidStatus(x, y, z, ReqLiquidType, data);
else
return LIQUID_MAP_NO_WATER;
{
GridMapLiquidData map_data;
GridMapLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, &map_data);
// Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER:
if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level))
{
if (data)
*data = map_data;
return map_result;
}
}
return result;
}
float Map::GetWaterLevel(float x, float y ) const
@ -1373,15 +1270,16 @@ void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 area
zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0;
}
bool Map::IsInWater(float x, float y, float pZ) const
bool Map::IsInWater(float x, float y, float pZ, GridMapLiquidData *data) const
{
// Check surface in x, y point for liquid
if (const_cast<Map*>(this)->GetGrid(x, y))
{
GridMapLiquidData liquid_status;
if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status))
GridMapLiquidData *liquid_ptr = data ? data : &liquid_status;
if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr))
{
if (liquid_status.level - liquid_status.depth_level > 2)
//if (liquid_prt->level - liquid_prt->depth_level > 2) //???
return true;
}
}

View file

@ -151,11 +151,11 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
// some calls like isInWater should not use vmaps due to processor power
// can return INVALID_HEIGHT if under z+2 z coord not found height
float GetHeight(float x, float y, float z, bool pCheckVMap=true) const;
bool IsInWater(float x, float y, float z) const; // does not use z pos. This is for future use
bool IsInWater(float x, float y, float z, GridMapLiquidData *data = 0) const;
GridMapLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData *data = 0) const;
uint16 GetAreaFlag(float x, float y, float z) const;
uint16 GetAreaFlag(float x, float y, float z, bool *isOutdoors=0) const;
uint8 GetTerrainType(float x, float y ) const;
float GetWaterLevel(float x, float y ) const;
bool IsUnderWater(float x, float y, float z) const;
@ -261,6 +261,8 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
// DynObjects currently
uint32 GenerateLocalLowGuid(HighGuid guidhigh);
bool GetAreaInfo(float x, float y, float z, uint32 &mogpflags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool IsOutdoors(float x, float y, float z) const;
private:
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);

View file

@ -1117,7 +1117,9 @@ void Player::HandleDrowning(uint32 time_diff)
uint32 damage = urand(600, 700);
if (m_MirrorTimerFlags&UNDERWATER_INLAVA)
EnvironmentalDamage(DAMAGE_LAVA, damage);
else
// need to skip Slime damage in Undercity,
// maybe someone can find better way to handle environmental damage
else if (m_zoneUpdateId != 1497)
EnvironmentalDamage(DAMAGE_SLIME, damage);
}
}
@ -6050,7 +6052,7 @@ bool Player::SetPosition(float x, float y, float z, float orientation, bool tele
// code block for underwater state update
UpdateUnderwaterState(m, x, y, z);
CheckExploreSystem();
CheckAreaExploreAndOutdoor();
return true;
}
@ -6112,7 +6114,7 @@ void Player::SendMovieStart(uint32 MovieId)
SendDirectMessage(&data);
}
void Player::CheckExploreSystem()
void Player::CheckAreaExploreAndOutdoor()
{
if (!isAlive())
return;
@ -6120,12 +6122,17 @@ void Player::CheckExploreSystem()
if (isInFlight())
return;
uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ());
if(areaFlag==0xffff)
bool isOutdoor;
uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ(), &isOutdoor);
if (sWorld.getConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK) && !isOutdoor)
RemoveAurasWithAttribute(SPELL_ATTR_OUTDOORS_ONLY);
if (areaFlag==0xffff)
return;
int offset = areaFlag / 32;
if(offset >= PLAYER_EXPLORED_ZONES_SIZE)
if (offset >= PLAYER_EXPLORED_ZONES_SIZE)
{
sLog.outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).",areaFlag,GetPositionX(),GetPositionY(),offset,offset, PLAYER_EXPLORED_ZONES_SIZE);
return;
@ -6134,7 +6141,7 @@ void Player::CheckExploreSystem()
uint32 val = (uint32)(1 << (areaFlag % 32));
uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset);
if( !(currFields & val) )
if (!(currFields & val))
{
SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val));
@ -20565,8 +20572,8 @@ void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
{
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER|UNDERWATER_INLAVA|UNDERWATER_INSLIME|UNDERWATER_INDARKWATER);
// Small hack for enable breath in WMO
if (IsInWater())
m_MirrorTimerFlags|=UNDERWATER_INWATER;
/* if (IsInWater())
m_MirrorTimerFlags|=UNDERWATER_INWATER; */
return;
}

View file

@ -1951,7 +1951,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; }
void ProcessDelayedOperations();
void CheckExploreSystem(void);
void CheckAreaExploreAndOutdoor(void);
static uint32 TeamForRace(uint8 race);
uint32 GetTeam() const { return m_team; }

View file

@ -4189,6 +4189,16 @@ SpellCastResult Spell::CheckCast(bool strict)
if(bg->GetStatus() == STATUS_WAIT_LEAVE)
return SPELL_FAILED_DONT_REPORT;
if(m_caster->GetTypeId() == TYPEID_PLAYER && VMAP::VMapFactory::createOrGetVMapManager()->isLineOfSightCalcEnabled())
{
if(m_spellInfo->Attributes & SPELL_ATTR_OUTDOORS_ONLY &&
!m_caster->GetMap()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()))
return SPELL_FAILED_ONLY_OUTDOORS;
if(m_spellInfo->Attributes & SPELL_ATTR_INDOORS_ONLY &&
m_caster->GetMap()->IsOutdoors(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ()))
return SPELL_FAILED_ONLY_INDOORS;
}
// only check at first call, Stealth auras are already removed at second call
// for now, ignore triggered spells
if( strict && !m_IsTriggeredSpell)

View file

@ -46,9 +46,9 @@
#include "BattleGround.h"
#include "BattleGroundEY.h"
#include "BattleGroundWS.h"
#include "VMapFactory.h"
#include "Language.h"
#include "SocialMgr.h"
#include "VMapFactory.h"
#include "Util.h"
#include "TemporarySummon.h"
#include "ScriptCalls.h"
@ -7505,7 +7505,8 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx)
if(goinfo->type==GAMEOBJECT_TYPE_FISHINGNODE)
{
if ( !cMap->IsInWater(fx, fy, fz-0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole
GridMapLiquidData liqData;
if ( !cMap->IsInWater(fx, fy, fz + 1.f/* -0.5f */, &liqData)) // Hack to prevent fishing bobber from failing to land on fishing hole
{ // but this is not proper, we really need to ignore not materialized objects
SendCastResult(SPELL_FAILED_NOT_HERE);
SendChannelUpdate(0);
@ -7513,7 +7514,8 @@ void Spell::EffectTransmitted(SpellEffectIndex eff_idx)
}
// replace by water level in this case
fz = cMap->GetWaterLevel(fx, fy);
//fz = cMap->GetWaterLevel(fx, fy);
fz = liqData.level;
}
// if gameobject is summoning object, it should be spawned right on caster's position
else if(goinfo->type==GAMEOBJECT_TYPE_SUMMONING_RITUAL)

View file

@ -4448,6 +4448,20 @@ void Unit::RemoveAurasWithInterruptFlags(uint32 flags)
}
}
void Unit::RemoveAurasWithAttribute(uint32 flags)
{
for (SpellAuraHolderMap::iterator iter = m_spellAuraHolders.begin(); iter != m_spellAuraHolders.end(); )
{
if (iter->second->GetSpellProto()->Attributes & flags)
{
RemoveSpellAuraHolder(iter->second);
iter = m_spellAuraHolders.begin();
}
else
++iter;
}
}
void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
{
// single target auras from other casters

View file

@ -1526,6 +1526,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject
void RemoveRankAurasDueToSpell(uint32 spellId);
bool RemoveNoStackAurasDueToAuraHolder(SpellAuraHolder *holder);
void RemoveAurasWithInterruptFlags(uint32 flags);
void RemoveAurasWithAttribute(uint32 flags);
void RemoveAurasWithDispelType( DispelType type );
void RemoveAllAuras(AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
void RemoveArenaAuras(bool onleave = false);

View file

@ -848,6 +848,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outString("Using DataDir %s",m_dataPath.c_str());
}
setConfig(CONFIG_BOOL_VMAP_INDOOR_CHECK, "vmap.enableIndoorCheck", true);
bool enableLOS = sConfig.GetBoolDefault("vmap.enableLOS", false);
bool enableHeight = sConfig.GetBoolDefault("vmap.enableHeight", false);
std::string ignoreMapIds = sConfig.GetStringDefault("vmap.ignoreMapIds", "");
@ -887,7 +888,7 @@ void World::SetInitialWorldSettings()
||m_configUint32Values[CONFIG_UINT32_EXPANSION] && (
!MapManager::ExistMapAndVMap(530,10349.6f,-6357.29f) || !MapManager::ExistMapAndVMap(530,-3961.64f,-13931.2f) ) )
{
sLog.outError("Correct *.map files not found in path '%smaps' or *.vmap/*vmdir files in '%svmaps'. Please place *.map/*.vmap/*.vmdir files in appropriate directories or correct the DataDir value in the mangosd.conf file.",m_dataPath.c_str(),m_dataPath.c_str());
sLog.outError("Correct *.map files not found in path '%smaps' or *.vmtree/*.vmtile files in '%svmaps'. Please place *.map and vmap files in appropriate directories or correct the DataDir value in the mangosd.conf file.",m_dataPath.c_str(),m_dataPath.c_str());
Log::WaitBeforeContinueIfNeed();
exit(1);
}

View file

@ -314,6 +314,7 @@ enum eConfigBoolValues
CONFIG_BOOL_KICK_PLAYER_ON_BAD_PACKET,
CONFIG_BOOL_STATS_SAVE_ONLY_ON_LOGOUT,
CONFIG_BOOL_CLEAN_CHARACTER_DB,
CONFIG_BOOL_VMAP_INDOOR_CHECK,
CONFIG_BOOL_VALUE_COUNT
};

View file

@ -144,6 +144,13 @@ BindIP = "0.0.0.0"
# These spells are ignored for LoS calculation
# List of ids with delimiter ','
#
# vmap.enableIndoorCheck
# Enable/Disable VMap based indoor check to remove outdoor-only auras (mounts etc.).
# Requires VMaps enabled to work.
# Default: 1 (Enabled)
# 0 (Disabled)
#
#
# DetectPosCollision
# Check final move position, summon position, etc for visible collision with other objects or
# wall (wall only if vmaps are enabled)
@ -194,8 +201,9 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
vmap.enableLOS = 0
vmap.enableHeight = 0
vmap.ignoreMapIds = "369"
vmap.ignoreMapIds = ""
vmap.ignoreSpellIds = "7720"
vmap.enableIndoorCheck = 1
DetectPosCollision = 1
TargetPosRecalculateRange = 1.5
UpdateUptimeInterval = 10

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "10164"
#define REVISION_NR "10165"
#endif // __REVISION_NR_H__

File diff suppressed because it is too large Load diff

304
src/shared/vmap/BIH.cpp Normal file
View file

@ -0,0 +1,304 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "BIH.h"
void BIH::buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats)
{
// create space for the first node
tempTree.push_back(3 << 30); // dummy leaf
tempTree.insert(tempTree.end(), 2, 0);
//tempTree.add(0);
// seed bbox
AABound gridBox = { bounds.low(), bounds.high() };
AABound nodeBox = gridBox;
// seed subdivide function
subdivide(0, dat.numPrims - 1, tempTree, dat, gridBox, nodeBox, 0, 1, stats);
}
void BIH::subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats)
{
if ((right - left + 1) <= dat.maxPrims || depth >= MAX_STACK_SIZE)
{
// write leaf node
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
// calculate extents
int axis = -1, prevAxis, rightOrig;
float clipL = G3D::fnan(), clipR = G3D::fnan(), prevClip = G3D::fnan();
float split = G3D::fnan(), prevSplit;
bool wasLeft = true;
while (true)
{
prevAxis = axis;
prevSplit = split;
// perform quick consistency checks
Vector3 d( gridBox.hi - gridBox.lo );
if (d.x < 0 || d.y < 0 || d.z < 0)
throw std::logic_error("negative node extents");
for (int i = 0; i < 3; i++)
{
if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
{
//UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
throw std::logic_error("invalid node overlap");
}
}
// find longest axis
axis = d.primaryAxis();
split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
// partition L/R subsets
clipL = -G3D::inf();
clipR = G3D::inf();
rightOrig = right; // save this for later
float nodeL = G3D::inf();
float nodeR = -G3D::inf();
for (int i = left; i <= right;)
{
int obj = dat.indices[i];
float minb = dat.primBound[obj].low()[axis];
float maxb = dat.primBound[obj].high()[axis];
float center = (minb + maxb) * 0.5f;
if (center <= split)
{
// stay left
i++;
if (clipL < maxb)
clipL = maxb;
}
else
{
// move to the right most
int t = dat.indices[i];
dat.indices[i] = dat.indices[right];
dat.indices[right] = t;
right--;
if (clipR > minb)
clipR = minb;
}
nodeL = std::min(nodeL, minb);
nodeR = std::max(nodeR, maxb);
}
// check for empty space
if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
{
float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
float nodeNewW = nodeR - nodeL;
// node box is too big compare to space occupied by primitives?
if (1.3f * nodeNewW < nodeBoxW)
{
stats.updateBVH2();
int nextIndex = tempTree.size();
// allocate child
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
// write bvh2 clip node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
// update nodebox and recurse
nodeBox.lo[axis] = nodeL;
nodeBox.hi[axis] = nodeR;
subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex, depth + 1, stats);
return;
}
}
// ensure we are making progress in the subdivision
if (right == rightOrig)
{
// all left
if (prevAxis == axis && prevSplit == split) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
if (clipL <= split) {
// keep looping on left half
gridBox.hi[axis] = split;
prevClip = clipL;
wasLeft = true;
continue;
}
gridBox.hi[axis] = split;
prevClip = G3D::fnan();
}
else if (left > right)
{
// all right
if (prevAxis == axis && prevSplit == split) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
right = rightOrig;
if (clipR >= split) {
// keep looping on right half
gridBox.lo[axis] = split;
prevClip = clipR;
wasLeft = false;
continue;
}
gridBox.lo[axis] = split;
prevClip = G3D::fnan();
}
else
{
// we are actually splitting stuff
if (prevAxis != -1 && !isnan(prevClip))
{
// second time through - lets create the previous split
// since it produced empty space
int nextIndex = tempTree.size();
// allocate child node
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
if (wasLeft) {
// create a node with a left child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
tempTree[nodeIndex + 2] = floatToRawIntBits(G3D::inf());
} else {
// create a node with a right child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
tempTree[nodeIndex + 1] = floatToRawIntBits(-G3D::inf());
tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
}
// count stats for the unused leaf
depth++;
stats.updateLeaf(depth, 0);
// now we keep going as we are, with a new nodeIndex:
nodeIndex = nextIndex;
}
break;
}
}
// compute index of child nodes
int nextIndex = tempTree.size();
// allocate left node
int nl = right - left + 1;
int nr = rightOrig - (right + 1) + 1;
if (nl > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
} else
nextIndex -= 3;
// allocate right node
if (nr > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
}
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
// prepare L/R child boxes
AABound gridBoxL(gridBox), gridBoxR(gridBox);
AABound nodeBoxL(nodeBox), nodeBoxR(nodeBox);
gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
nodeBoxL.hi[axis] = clipL;
nodeBoxR.lo[axis] = clipR;
// recurse
if (nl > 0)
subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
if (nr > 0)
subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
}
bool BIH::writeToFile(FILE *wf) const
{
uint32 treeSize = tree.size();
uint32 check=0, count=0;
check += fwrite(&bounds.low(), sizeof(float), 3, wf);
check += fwrite(&bounds.high(), sizeof(float), 3, wf);
check += fwrite(&treeSize, sizeof(uint32), 1, wf);
check += fwrite(&tree[0], sizeof(uint32), treeSize, wf);
count = objects.size();
check += fwrite(&count, sizeof(uint32), 1, wf);
check += fwrite(&objects[0], sizeof(uint32), count, wf);
return check == (3 + 3 + 2 + treeSize + count);
}
bool BIH::readFromFile(FILE *rf)
{
uint32 treeSize;
Vector3 lo, hi;
uint32 check=0, count=0;
check += fread(&lo, sizeof(float), 3, rf);
check += fread(&hi, sizeof(float), 3, rf);
bounds = AABox(lo, hi);
check += fread(&treeSize, sizeof(uint32), 1, rf);
tree.resize(treeSize);
check += fread(&tree[0], sizeof(uint32), treeSize, rf);
check += fread(&count, sizeof(uint32), 1, rf);
objects.resize(count); // = new uint32[nObjects];
check += fread(&objects[0], sizeof(uint32), count, rf);
return check == (3 + 3 + 2 + treeSize + count);
}
void BIH::BuildStats::updateLeaf(int depth, int n)
{
numLeaves++;
minDepth = std::min(depth, minDepth);
maxDepth = std::max(depth, maxDepth);
sumDepth += depth;
minObjects = std::min(n, minObjects);
maxObjects = std::max(n, maxObjects);
sumObjects += n;
int nl = std::min(n, 5);
++numLeavesN[nl];
}
void BIH::BuildStats::printStats()
{
printf("Tree stats:\n");
printf(" * Nodes: %d\n", numNodes);
printf(" * Leaves: %d\n", numLeaves);
printf(" * Objects: min %d\n", minObjects);
printf(" avg %.2f\n", (float) sumObjects / numLeaves);
printf(" avg(n>0) %.2f\n", (float) sumObjects / (numLeaves - numLeavesN[0]));
printf(" max %d\n", maxObjects);
printf(" * Depth: min %d\n", minDepth);
printf(" avg %.2f\n", (float) sumDepth / numLeaves);
printf(" max %d\n", maxDepth);
printf(" * Leaves w/: N=0 %3d%%\n", 100 * numLeavesN[0] / numLeaves);
printf(" N=1 %3d%%\n", 100 * numLeavesN[1] / numLeaves);
printf(" N=2 %3d%%\n", 100 * numLeavesN[2] / numLeaves);
printf(" N=3 %3d%%\n", 100 * numLeavesN[3] / numLeaves);
printf(" N=4 %3d%%\n", 100 * numLeavesN[4] / numLeaves);
printf(" N>4 %3d%%\n", 100 * numLeavesN[5] / numLeaves);
printf(" * BVH2 nodes: %d (%3d%%)\n", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
}

391
src/shared/vmap/BIH.h Normal file
View file

@ -0,0 +1,391 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BIH_H
#define _BIH_H
#include <G3D/Vector3.h>
#include <G3D/Ray.h>
#include <G3D/AABox.h>
#include <Platform/Define.h>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <limits>
#include <cmath>
#define MAX_STACK_SIZE 64
#ifdef _MSC_VER
#define isnan(x) _isnan(x)
#endif
using G3D::Vector3;
using G3D::AABox;
using G3D::Ray;
static inline uint32 floatToRawIntBits(float f)
{
union
{
uint32 ival;
float fval;
} temp;
temp.fval=f;
return temp.ival;
}
static inline float intBitsToFloat(uint32 i)
{
union
{
uint32 ival;
float fval;
} temp;
temp.ival=i;
return temp.fval;
}
struct AABound
{
Vector3 lo, hi;
};
/** Bounding Interval Hierarchy Class.
Building and Ray-Intersection functions based on BIH from
Sunflow, a Java Raytracer, released under MIT/X11 License
http://sunflow.sourceforge.net/
Copyright (c) 2003-2007 Christopher Kulla
*/
class BIH
{
public:
BIH() {};
template< class T, class BoundsFunc >
void build(const std::vector<T> &primitives, BoundsFunc &getBounds, uint32 leafSize = 3, bool printStats=false)
{
if(primitives.size() == 0)
return;
buildData dat;
dat.maxPrims = leafSize;
dat.numPrims = primitives.size();
dat.indices = new uint32[dat.numPrims];
dat.primBound = new AABox[dat.numPrims];
getBounds(primitives[0], bounds);
for (uint32 i=0; i<dat.numPrims; ++i)
{
dat.indices[i] = i;
AABox tb;
getBounds(primitives[i], dat.primBound[i]);
bounds.merge(dat.primBound[i]);
}
std::vector<uint32> tempTree;
BuildStats stats;
buildHierarchy(tempTree, dat, stats);
if (printStats)
stats.printStats();
objects.resize(dat.numPrims);
for (uint32 i=0; i<dat.numPrims; ++i)
objects[i] = dat.indices[i];
//nObjects = dat.numPrims;
tree = tempTree;
delete[] dat.primBound;
delete[] dat.indices;
}
uint32 primCount() { return objects.size(); }
template<typename RayCallback>
void intersectRay(const Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirst=false) const
{
float intervalMin = 0.f;
float intervalMax = maxDist;
Vector3 org = r.origin();
Vector3 dir = r.direction();
Vector3 invDir;
float t1, t2;
for(int i=0; i<3; ++i)
{
invDir[i] = 1.f / dir[i];
t1 = (bounds.low()[i] - org[i]) * invDir[i];
t2 = (bounds.high()[i] - org[i]) * invDir[i];
if (invDir[i] > 0) {
if (t1 > intervalMin)
intervalMin = t1;
if (t2 < intervalMax)
intervalMax = t2;
} else {
if (t2 > intervalMin)
intervalMin = t2;
if (t1 < intervalMax)
intervalMax = t1;
}
if (intervalMin > intervalMax)
return;
}
uint32 offsetFront[3];
uint32 offsetBack[3];
uint32 offsetFront3[3];
uint32 offsetBack3[3];
// compute custom offsets from direction sign bit
for(int i=0; i<3; ++i)
{
offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
offsetBack[i] = offsetFront[i] ^ 1;
offsetFront3[i] = offsetFront[i] * 3;
offsetBack3[i] = offsetBack[i] * 3;
// avoid always adding 1 during the inner loop
++offsetFront[i];
++offsetBack[i];
}
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
// ray passes between clip zones
if (tf < intervalMin && tb > intervalMax)
break;
int back = offset + offsetBack3[axis];
node = back;
// ray passes through far node only
if (tf < intervalMin) {
intervalMin = (tb >= intervalMin) ? tb : intervalMin;
continue;
}
node = offset + offsetFront3[axis]; // front
// ray passes through near node only
if (tb > intervalMax) {
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
// ray passes through both nodes
// push back node
stack[stackPos].node = back;
stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
stack[stackPos].tfar = intervalMax;
stackPos++;
// update ray interval for front node
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirst);
if(stopAtFirst && hit) return;
--n;
++offset;
}
break;
}
}
else
{
if (axis>2)
return; // should not happen
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
node = offset;
intervalMin = (tf >= intervalMin) ? tf : intervalMin;
intervalMax = (tb <= intervalMax) ? tb : intervalMax;
if (intervalMin > intervalMax)
break;
continue;
}
} // traversal loop
do
{
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
intervalMin = stack[stackPos].tnear;
if (maxDist < intervalMin)
continue;
node = stack[stackPos].node;
intervalMax = stack[stackPos].tfar;
break;
} while (true);
}
}
template<typename IsectCallback>
void intersectPoint(const Vector3 &p, IsectCallback& intersectCallback) const
{
if (!bounds.contains(p))
return;
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
// point is between clip zones
if (tl < p[axis] && tr > p[axis])
break;
int right = offset + 3;
node = right;
// point is in right node only
if (tl < p[axis]) {
continue;
}
node = offset; // left
// point is in left node only
if (tr > p[axis]) {
continue;
}
// point is in both nodes
// push back right node
stack[stackPos].node = right;
stackPos++;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
intersectCallback(p, objects[offset]); // !!!
--n;
++offset;
}
break;
}
}
else // BVH2 node (empty space cut off left and right)
{
if (axis>2)
return; // should not happen
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
node = offset;
if (tl > p[axis] || tr < p[axis])
break;
continue;
}
} // traversal loop
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
node = stack[stackPos].node;
}
}
bool writeToFile(FILE *wf) const;
bool readFromFile(FILE *rf);
protected:
std::vector<uint32> tree;
std::vector<uint32> objects;
AABox bounds;
struct buildData
{
uint32 *indices;
AABox *primBound;
uint32 numPrims;
int maxPrims;
};
struct StackNode
{
uint32 node;
float tnear;
float tfar;
};
class BuildStats
{
private:
int numNodes;
int numLeaves;
int sumObjects;
int minObjects;
int maxObjects;
int sumDepth;
int minDepth;
int maxDepth;
int numLeavesN[6];
int numBVH2;
public:
BuildStats():
numNodes(0), numLeaves(0), sumObjects(0), minObjects(0x0FFFFFFF),
maxObjects(0xFFFFFFFF), sumDepth(0), minDepth(0x0FFFFFFF),
maxDepth(0xFFFFFFFF), numBVH2(0)
{
for(int i=0; i<6; ++i) numLeavesN[i] = 0;
}
void updateInner() { numNodes++; }
void updateBVH2() { numBVH2++; }
void updateLeaf(int depth, int n);
void printStats();
};
void buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats);
void createNode(std::vector<uint32> &tempTree, int nodeIndex, uint32 left, uint32 right) {
// write leaf node
tempTree[nodeIndex + 0] = (3 << 30) | left;
tempTree[nodeIndex + 1] = right - left + 1;
}
void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
};
#endif // _BIH_H

View file

@ -1,95 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "BaseModel.h"
#include "VMapTools.h"
using namespace G3D;
namespace VMAP
{
//==========================================================
void BaseModel::getMember(Array<TriangleBox>& pMembers)
{
for(unsigned int i=0; i<iNTriangles; i++)
{
pMembers.append(iTriangles[i]);
}
}
//==========================================================
BaseModel::BaseModel(unsigned int pNNodes, unsigned int pNTriangles)
{
init(pNNodes, pNTriangles);
};
//==========================================================
void BaseModel::init(unsigned int pNNodes, unsigned int pNTriangles)
{
iNNodes = pNNodes;
iNTriangles = pNTriangles;
iTriangles = 0;
iTreeNodes = 0;
if(iNNodes >0) iTreeNodes = new TreeNode[iNNodes];
if(iNTriangles >0) iTriangles = new TriangleBox[iNTriangles];
}
//==========================================================
void BaseModel::free()
{
if(getTriangles() != 0) delete [] getTriangles(); setNTriangles(0);
if(getTreeNodes() != 0) delete [] getTreeNodes(); setNNodes(0);
}
//==========================================================
void BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& /*pOutNormal*/) const
{
bool isInside = false;
float d = MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
pRay.origin(), pRay.direction(),
pBox,
pOutLocation, isInside);
if (!isInside && ((d > 0) && (d < pMaxDist)))
{
pMaxDist = d;
}
}
//==========================================================
bool BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const
{
// See if the ray will ever hit this node or its children
Vector3 location;
bool alreadyInsideBounds = false;
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
pRay.origin(), pRay.direction(), pBox, location, alreadyInsideBounds);
bool canHitThisNode = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - pRay.origin()).squaredLength() < (pMaxDist * pMaxDist))));
return canHitThisNode;
}
} // VMAP

View file

@ -1,99 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _BASEMODEL_H_
#define _BASEMODEL_H_
#include <G3D/AABox.h>
#include <G3D/Vector3.h>
#include "ShortVector.h"
#include "ShortBox.h"
#include "TreeNode.h"
/**
A model is based on triangles. To be able to check intersection we need a BSP-Tree.
This Class holds the array of triangles as well as the management information for the BSP-Tree.
Both are stored in static array and index information is used instead of pointers.
Therefore we can load the whole object as a binary block.
The vectors are relative to a base position.
*/
namespace VMAP
{
class BaseModel
{
protected:
TriangleBox *iTriangles;
TreeNode *iTreeNodes;
unsigned int iNTriangles;
unsigned int iNNodes;
G3D::Vector3 iBasePosition;
public:
BaseModel() { iNTriangles = 0; iNNodes = 0; iTriangles = 0; iTreeNodes = 0;};
BaseModel(unsigned int pNNodes , TreeNode* pTreeNode, unsigned int pNTriangles, TriangleBox* pTriangleBox)
{
iNNodes = pNNodes; iNTriangles = pNTriangles; iTriangles = pTriangleBox; iTreeNodes = pTreeNode;
};
BaseModel(unsigned int pNNodes, unsigned int pNTriangles);
// destructor does nothing ! The subclass controles the array memory and knows when to free it
~BaseModel() {}
void free();
void init(unsigned int pNNodes, unsigned int pNTriangles);
void getMember(G3D::Array<TriangleBox>& pMembers);
inline const TriangleBox& getTriangle(int pPos) const { return(iTriangles[pPos]); }
inline TriangleBox& getTriangle(int pPos) { return(iTriangles[pPos]); }
inline void setTriangle(const TriangleBox& pTriangleBox, int pPos) { iTriangles[pPos] = pTriangleBox; }
inline const TreeNode& getTreeNode(int pPos) const { return(getTreeNodes()[pPos]); }
inline TreeNode& getTreeNode(int pPos) { return(getTreeNodes()[pPos]); }
inline void setTreeNode(const TreeNode& pTreeNode, int pPos) { getTreeNodes()[pPos] = pTreeNode; }
inline void setBasePosition(const G3D::Vector3& pBasePosition) { iBasePosition = pBasePosition; }
inline const G3D::Vector3& getBasePosition() const { return(iBasePosition); }
inline unsigned int getNNodes() const { return(iNNodes); }
inline unsigned int getNTriangles() const { return(iNTriangles); }
inline void setNNodes(unsigned int pNNodes) { iNNodes = pNNodes; }
inline void setNTriangles(unsigned int pNTriangles) { iNTriangles = pNTriangles; }
inline void setTriangleArray(TriangleBox *pGlobalTriangleArray ) { iTriangles = pGlobalTriangleArray ; }
inline void setTreeNodeArray(TreeNode *pGlobalTreeNodeArray ) { iTreeNodes = pGlobalTreeNodeArray ; }
inline TriangleBox* getTriangles() const { return(iTriangles); }
inline TreeNode* getTreeNodes() const{ return(iTreeNodes); }
inline size_t getMemUsage() { return(iNTriangles * sizeof(TriangleBox) + iNNodes * sizeof(TreeNode) + sizeof(BaseModel)); }
void intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const;
};
}
#endif /*BASEMODEL_H_*/

View file

@ -1,197 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CoordModelMapping.h"
#include <string.h>
#include <stdio.h>
using namespace G3D;
namespace VMAP
{
//============================================================
//============================================================
void CMappingEntry::addFilename(char *pName)
{
std::string name = std::string(pName);
if(!iFilenames.contains(name))
iFilenames.append(std::string(pName));
}
//============================================================
const std::string CMappingEntry::getKeyString() const
{
return(CMappingEntry::getKeyString(iMapId,xPos, yPos));
}
const std::string CMappingEntry::getKeyString( unsigned int pMapId, int pXPos, int pYPos )
{
char b[100];
sprintf(b,"%03u_%d_%d", pMapId, pXPos, pYPos);
return(std::string(b));
}
//============================================================
//============================================================
//============================================================
CoordModelMapping::~CoordModelMapping()
{
Array<std::string> keys = iMapObjectFiles.getKeys();
for (int k = 0; k < keys.length(); k++)
{
CMappingEntry *value = getCMappingEntry(keys[k]);
if(value != 0)
{
iMapObjectFiles.remove(keys[k]);
delete value;
}
}
}
//============================================================
int findPosChar(const char *namebuffer, char pSearch, int pCount)
{
int result = -1;
int pos=0;
while(namebuffer[pos] != 0)
{
if(namebuffer[pos] == pSearch)
{
--pCount;
}
if(pCount == 0)
{
result = pos;
break;
}
++pos;
}
return result;
}
//============================================================
bool CoordModelMapping::readCoordinateMapping(const std::string& pDirectoryFileName)
{
FILE *f = fopen(pDirectoryFileName.c_str(), "rb");
if(!f)
{
printf("ERROR: Can't open file: %s\n",pDirectoryFileName.c_str());
return false;
}
char buffer[500+1];
CMappingEntry* cMappingEntry;
while(fgets(buffer, 500, f))
{
//char namebuffer[500];
char positionbuffer[500];
int xpos, ypos, noVec;
float scale;
xpos = ypos = noVec = 0;
//sscanf(buffer, "%d %d %s %s %f %d", &xpos, &ypos, namebuffer,positionbuffer, &scale, &noVec);
// this is ugly, but the format has no read delimiter and a space could be in the first part of the name
int nameStart = findPosChar(buffer, ' ', 2);// find the 2. space
if(nameStart > -1 && (iFilterMethod == NULL || (*iFilterMethod)(buffer)))
{
++nameStart;
// find the 1. / (now a space only can be found at the end of the name)
int nameEnd = nameStart + findPosChar(&buffer[nameStart], '/', 1);
// find the 1. space (after the name)
nameEnd += findPosChar(&buffer[nameEnd], ' ', 1);
buffer[nameEnd] = 0; // terminate the name
sscanf(buffer, "%d %d", &xpos, &ypos);
sscanf(&buffer[nameEnd+1], "%s %f %d", positionbuffer, &scale, &noVec);
unsigned int mapId = getMapIdFromFilename(std::string(&buffer[nameStart]));
if(!iMapIds.contains(mapId))
{
iMapIds.append(mapId);
printf("Coords for map %u...\n",mapId);
}
if(!isWorldAreaMap(mapId))
{
xpos = 0; // store all files under the groupKey
ypos = 0;
}
std::string key = CMappingEntry::getKeyString(mapId, xpos, ypos);
cMappingEntry = getCMappingEntry(key);
if(cMappingEntry == 0)
{
cMappingEntry = new CMappingEntry(mapId, xpos, ypos);
addCMappingEntry(cMappingEntry);
}
char namebuffer2[500];
sprintf(namebuffer2, "%d %s#%s_%f", noVec, &buffer[nameStart], positionbuffer, scale);
cMappingEntry->addFilename(namebuffer2);
//break;
}
}
fclose(f);
return true;
}
//============================================================
const NameCollection CoordModelMapping::getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos)
{
NameCollection result;
Array<std::string> rawNames;
CMappingEntry *entry = getCMappingEntry(CMappingEntry::getKeyString(pMapId, xPos, yPos));
if(entry != 0)
{
rawNames = entry->getFilenames();
int pos = 0;
while(pos < rawNames.size())
{
char namebuffer[500];
int noVerc;
int startName = findPosChar(rawNames[pos].c_str(), ' ', 1) + 1;
int endName = (int) rawNames[pos].length();
sscanf(rawNames[pos].c_str(), "%d", &noVerc);
memcpy(namebuffer, &rawNames[pos].c_str()[startName], endName-startName);
namebuffer[endName-startName] = 0;
sscanf(rawNames[pos].c_str(), "%d", &noVerc);
std::string modelPosFileName = std::string(namebuffer);
if(noVerc > MIN_VERTICES_FOR_OWN_CONTAINER_FILE)
{
result.appendToSingle(modelPosFileName);
}
else
{
result.appendToMain(modelPosFileName);
}
++pos;
}
}
return result;
}
//=================================================================
}

View file

@ -1,139 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _COORDMODELMAPPING_H_
#define _COORDMODELMAPPING_H_
#include <G3D/Table.h>
#include <G3D/Array.h>
/**
This Class is a helper Class to convert the raw vector data into BSP-Trees.
We read the directory file of the raw data output and build logical groups.
Models with a lot of vectors are not merged into a resulting model, but separated into an additional file.
*/
namespace VMAP
{
#define MIN_VERTICES_FOR_OWN_CONTAINER_FILE 65000
// if we are in an instance
#define MIN_INST_VERTICES_FOR_OWN_CONTAINER_FILE 40000
//=====================================================
class NameCollection
{
public:
G3D::Array<std::string> iMainFiles;
G3D::Array<std::string> iSingeFiles;
void appendToMain(const std::string& pStr) { iMainFiles.append(pStr); }
void appendToSingle(const std::string& pStr) { iSingeFiles.append(pStr); }
size_t size() { return (iMainFiles.size() + iSingeFiles.size()); }
};
//=====================================================
class CMappingEntry
{
private:
int xPos;
int yPos;
unsigned int iMapId;
G3D::Array<std::string> iFilenames;
public:
CMappingEntry() { };
CMappingEntry(unsigned int pMapId, const int pXPos, const int pYPos)
{
iMapId = pMapId;
xPos = pXPos; yPos = pYPos;
};
~CMappingEntry() {};
void addFilename(char *pName);
const std::string getKeyString() const;
inline const G3D::Array<std::string>& getFilenames() const { return(iFilenames); }
static const std::string getKeyString(unsigned int pMapId, int pXPos, int pYPos);
};
//=====================================================
class CoordModelMapping
{
private:
G3D::Table<std::string, CMappingEntry *> iMapObjectFiles;
G3D::Table<std::string, std::string> iProcesseSingleFiles;
G3D::Array<unsigned int> iMapIds;
G3D::Array<unsigned int> iWorldAreaGroups;
bool (*iFilterMethod)(char *pName);
inline void addCMappingEntry(CMappingEntry* pCMappingEntry)
{
iMapObjectFiles.set(pCMappingEntry->getKeyString(), pCMappingEntry);
}
inline CMappingEntry* getCMappingEntry(const std::string& pKey)
{
if(iMapObjectFiles.containsKey(pKey))
return(iMapObjectFiles.get(pKey));
else
return 0;
}
public:
CoordModelMapping() { iFilterMethod = NULL; }
virtual ~CoordModelMapping();
bool readCoordinateMapping(const std::string& pDirectoryFileName);
const NameCollection getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos);
static unsigned int getMapIdFromFilename(const std::string& pName)
{
size_t spos;
spos = pName.find_last_of('/');
std::string basename = pName.substr(0, spos);
spos = basename.find_last_of('/');
std::string groupname = basename.substr(spos+1, basename.length());
unsigned int mapId = atoi(groupname.c_str());
return(mapId);
}
const G3D::Array<unsigned int>& getMaps() const { return iMapIds; }
bool isAlreadyProcessedSingleFile(const std::string& pName) const { return iProcesseSingleFiles.containsKey(pName); }
void addAlreadyProcessedSingleFile(const std::string& pName) { iProcesseSingleFiles.set(pName,pName); }
inline void addWorldAreaMap(unsigned int pMapId)
{
if(!iWorldAreaGroups.contains(pMapId))
{
iWorldAreaGroups.append(pMapId);
}
}
bool isWorldAreaMap(unsigned int pMapId) const { return(iWorldAreaGroups.contains(pMapId)); }
void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
};
}
#endif /*_COORDMODELMAPPING_H_*/

View file

@ -1,126 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "DebugCmdLogger.h"
#include <stdio.h>
using namespace G3D;
namespace VMAP
{
bool CommandFileRW::appendCmd(const Command&
#ifdef _DEBUG
pCommand
#endif
)
{
#ifdef _DEBUG
bool result = false;
if(iWritingEnabled || pCommand.isCoreCmd())
{
FILE* f = fopen(iFileName.c_str(), "ab");
if(f)
{
result = true;
if(fwrite(&pCommand, sizeof(Command), 1, f) != 1) { result = false; }
fclose(f);
}
}
else
{
result = true;
}
return result;
#else
return true;
#endif
}
//=========================================================
bool CommandFileRW::appendCmds(const Array<Command>&
#ifdef _DEBUG
pCmdArray
#endif
)
{
#ifdef _DEBUG
bool result = false;
if(iWritingEnabled)
{
FILE* f;
if(resetfile)
f = fopen(iFileName.c_str(), "wb");
else
f = fopen(iFileName.c_str(), "ab");
resetfile = false;
if(f)
{
result = true;
for(int i=0; i<pCmdArray.size(); ++i)
{
if(fwrite(&pCmdArray[i], sizeof(Command), 1, f) != 1) { result = false; break; }
}
fclose(f);
}
}
else
{
result = true;
}
return result;
#else
return true;
#endif
}
//=========================================================
bool CommandFileRW::getNewCommands(Array<Command>& pCmdArray)
{
bool result = false;
FILE* f = fopen(iFileName.c_str(), "rb");
if(f)
{
Command cmd;
if(fseek(f, iLastPos, SEEK_SET) == 0) { result = true; }
while(result)
{
if(fread(&cmd, sizeof(Command), 1, f) != 1)
{
result = false;
}
iLastPos = ftell(f);
if(cmd.getType() == STOP)
{
break;
}
pCmdArray.append(cmd);
}
fclose(f);
}
if(result)
{
iCommandArray.append(pCmdArray);
}
return(result);
}
//========================================================
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _DEBUGCMDLOGGER_H
#define _DEBUGCMDLOGGER_H
#include <G3D/Vector3.h>
#include <G3D/Array.h>
/**
Class is used for debugging. We log activities into a file.
With an external Class we read that log and display the activity in a graphical view.
*/
namespace VMAP
{
//==========================================
enum C_TYPES
{
STOP,
START,
LOAD_TILE,
UNLOAD_TILE,
SET_POS,
TEST_VIS,
LOAD_INSTANCE,
UNLOAD_INSTANCE,
TEST_HEIGHT,
TEST_OBJECT_HIT,
};
class Command
{
int iType;
float floats[9];
int ints[4];
char buffer[100];
public:
Command() { iType = STOP; }
inline int getType() { return iType; }
inline G3D::Vector3 getVector(int pos) { return(G3D::Vector3(floats[pos*3+0], floats[pos*3+1], floats[pos*3+2])); }
inline int getInt(int pos) { return(ints[pos]); }
inline char* getBuffer() { return(buffer); }
void fillStopCmd() { iType = STOP; }
void fillStartCmd() { iType = START; }
void fillLoadTileCmd(int x, int y, G3D::uint32 pMapId) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; ints[2] = pMapId; }
//void fillLoadTileCmd(int x,int y) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; }
void fillUnloadTileCmd(G3D::uint32 pMapId) { iType = UNLOAD_INSTANCE; ints[0] = pMapId; }
void fillUnloadTileCmd(unsigned int pMapId, int x,int y) { iType = UNLOAD_TILE; ints[0] = x; ints[1] = y; ints[0]=pMapId; }
void fillSetPosCmd(G3D::Vector3 pPos) { iType = SET_POS; floats[0] = pPos.x; floats[1]=pPos.y; floats[2]=pPos.z; }
void fillTestVisCmd(int pMapId, G3D::Vector3 pPos1, G3D::Vector3 pPos2, bool result)
{
iType = TEST_VIS; floats[0] = pPos1.x; floats[1]=pPos1.y; floats[2]=pPos1.z;
floats[3] = pPos2.x; floats[4]=pPos2.y; floats[5]=pPos2.z;
ints[0] = result; ints[1] = pMapId;
}
void fillTestHeightCmd(int pMapId, G3D::Vector3 pPos, float result)
{
iType = TEST_HEIGHT; floats[0] = pPos.x; floats[1]=pPos.y; floats[2]=pPos.z;
floats[3] = result; ints[0] = pMapId;
}
void fillTestObjectHitCmd(int pMapId, G3D::Vector3 pPos1, G3D::Vector3 pPos2, G3D::Vector3 pResultPos, bool result)
{
iType = TEST_OBJECT_HIT; floats[0] = pPos1.x; floats[1]=pPos1.y; floats[2]=pPos1.z;
floats[3] = pPos2.x; floats[4]=pPos2.y; floats[5]=pPos2.z;
floats[6] = pResultPos.x; floats[7]=pResultPos.y; floats[8]=pResultPos.z;
ints[0] = result; ints[1] = pMapId;
}
bool isCoreCmd() const { return(iType != TEST_VIS); }
};
//==========================================
class CommandFileRW
{
private:
std::string iFileName;
long iLastPos;
G3D::Array<G3D::Array<Command> > iCommandArray;
bool resetfile;
bool iWritingEnabled;
public:
CommandFileRW() { iLastPos=0; iWritingEnabled = true; resetfile = true;}
CommandFileRW(const std::string& pFileName) { iLastPos = 0; iFileName = pFileName; iWritingEnabled = true; resetfile = true; }
void setResetFile() { resetfile = true; }
void enableWriting(bool pValue) { iWritingEnabled = pValue; }
void setFileName(const std::string& pName) { iFileName = pName; }
bool getNewCommands(G3D::Array<Command>& commandArray);
const G3D::Array<G3D::Array<Command> >& getFullCommandArray() { return iCommandArray; }
bool appendCmd(const Command& pCommand);
bool appendCmds(const G3D::Array<Command>& pCmdArray);
};
}
#endif

View file

@ -20,6 +20,7 @@
#define _IVMAPMANAGER_H
#include<string>
#include <Platform/Define.h>
//===========================================================
@ -93,6 +94,12 @@ namespace VMAP
e.g.: "0,1,530"
*/
virtual void preventMapsFromBeingUsed(const char* pMapIdString) =0;
/**
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const=0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const=0;
};
}

View file

@ -27,30 +27,20 @@ AM_CPPFLAGS = $(MANGOS_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(sr
noinst_LIBRARIES = libmangosvmaps.a
libmangosvmaps_a_SOURCES = \
AABSPTree.h \
BaseModel.cpp \
BaseModel.h \
CoordModelMapping.cpp \
CoordModelMapping.h \
DebugCmdLogger.cpp \
DebugCmdLogger.h \
BIH.h \
BIH.cpp \
IVMapManager.h \
ManagedModelContainer.cpp \
ManagedModelContainer.h \
ModelContainer.cpp \
ModelContainer.h \
NodeValueAccess.h \
ShortBox.h \
ShortVector.h \
SubModel.cpp \
SubModel.h \
MapTree.cpp \
MapTree.h \
ModelInstance.cpp \
ModelInstance.h \
TileAssembler.cpp \
TileAssembler.h \
TreeNode.cpp \
TreeNode.h \
VMapDefinitions.h \
VMapFactory.cpp \
VMapFactory.h \
VMapManager.cpp \
VMapManager.h \
VMapTools.h
VMapManager2.cpp \
VMapManager2.h \
VMapTools.h \
WorldModel.cpp \
WorldModel.h

View file

@ -1,35 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ManagedModelContainer.h"
using namespace G3D;
namespace VMAP
{
ManagedModelContainer::ManagedModelContainer(void) : ModelContainer()
{
refCount = 0;
}
ManagedModelContainer::~ManagedModelContainer(void)
{
}
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MANAGEDMODELCONTAINER_H
#define _MANAGEDMODELCONTAINER_H
#include "ModelContainer.h"
//=======================================================
/**
This is a ModelContainer with reference count information.
*/
namespace VMAP
{
//=======================================================
class ManagedModelContainer :
public ModelContainer
{
private:
int refCount;
public:
ManagedModelContainer(void) ;
~ManagedModelContainer(void);
void incRefCount() { ++refCount; }
void decRefCount() { --refCount; if(refCount < 0) refCount = 0; }
int getRefCount() { return refCount; }
};
//=======================================================
}
#endif

467
src/shared/vmap/MapTree.cpp Normal file
View file

@ -0,0 +1,467 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "MapTree.h"
#include "ModelInstance.h"
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <limits>
using G3D::Vector3;
namespace VMAP
{
class MapRayCallback
{
public:
MapRayCallback(ModelInstance *val): prims(val) {}
ModelInstance *prims;
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit=true)
{
return prims[entry].intersectRay(ray, distance, pStopAtFirstHit);
//std::cout << "trying to intersect '" << entity->name << "'\n";
}
};
class AreaInfoCallback
{
public:
AreaInfoCallback(ModelInstance *val): prims(val) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
DEBUG_LOG("trying to intersect '%s'", prims[entry].name.c_str());
#endif
prims[entry].intersectPoint(point, aInfo);
}
ModelInstance *prims;
AreaInfo aInfo;
};
class LocationInfoCallback
{
public:
LocationInfoCallback(ModelInstance *val, LocationInfo &info): prims(val), locInfo(info), result(false) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
DEBUG_LOG("trying to intersect '%s'", prims[entry].name.c_str());
#endif
if (prims[entry].GetLocationInfo(point, locInfo))
result = true;
}
ModelInstance *prims;
LocationInfo &locInfo;
bool result;
};
//=========================================================
std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
{
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << std::setw(3) << mapID << "_";
//tilefilename << std::setw(2) << tileX << "_" << std::setw(2) << tileY << ".vmtile";
tilefilename << std::setw(2) << tileY << "_" << std::setw(2) << tileX << ".vmtile";
return tilefilename.str();
}
bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
AreaInfoCallback intersectionCallBack(iTreeValues);
iTree.intersectPoint(pos, intersectionCallBack);
if (intersectionCallBack.aInfo.result)
{
flags = intersectionCallBack.aInfo.flags;
adtId = intersectionCallBack.aInfo.adtId;
rootId = intersectionCallBack.aInfo.rootId;
groupId = intersectionCallBack.aInfo.groupId;
pos.z = intersectionCallBack.aInfo.ground_Z;
return true;
}
return false;
}
bool StaticMapTree::GetLocationInfo(const Vector3 &pos, LocationInfo &info) const
{
LocationInfoCallback intersectionCallBack(iTreeValues, info);
iTree.intersectPoint(pos, intersectionCallBack);
return intersectionCallBack.result;
}
StaticMapTree::StaticMapTree(uint32 mapID, const std::string &basePath):
iMapID(mapID), iTreeValues(0), iBasePath(basePath)
{
if (iBasePath.length() > 0 && (iBasePath[iBasePath.length()-1] != '/' || iBasePath[iBasePath.length()-1] != '\\'))
{
iBasePath.append("/");
}
}
//=========================================================
//! Make sure to call unloadMap() to unregister acquired model references before destroying
StaticMapTree::~StaticMapTree()
{
delete[] iTreeValues;
}
//=========================================================
/**
return dist to hit or inf() if no hit
*/
float StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit) const
{
float distance = pMaxDist;
MapRayCallback intersectionCallBack(iTreeValues);
iTree.intersectRay(pRay, intersectionCallBack, distance, pStopAtFirstHit);
return distance;
}
//=========================================================
bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const
{
bool result = true;
float maxDist = (pos2 - pos1).magnitude();
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too:
ASSERT(maxDist < std::numeric_limits<float>::max());
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
return true;
// direction with length of 1
G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
float resultDist = getIntersectionTime(ray, maxDist, true);
if (resultDist < maxDist)
{
result = false;
}
return result;
}
//=========================================================
/**
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
bool StaticMapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
{
bool result=false;
float maxDist = (pPos2 - pPos1).magnitude();
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too:
ASSERT(maxDist < std::numeric_limits<float>::max());
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
{
pResultHitPos = pPos2;
return false;
}
Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
G3D::Ray ray(pPos1, dir);
float dist = getIntersectionTime(ray, maxDist, false);
if (dist < maxDist)
{
pResultHitPos = pPos1 + dir * dist;
if (pModifyDist < 0)
{
if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
else
{
pResultHitPos = pPos1;
}
}
else
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
result = true;
}
else
{
pResultHitPos = pPos2;
result = false;
}
return result;
}
//=========================================================
float StaticMapTree::getHeight(const Vector3& pPos) const
{
float height = G3D::inf();
Vector3 dir = Vector3(0,0,-1);
G3D::Ray ray(pPos, dir); // direction with length of 1
float maxDist = VMapDefinitions::getMaxCanFallDistance();
float dist = getIntersectionTime(ray, maxDist, false);
if (dist < maxDist)
{
height = pPos.z - dist;
}
return(height);
}
//=========================================================
bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
{
std::string basePath = vmapPath;
if (basePath.length() > 0 && (basePath[basePath.length()-1] != '/' || basePath[basePath.length()-1] != '\\'))
basePath.append("/");
std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
bool success = true;
FILE *rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
// TODO: check magic number when implemented...
char tiled;
char chunk[8];
if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
{
fclose(rf);
return false;
}
if (tiled)
{
std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (!tf)
success = false;
else
{
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
success = false;
fclose(tf);
}
}
fclose(rf);
return success;
}
//=========================================================
bool StaticMapTree::InitMap(const std::string &fname, VMapManager2 *vm)
{
DEBUG_LOG("Initializing StaticMapTree '%s'", fname.c_str());
bool success = true;
std::string fullname = iBasePath + fname;
FILE *rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
else
{
char chunk[8];
//general info
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) success = false;
char tiled;
if (success && fread(&tiled, sizeof(char), 1, rf) != 1) success = false;
iIsTiled = bool(tiled);
// Nodes
if (success && !readChunk(rf, chunk, "NODE", 4)) success = false;
if (success) success = iTree.readFromFile(rf);
if (success)
{
iNTreeValues = iTree.primCount();
iTreeValues = new ModelInstance[iNTreeValues];
}
if (success && !readChunk(rf, chunk, "GOBJ", 4)) success = false;
// global model spawns
// only non-tiled maps have them, and if so exactly one (so far at least...)
ModelSpawn spawn;
#ifdef VMAP_DEBUG
DEBUG_LOG("Map isTiled: %u", static_cast<uint32>(iIsTiled));
#endif
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
DEBUG_LOG("StaticMapTree::InitMap(): loading %s", spawn.name.c_str());
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
iTreeValues[0] = ModelInstance(spawn, model);
iLoadedSpawns[0] = 1;
}
else
{
success = false;
ERROR_LOG("StaticMapTree::InitMap() could not acquire WorldModel pointer for '%s'!", spawn.name.c_str());
}
}
fclose(rf);
}
return success;
}
//=========================================================
void StaticMapTree::UnloadMap(VMapManager2 *vm)
{
for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
{
iTreeValues[i->first].setUnloaded();
for (uint32 refCount = 0; refCount < i->second; ++refCount)
vm->releaseModelInstance(iTreeValues[i->first].name);
}
iLoadedSpawns.clear();
iLoadedTiles.clear();
}
//=========================================================
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
{
if (!iIsTiled)
{
// currently, core creates grids for all maps, whether it has terrain tiles or not
// so we need "fake" tile loads to know when we can unload map geometry
iLoadedTiles[packTileID(tileX, tileY)] = false;
return true;
}
if (!iTreeValues)
{
ERROR_LOG("StaticMapTree::LoadMapTile(): Tree has not been initialized! [%u,%u]", tileX, tileY);
return false;
}
bool result = true;
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
char chunk[8];
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
result = false;
uint32 numSpawns;
if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// acquire model instance
WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
if (!model)
ERROR_LOG("StaticMapTree::LoadMapTile() could not acquire WorldModel pointer for '%s'!", spawn.name.c_str());
// update tree
uint32 referencedVal;
fread(&referencedVal, sizeof(uint32), 1, tf);
if (!iLoadedSpawns.count(referencedVal))
{
#ifdef VMAP_DEBUG
if (referencedVal > iNTreeValues)
{
DEBUG_LOG("invalid tree element! (%u/%u)", referencedVal, iNTreeValues);
continue;
}
#endif
iTreeValues[referencedVal] = ModelInstance(spawn, model);
iLoadedSpawns[referencedVal] = 1;
}
else
{
++iLoadedSpawns[referencedVal];
#ifdef VMAP_DEBUG
if (iTreeValues[referencedVal].ID != spawn.ID)
DEBUG_LOG("Error: trying to load wrong spawn in node!");
else if (iTreeValues[referencedVal].name != spawn.name)
DEBUG_LOG("Error: name mismatch on GUID=%u", spawn.ID);
#endif
}
}
}
iLoadedTiles[packTileID(tileX, tileY)] = true;
fclose(tf);
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
return result;
}
//=========================================================
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm)
{
uint32 tileID = packTileID(tileX, tileY);
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
if (tile == iLoadedTiles.end())
{
ERROR_LOG("StaticMapTree::UnloadMapTile(): Trying to unload non-loaded tile. Map:%u X:%u Y:%u", iMapID, tileX, tileY);
return;
}
if (tile->second) // file associated with tile
{
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
bool result=true;
char chunk[8];
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
result = false;
uint32 numSpawns;
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// release model instance
vm->releaseModelInstance(spawn.name);
// update tree
uint32 referencedNode;
fread(&referencedNode, sizeof(uint32), 1, tf);
if (!iLoadedSpawns.count(referencedNode))
{
ERROR_LOG("Trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
}
else if (--iLoadedSpawns[referencedNode] == 0)
{
iTreeValues[referencedNode].setUnloaded();
iLoadedSpawns.erase(referencedNode);
}
}
}
fclose(tf);
}
}
iLoadedTiles.erase(tile);
}
}

97
src/shared/vmap/MapTree.h Normal file
View file

@ -0,0 +1,97 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MAPTREE_H
#define _MAPTREE_H
#include "Platform/Define.h"
#include "Utilities/UnorderedMap.h"
#include "BIH.h"
namespace VMAP
{
class ModelInstance;
class GroupModel;
class VMapManager2;
struct LocationInfo
{
LocationInfo(): hitInstance(0), hitModel(0), ground_Z(-G3D::inf()) {};
const ModelInstance *hitInstance;
const GroupModel *hitModel;
float ground_Z;
};
class StaticMapTree
{
typedef UNORDERED_MAP<uint32, bool> loadedTileMap;
typedef UNORDERED_MAP<uint32, uint32> loadedSpawnMap;
private:
uint32 iMapID;
bool iIsTiled;
BIH iTree;
ModelInstance *iTreeValues; // the tree entries
uint32 iNTreeValues;
// Store all the map tile idents that are loaded for that map
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
loadedTileMap iLoadedTiles;
// stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
loadedSpawnMap iLoadedSpawns;
std::string iBasePath;
private:
float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit) const;
//bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
public:
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY);
static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX<<16 | tileY; }
static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY) { tileX = ID>>16; tileY = ID&0xFF; }
static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY);
StaticMapTree(uint32 mapID, const std::string &basePath);
~StaticMapTree();
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const;
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
float getHeight(const G3D::Vector3& pPos) const;
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLocationInfo(const Vector3 &pos, LocationInfo &info) const;
bool InitMap(const std::string &fname, VMapManager2 *vm);
void UnloadMap(VMapManager2 *vm);
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm);
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2 *vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
};
struct AreaInfo
{
AreaInfo(): result(false), ground_Z(-G3D::inf()) {};
bool result;
float ground_Z;
uint32 flags;
int32 adtId;
int32 rootId;
int32 groupId;
};
} // VMAP
#endif // _MAPTREE_H

View file

@ -1,375 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <iostream>
#include <fstream>
#include <string.h>
#include "ModelContainer.h"
#include "VMapDefinitions.h"
using namespace G3D;
namespace VMAP
{
//==========================================================
/**
Functions to use ModelContainer with a AABSPTree
*/
size_t hashCode(const ModelContainer& pMc)
{
return (pMc.getBasePosition() * pMc.getNTriangles()).hashCode();
}
//==========================================================
ModelContainer::ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel) :
BaseModel(pNNodes, pNTriangles)
{
iNSubModel = pNSubModel;
iSubModel = 0;
if(pNSubModel > 0) iSubModel = new SubModel[iNSubModel];
}
//==========================================================
bool ModelContainer::operator==(const ModelContainer& pMc2) const
{
if (this->iNSubModel == 0 && pMc2.iNSubModel == 0 && this->iSubModel == 0 && pMc2.iSubModel == 0)
return true;
return this == &pMc2;
}
//==========================================================
void ModelContainer::countSubModelsAndNodesAndTriangles(AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles)
{
// For this node we will need a TreeNode as well as for the internal nodes
++nNodes;
nSubModels += pNode.valueArray.size();
for(int i=0;i<pNode.valueArray.size(); i++)
{
G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i];
SubModel *m = h->value;
// add the internal nodes as well
nNodes += m->getNNodes();
nTriangles += m->getNTriangles();
}
if(pNode.child[0] != 0)
{
countSubModelsAndNodesAndTriangles(*pNode.child[0], nSubModels, nNodes, nTriangles);
}
if(pNode.child[1] != 0)
{
countSubModelsAndNodesAndTriangles(*pNode.child[1], nSubModels, nNodes, nTriangles);
}
}
//==========================================================
void ModelContainer::fillContainer(const AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi, Vector3& pFinalLo, Vector3& pFinalHi)
{
// TreeNode for the SubModel
TreeNode treeNode = TreeNode(pNode.valueArray.size(), pSubModelPos);
treeNode.setSplitAxis(pNode.splitAxis);
treeNode.setSplitLocation(pNode.splitLocation);
int currentTreeNodePos = pTreeNodePos++;
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
for(int i=0;i<pNode.valueArray.size(); i++)
{
G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i];
SubModel *m = h->value;
memcpy(&getTreeNodes()[pTreeNodePos], &m->getTreeNode(0), sizeof(TreeNode) * m->getNNodes());
memcpy(&getTriangles()[pTrianglePos], &m->getTriangle(0), sizeof(TriangleBox) * m->getNTriangles());
SubModel newModel = SubModel(m->getNTriangles(), getTriangles(), pTrianglePos, m->getNNodes(), getTreeNodes(), pTreeNodePos);
newModel.setReletiveBounds(m->getReletiveBounds().getLo(), m->getReletiveBounds().getHi());
newModel.setBasePosition(m->getBasePosition());
iSubModel[pSubModelPos++] = newModel;
pTreeNodePos += m->getNNodes();
pTrianglePos += m->getNTriangles();
AABox box = m->getAABoxBounds();
lo = lo.min(box.low());
hi = hi.max(box.high());
pFinalLo = pFinalLo.min(lo);
pFinalHi = pFinalHi.max(hi);
}
/*
if(pNode.valueArray.size() == 0) {
int xxx = 0; // just for the breakpoint
}
*/
// get absolute bounds
if(pNode.child[0] != 0)
{
treeNode.setChildPos(0, pTreeNodePos);
fillContainer(*pNode.child[0], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi);
}
if(pNode.child[1] != 0)
{
treeNode.setChildPos(1, pTreeNodePos);
fillContainer(*pNode.child[1], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi);
}
pLo = pLo.min(lo);
pHi = pHi.max(hi);
treeNode.setBounds(lo,hi);
setTreeNode(treeNode, currentTreeNodePos);
}
//==========================================================
/**
Create the structure out of a AABSPTree
*/
ModelContainer::ModelContainer(AABSPTree<SubModel *> *pTree)
{
int nSubModels, nNodes, nTriangles;
nSubModels = nNodes = nTriangles = 0;
countSubModelsAndNodesAndTriangles(*pTree->root, nSubModels, nNodes, nTriangles);
init(nNodes, nTriangles);
iNSubModel = nSubModels;
iSubModel = new SubModel[iNSubModel];
int subModelPos,treeNodePos, trianglePos;
subModelPos = treeNodePos = trianglePos = 0;
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
Vector3 finalLo, finalHi;
finalLo = lo;
finalHi = hi;
fillContainer(*pTree->root, subModelPos, treeNodePos, trianglePos, lo, hi, finalLo, finalHi);
setBounds(finalLo, finalHi);
}
//==========================================================
ModelContainer::~ModelContainer(void)
{
free();
if(iSubModel != 0) delete [] iSubModel;
}
//==========================================================
bool ModelContainer::writeFile(const char *filename)
{
bool result = false;
unsigned int flags=0;
unsigned int size;
FILE *wf =fopen(filename,"wb");
if(wf)
{
fwrite(VMAP_MAGIC,1,8,wf);
result = true;
if(result && fwrite("CTREE01",8,1,wf) != 1) result = false;
if(result && fwrite(&flags,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite("POS ",4,1,wf) != 1) result = false;
size = sizeof(float)*3;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
Vector3 basePos = getBasePosition();
if(result && fwrite(&basePos,sizeof(float),3,wf) != 3) result = false;
if(result && fwrite("BOX ",4,1,wf) != 1) result = false;
size = sizeof(float)*6;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
Vector3 low = iBox.low();
if(result && fwrite(&low,sizeof(float),3,wf) != 3) result = false;
Vector3 high = iBox.high();
if(result && fwrite(&high,sizeof(float),3,wf) != 3) result = false;
if(result && fwrite("NODE",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(TreeNode)*getNNodes();
if(result && fwrite(&size,4,1,wf) != 1) result = false;
unsigned int val = getNNodes();
if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(getTreeNodes(),sizeof(TreeNode),getNNodes(),wf) != getNNodes()) result = false;
if(result && fwrite("TRIB",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(TriangleBox)*getNTriangles();
if(result && fwrite(&size,4,1,wf) != 1) result = false;
val = getNTriangles();
if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(getTriangles(),sizeof(TriangleBox),getNTriangles(),wf) != getNTriangles()) result = false;
if(result && fwrite("SUBM",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(SubModel)*iNSubModel;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
if(result && fwrite(&iNSubModel,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(iSubModel,sizeof(SubModel),iNSubModel,wf) != iNSubModel) result = false;
fclose(wf);
}
return(result);
}
//===============================================================
bool ModelContainer::readFile(const char *filename)
{
bool result = false;
unsigned int flags;
unsigned int size;
char ident[8];
char chunk[4];
unsigned int ival;
FILE *rf = fopen(filename, "rb");
if(rf)
{
free();
result = true;
char magic[8]; // Ignore the added magic header
fread(magic,1,8,rf);
if(strncmp(VMAP_MAGIC,magic,8)) result = false;
if(result && fread(ident,8,1,rf) != 1) result = false;
if(result && fread(&flags,sizeof(unsigned int),1,rf) != 1) result = false;
//POS
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
Vector3 basePos;
if(result && fread(&basePos,sizeof(float),3,rf) != 3) result = false;
setBasePosition(basePos);
//---- Box
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
Vector3 low,high;
if(result && fread(&low,sizeof(float),3,rf) != 3) result = false;
if(result && fread(&high,sizeof(float),3,rf) != 3) result = false;
setBounds(low, high);
//---- TreeNodes
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false;
if(result) setNNodes(ival);
if(result) setTreeNodeArray(new TreeNode[getNNodes()]);
if(result && fread(getTreeNodes(),sizeof(TreeNode),getNNodes(),rf) != getNNodes()) result = false;
//---- TriangleBoxes
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false;
setNTriangles(ival);
if(result) setTriangleArray(new TriangleBox[getNTriangles()]);
if(result && fread(getTriangles(),sizeof(TriangleBox),getNTriangles(),rf) != getNTriangles()) result = false;
//---- SubModel
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
if(result && fread(&iNSubModel,sizeof(unsigned int),1,rf) != 1) result = false;
if(result) iSubModel = new SubModel[iNSubModel];
if(result)
{
for(unsigned int i=0;i<iNSubModel && result; ++i)
{
unsigned char readBuffer[52]; // this is the size of SubModel on 32 bit systems
if(fread(readBuffer,sizeof(readBuffer),1,rf) != 1) result = false;
iSubModel[i].initFromBinBlock(readBuffer);
iSubModel[i].setTriangleArray(getTriangles());
iSubModel[i].setTreeNodeArray(getTreeNodes());
}
}
fclose(rf);
}
return result;
}
//=================================================================
size_t ModelContainer::getMemUsage()
{
// BaseModel is included in ModelContainer
return(iNSubModel * sizeof(SubModel) + BaseModel::getMemUsage() + sizeof(ModelContainer) - sizeof(BaseModel));
}
//=================================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
extern Vector3 p1,p2,p3,p4,p5,p6,p7;
extern Array<AABox>gBoxArray;
extern int gCount1, gCount2, gCount3, gCount4;
extern bool myfound;
#endif
#endif
void ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const
{
IntersectionCallBack<SubModel> intersectCallback;
NodeValueAccess<TreeNode, SubModel> vna = NodeValueAccess<TreeNode, SubModel>(getTreeNodes(), iSubModel);
Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin() - getBasePosition(), pRay.direction());
iTreeNodes[0].intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false);
}
//==========================================================
bool ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist) const
{
return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist);
}
//=================================================================
template<typename RayCallback>
void ModelContainer::intersectRay(const G3D::Ray& pRay, RayCallback& intersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast)
{
if(intersect(pRay, pMaxDist))
{
NodeValueAccess<TreeNode, SubModel> vna = NodeValueAccess<TreeNode, SubModel>(getTreeNodes(), iSubModel);
iTreeNodes[0].intersectRay(pRay, intersectCallback, distance, vna, pStopAtFirstHit, true);
}
}
//=================================================================
void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox)
{
pAABox = pMc.getAABoxBounds();
}
//=================================================================
void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox)
{
pAABox = pMc->getAABoxBounds();
}
//=================================================================
} // VMAP

View file

@ -1,109 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MODELCONTAINER_H
#define _MODELCONTAINER_H
// load our modified version first !!
#include "AABSPTree.h"
#include <G3D/AABox.h>
#include <G3D/Vector3.h>
#include <G3D/Ray.h>
#include "ShortBox.h"
#include "TreeNode.h"
#include "VMapTools.h"
#include "SubModel.h"
#include "BaseModel.h"
namespace VMAP
{
/**
The ModelContainer is a balanced BSP-Tree of SubModels.
We store a map tile or an instance in one ModelContainer.
The ModelContainer manages the memory used for the tree nodes, the SubModels and its triangles in static arrays.
The tree nodes are used for the BSP-Tree of SubModels as well as for the BSP-Tree of triangles within one SubModel.
The references are done by indexes within these static arrays.
Therefore we are able to just load a binary block and do not need to mess around with memory allocation and pointers.
*/
//=====================================================
class ModelContainer : public BaseModel
{
private:
unsigned int iNSubModel;
SubModel *iSubModel;
G3D::AABox iBox;
// not allowed copy
explicit ModelContainer (const ModelContainer&);
ModelContainer& operator=(const ModelContainer&);
public:
ModelContainer() : BaseModel() { iNSubModel =0; iSubModel = 0; };
// for the mainnode
ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel);
ModelContainer(G3D::AABSPTree<SubModel *> *pTree);
~ModelContainer(void);
inline void setSubModel(const SubModel& pSubModel, int pPos) { iSubModel[pPos] = pSubModel; }
inline const SubModel& getSubModel(int pPos) const { return iSubModel[pPos]; }
inline unsigned int getNSubModel() const { return(iNSubModel); }
void countSubModelsAndNodesAndTriangles(G3D::AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles);
void fillContainer(const G3D::AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi, G3D::Vector3& pFinalLo, G3D::Vector3& pFinalHi);
bool readRawFile(const char *name);
inline const G3D::AABox& getAABoxBounds() const { return(iBox); }
inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBox.set(lo,hi); }
bool writeFile(const char *filename);
bool readFile(const char *filename);
size_t getMemUsage();
size_t hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); }
void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::Ray& pRay, float& pMaxDist) const;
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false);
bool operator==(const ModelContainer& pMc2) const;
};
//=====================================================
//=====================================================
size_t hashCode(const ModelContainer& pMc);
void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox);
void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox);
}
#endif

View file

@ -0,0 +1,217 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "ModelInstance.h"
#include "WorldModel.h"
#include "MapTree.h"
#include "VMapDefinitions.h"
using G3D::Vector3;
using G3D::Ray;
namespace VMAP
{
ModelInstance::ModelInstance(const ModelSpawn &spawn, WorldModel *model): ModelSpawn(spawn), iModel(model)
{
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iRot.y/180.f, G3D::pi()*iRot.x/180.f, G3D::pi()*iRot.z/180.f).inverse();
iInvScale = 1.f/iScale;
}
bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
DEBUG_LOG("<object not loaded>");
#endif
return false;
}
float time = pRay.intersectionTime(iBound);
if (time == G3D::inf())
{
#ifdef VMAP_DEBUG
DEBUG_LOG("Ray does not hit '%s'", name.c_str());
#endif
return false;
}
// child bounds are defined in object space:
Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
Ray modRay(p, iInvRot * pRay.direction());
float distance = pMaxDist * iInvScale;
bool hit = iModel->IntersectRay(modRay, distance, pStopAtFirstHit);
distance *= iScale;
pMaxDist = distance;
return hit;
}
void ModelInstance::intersectPoint(const G3D::Vector3& p, AreaInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
DEBUG_LOG("<object not loaded>");
#endif
return;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return;
if (!iBound.contains(p))
return;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
{
info.ground_Z = world_Z;
info.adtId = adtId;
}
}
}
bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
DEBUG_LOG("<object not loaded>");
#endif
return false;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return false;
if (!iBound.contains(p))
return false;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
{
info.ground_Z = world_Z;
info.hitInstance = this;
return true;
}
}
return false;
}
bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const
{
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (info.hitModel->GetLiquidLevel(pModel, zDist))
{
// calculate world height (zDist in model coords):
// assume WMO not tilted (wouldn't make much sense anyway)
liqHeight = zDist * iScale + iPos.z;
return true;
}
return false;
}
bool ModelSpawn::readFromFile(FILE *rf, ModelSpawn &spawn)
{
uint32 check=0, nameLen;
check += fread(&spawn.flags, sizeof(uint32), 1, rf);
// EoF?
if (!check)
{
if (ferror(rf))
ERROR_LOG("Error reading ModelSpawn!");
return false;
}
check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
check += fread(&spawn.ID, sizeof(uint32), 1, rf);
check += fread(&spawn.iPos, sizeof(float), 3, rf);
check += fread(&spawn.iRot, sizeof(float), 3, rf);
check += fread(&spawn.iScale, sizeof(float), 1, rf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if (has_bound) // only WMOs have bound in MPQ, only available after computation
{
Vector3 bLow, bHigh;
check += fread(&bLow, sizeof(float), 3, rf);
check += fread(&bHigh, sizeof(float), 3, rf);
spawn.iBound = G3D::AABox(bLow, bHigh);
}
check += fread(&nameLen, sizeof(uint32), 1, rf);
if(check != (has_bound ? 17 : 11))
{
ERROR_LOG("Error reading ModelSpawn!");
return false;
}
char nameBuff[500];
if (nameLen>500) // file names should never be that long, must be file error
{
ERROR_LOG("Error reading ModelSpawn, file name too long!");
return false;
}
check = fread(nameBuff, sizeof(char), nameLen, rf);
if (check != nameLen)
{
ERROR_LOG("Error reading name string of ModelSpawn!");
return false;
}
spawn.name = std::string(nameBuff, nameLen);
return true;
}
bool ModelSpawn::writeToFile(FILE *wf, const ModelSpawn &spawn)
{
uint32 check=0;
check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if(has_bound) // only WMOs have bound in MPQ, only available after computation
{
check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
}
uint32 nameLen = spawn.name.length();
check += fwrite(&nameLen, sizeof(uint32), 1, wf);
if(check != (has_bound ? 17 : 11)) return false;
check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
if(check != nameLen) return false;
return true;
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _MODELINSTANCE_H_
#define _MODELINSTANCE_H_
#include <G3D/Matrix3.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "Platform/Define.h"
namespace VMAP
{
class WorldModel;
struct AreaInfo;
struct LocationInfo;
enum ModelFlags
{
MOD_M2 = 1,
MOD_WORLDSPAWN = 1<<1,
MOD_HAS_BOUND = 1<<2
};
class ModelSpawn
{
public:
//mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
uint32 flags;
uint16 adtId;
uint32 ID;
G3D::Vector3 iPos;
G3D::Vector3 iRot;
float iScale;
G3D::AABox iBound;
std::string name;
bool operator==(const ModelSpawn &other) const { return ID == other.ID; }
//uint32 hashCode() const { return ID; }
// temp?
const G3D::AABox& getBounds() const { return iBound; }
static bool readFromFile(FILE *rf, ModelSpawn &spawn);
static bool writeToFile(FILE *rw, const ModelSpawn &spawn);
};
class ModelInstance: public ModelSpawn
{
public:
ModelInstance(): iModel(0) {}
ModelInstance(const ModelSpawn &spawn, WorldModel *model);
void setUnloaded() { iModel = 0; }
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit) const;
void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const;
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const;
protected:
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel *iModel;
};
} // namespace VMAP
#endif // _MODELINSTANCE

View file

@ -1,48 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NODEVALUEACCESS_H
#define _NODEVALUEACCESS_H
namespace VMAP
{
/**
This is a helper Class to get access to SubModels or triangles when analyzing the BSP-Tree.
*/
template<class TNode, class TValue> class NodeValueAccess
{
private:
TNode const* iNodeArray;
TValue const* iValueArray;
public:
inline NodeValueAccess() : iNodeArray(NULL), iValueArray(NULL) {}
inline NodeValueAccess(TNode const* pNodeArray, TValue const* pValueArray) : iNodeArray(pNodeArray), iValueArray(pValueArray) {}
inline TNode const* getNodePtr() const { return(iNodeArray); }
inline TValue const* getValuePtr() const { return(iValueArray); }
inline TNode const& getNode(unsigned int pPos) const { return(iNodeArray[pPos]); }
inline void setNode(const TNode& pNode, unsigned int pPos) { iNodeArray[pPos] = pNode; }
inline TValue const& getValue(unsigned int pPos) const { return(iValueArray[pPos]); }
inline void setValue(const TValue& pValue, unsigned int pPos) { iValueArray[pPos] = pValue; }
};
}
#endif

View file

@ -1,148 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SHORTBOX_H
#define _SHORTBOX_H
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Triangle.h>
#include <G3D/Ray.h>
#include "ShortVector.h"
/**
This is a box and a triangle Class using ShortVectors. Each vector has 16 bit an a fixed point 12.4 representation.
*/
namespace VMAP
{
class ShortBox
{
private:
ShortVector iV1;
ShortVector iV2;
public:
ShortBox() {}
inline const ShortVector& getLo() const { return(iV1); }
inline const ShortVector& getHi() const { return(iV2); }
inline void setLo(const ShortVector& pV){ iV1 = pV; }
inline void setHi(const ShortVector& pV){ iV2 = pV; }
inline void setLo(const G3D::Vector3& pV){ iV1 = ShortVector(pV); }
inline void setHi(const G3D::Vector3& pV){ iV2 = ShortVector(pV); }
inline bool operator==(const ShortBox& b) const
{
return ((iV1 == b.iV1) && (iV2 == b.iV2));
}
inline bool operator!=(const ShortBox& b) const
{
return !((iV1 == b.iV1) && (iV2 == b.iV2));
}
};
//=====================================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
extern G3D::Vector3 p1,p2,p3,p4,p5,p6,p7;
extern G3D::Array<G3D::AABox>gBoxArray;
extern G3D::Array<G3D::Triangle>gTriArray;
extern int gCount1, gCount2, gCount3, gCount4;
extern bool myfound;
#endif
#endif
static const G3D::Vector3 dummyZeroPosition = G3D::Vector3(0,0,0);
class TriangleBox
{
private:
ShortVector _vertex[3];
//ShortBox iBox;
public:
inline TriangleBox() { }
inline TriangleBox(const ShortVector& pV1, const ShortVector& pV2, const ShortVector& pV3)
{
_vertex[0] = pV1;
_vertex[1] = pV2;
_vertex[2] = pV3;
}
inline const ShortVector& vertex (int n) const
{
return(_vertex[n]);
}
inline const ShortBox getBounds()const
{
ShortBox box;
ShortVector lo = _vertex[0];
ShortVector hi = lo;
for (int i = 1; i < 3; ++i)
{
lo = lo.min(_vertex[i]);
hi = hi.max(_vertex[i]);
}
box.setLo(lo);
box.setHi(hi);
return(box);
}
inline const G3D::Vector3& getBasePosition() { return(dummyZeroPosition); }
inline const G3D::AABox getAABoxBounds() const { ShortBox box = getBounds(); return(G3D::AABox(box.getLo().getVector3(), box.getHi().getVector3())); }
inline bool operator==(const TriangleBox& t) const
{
return ((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2]));
}
inline bool operator!=(const TriangleBox& t) const
{
return !((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2]));
}
inline void intersect(const G3D::Ray& pRay, float& pMaxDist, bool /*pStopAtFirstHitDummy*/, G3D::Vector3& /*pOutLocationDummy*/, G3D::Vector3& /*pOutNormalDummy*/) const
{
static const double epsilon = 0.00001;
G3D::Triangle testT(vertex(0).getVector3(),vertex(1).getVector3(),vertex(2).getVector3());
float t = pRay.intersectionTime(testT);
if ((t < pMaxDist) || t < (pMaxDist + epsilon))
pMaxDist = t;
else
{
testT = G3D::Triangle(vertex(2).getVector3(),vertex(1).getVector3(),vertex(0).getVector3());
#ifdef _DEBUG_VMAPS
{
G3D::Triangle myt(testT.vertex(0)+p6, testT.vertex(1)+p6,testT.vertex(2)+p6);
gTriArray.push_back(myt);
}
#endif
t = pRay.intersectionTime(testT);
if ((t < pMaxDist) || t < (pMaxDist + epsilon))
pMaxDist = t;
}
}
};
}
#endif

View file

@ -1,134 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SHORTVECTOR_H
#define _SHORTVECTOR_H
#include <G3D/Vector3.h>
namespace VMAP
{
/**
Vector with 16 bit fix point values 12.4 bit.
*/
class ShortVector
{
private:
short iX;
short iY;
short iZ;
const static short maxvalue = 0x7fff;
const static short minvalue = -0x7fff;
const static int fixpointdiv = 16;
const static short fixpoint_maxvalue = maxvalue / fixpointdiv;
const static short fixpoint_minvalue = minvalue / fixpointdiv;
inline short float2Short(float fv) const
{
short sv;
debugAssert((fv <= fixpoint_maxvalue || fv >= 1000000) && (fv >= fixpoint_minvalue || fv <= -1000000));
if(fv >= fixpoint_maxvalue)
sv=maxvalue;
else if(fv <= fixpoint_minvalue)
sv=minvalue;
else
sv = (short) (fv * fixpointdiv + 0.5);
return(sv);
}
inline float short2Float(short sv) const
{
float fv;
if(sv >= maxvalue)
fv=G3D::inf();
else if(sv <= minvalue)
fv=-G3D::inf();
else
fv = ((float)sv) / fixpointdiv;
return fv;
}
inline float getFX() const { return(short2Float(iX)); }
inline float getFY() const { return(short2Float(iY)); }
inline float getFZ() const { return(short2Float(iZ)); }
public:
inline ShortVector() {}
inline ShortVector(const G3D::Vector3& pVector)
{
iX = float2Short(pVector.x);
iY = float2Short(pVector.y);
iZ = float2Short(pVector.z);
}
inline ShortVector(float pX, float pY, float pZ)
{
iX = float2Short(pX);
iY = float2Short(pY);
iZ = float2Short(pZ);
}
inline ShortVector(short pX, short pY, short pZ)
{
iX = pX;
iY = pY;
iZ = pZ;
}
inline ShortVector(const ShortVector& pShortVector)
{
iX = pShortVector.iX;
iY = pShortVector.iY;
iZ = pShortVector.iZ;
}
inline float getX() const { return(iX); }
inline float getY() const { return(iY); }
inline float getZ() const { return(iZ); }
inline G3D::Vector3 getVector3() const { return(G3D::Vector3(getFX(), getFY(), getFZ())); }
inline ShortVector min(const ShortVector pShortVector)
{
ShortVector result = pShortVector;
if(pShortVector.iX > iX) { result.iX = iX; }
if(pShortVector.iY > iY) { result.iY = iY; }
if(pShortVector.iZ > iZ) { result.iZ = iZ; }
return(result);
}
inline ShortVector max(const ShortVector pShortVector)
{
ShortVector result = pShortVector;
if(pShortVector.iX < iX) { result.iX = iX; }
if(pShortVector.iY < iY) { result.iY = iY; }
if(pShortVector.iZ < iZ) { result.iZ = iZ; }
return(result);
}
inline bool operator==(const ShortVector& v) const
{
return (iX == v.iX && iY == v.iY && iZ == v.iZ);
}
inline bool operator!=(const ShortVector& v) const
{
return !(iX == v.iX && iY == v.iY && iZ == v.iZ);
}
};
}
#endif

View file

@ -1,248 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "SubModel.h"
#ifdef _ASSEMBLER_DEBUG
extern FILE *::g_df;
#endif
using namespace G3D;
namespace VMAP
{
//==========================================================
/**
Functions to use ModelContainer with a AABSPTree
*/
unsigned int hashCode(const SubModel& pSm)
{
return pSm.getNTriangles();
}
void getBounds(const SubModel& pSm, G3D::AABox& pAABox)
{
ShortBox box = pSm.getReletiveBounds();
pAABox.set(box.getLo().getVector3()+pSm.getBasePosition(), box.getHi().getVector3()+pSm.getBasePosition());
}
void getBounds(const SubModel* pSm, G3D::AABox& pAABox)
{
ShortBox box = pSm->getReletiveBounds();
pAABox.set(box.getLo().getVector3()+pSm->getBasePosition(), box.getHi().getVector3()+pSm->getBasePosition());
}
//==========================================================
//==========================================================
//==========================================================
//==========================================================
SubModel::SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos) :
BaseModel(pNNodes, pTreeNodes, pNTriangles, pTriangles)
{
iTrianglesPos = pTrianglesPos;
iNodesPos = pNodesPos;
iHasInternalMemAlloc = false;
}
//==========================================================
SubModel::~SubModel(void)
{
if(iHasInternalMemAlloc)
{
free();
}
}
//==========================================================
bool SubModel::operator==(const SubModel& pSm2) const
{
bool result = false;
if(getNNodes() == pSm2.getNNodes() &&
getNTriangles() == pSm2.getNTriangles() &&
getBasePosition() == pSm2.getBasePosition() &&
getNodesPos() == pSm2.getNodesPos() &&
getTrianglesPos() == pSm2.getTrianglesPos())
{
result = true;
}
return result;
}
//==========================================================
enum BIN_POSITIONS
{
BP_iNTriangles=8,
BP_iNNodes=12,
BP_iBasePosition=16,
BP_iNodesPos=28,
BP_iTrianglesPos=32,
BP_iHasInternalMemAlloc=36,
BP_iBox=38,
};
/**
This is ugly, but due to compatibility and 64 bit support we have to do that ... sorry
*/
void SubModel::initFromBinBlock(void *pBinBlock)
{
iNTriangles = *((unsigned int *)(((char *) pBinBlock) + BP_iNTriangles));
iNNodes = *((unsigned int *) (((char *) pBinBlock) + BP_iNNodes));
iBasePosition = *((Vector3 *) (((char *) pBinBlock) + BP_iBasePosition));
iNodesPos = *((unsigned int *) (((char *) pBinBlock) + BP_iNodesPos));
iTrianglesPos = *((unsigned int *) (((char *) pBinBlock) + BP_iTrianglesPos));
iHasInternalMemAlloc = *((bool *) (((char *) pBinBlock) + BP_iHasInternalMemAlloc));
iBox = *((ShortBox *) (((char *) pBinBlock) + BP_iBox));
}
//==========================================================
void SubModel::countNodesAndTriangles(AABSPTree<Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles)
{
++pNNodes;
pNTriabgles += pNode.valueArray.size();
#ifdef _ASSEMBLER_DEBUG
fprintf(::g_df, "Nodes: %d, Tris: %d\n",pNNodes, pNTriabgles);
#endif
if(pNode.child[0] != 0)
{
countNodesAndTriangles(*pNode.child[0], pNNodes, pNTriabgles);
}
if(pNode.child[1] != 0)
{
countNodesAndTriangles(*pNode.child[1], pNNodes, pNTriabgles);
}
}
//==========================================================
void SubModel::fillContainer(const AABSPTree<Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi)
{
TreeNode treeNode = TreeNode(pNode.valueArray.size(), pTrianglePos);
treeNode.setSplitAxis(pNode.splitAxis);
treeNode.setSplitLocation(pNode.splitLocation);
int currentTreeNodePos = pTreeNodePos++;
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
for(int i=0;i<pNode.valueArray.size(); i++)
{
G3D::_AABSPTree::Handle<Triangle>* h= pNode.valueArray[i];
Triangle t = h->value;
TriangleBox triangleBox = TriangleBox(t.vertex(0),t.vertex(1), t.vertex(2));
lo = lo.min(triangleBox.getBounds().getLo().getVector3());
hi = hi.max(triangleBox.getBounds().getHi().getVector3());
getTriangles()[pTrianglePos++] = triangleBox;
}
if(pNode.child[0] != 0)
{
treeNode.setChildPos(0, pTreeNodePos);
fillContainer(*pNode.child[0], pTreeNodePos, pTrianglePos, lo, hi);
}
if(pNode.child[1] != 0)
{
treeNode.setChildPos(1, pTreeNodePos);
fillContainer(*pNode.child[1], pTreeNodePos, pTrianglePos, lo, hi);
}
treeNode.setBounds(lo,hi);
// get absolute bounds
pLo = pLo.min(lo);
pHi = pHi.max(hi);
getTreeNodes()[currentTreeNodePos] = treeNode;
}
//==========================================================
SubModel::SubModel(AABSPTree<Triangle> *pTree)
{
int nNodes, nTriangles;
nNodes = nTriangles = 0;
countNodesAndTriangles(*pTree->root, nNodes, nTriangles);
init(nNodes, nTriangles);
iTrianglesPos = 0; // this is the global array
iNodesPos = 0; // this is the global array
iHasInternalMemAlloc = true;
int treeNodePos, trianglePos;
treeNodePos = trianglePos = 0;
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
fillContainer(*pTree->root, treeNodePos, trianglePos, lo, hi);
setReletiveBounds(lo, hi);
}
//==========================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
extern Vector3 p1,p2,p3,p4,p5,p6,p7;
extern Array<AABox>gBoxArray;
extern Array<G3D::Triangle>gTriArray;
extern int gCount1, gCount2, gCount3, gCount4;
extern bool myfound;
#endif
#endif
//==========================================================
void SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const
{
NodeValueAccess<TreeNode, TriangleBox> vna = NodeValueAccess<TreeNode, TriangleBox>(getTreeNodes(), getTriangles());
IntersectionCallBack<TriangleBox> intersectCallback;
Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin() - getBasePosition(), pRay.direction());
#ifdef _DEBUG_VMAPS
//p6=getBasePosition();
//gBoxArray.push_back(getAABoxBounds());
#endif
getTreeNode(0).intersectRay(relativeRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false);
}
//==========================================================
bool SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist) const
{
return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist);
}
//==========================================================
template<typename RayCallback>
void SubModel::intersectRay(const Ray& pRay, RayCallback& pIntersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast)
{
if(intersect(pRay, pMaxDist))
{
NodeValueAccess<TreeNode, TriangleBox> vna = NodeValueAccess<TreeNode, TriangleBox>(getTreeNodes(), getTriangles());
IntersectionCallBack<TriangleBox> intersectCallback;
getTreeNode(0).intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false);
}
}
//==========================================================
}

View file

@ -1,102 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SUBMODEL_H
#define _SUBMODEL_H
// load our modified version first !!
#include "AABSPTree.h"
#include "ShortVector.h"
#include "ShortBox.h"
#include "TreeNode.h"
#include "VMapTools.h"
#include "BaseModel.h"
namespace VMAP
{
/**
This is a balanced static BSP-Tree of triangles.
The memory for the tree nodes and the triangles are managed by the ModelContainer.
The exception to this is during the conversion of raw data info balanced BSP-Trees.
During this conversion the memory management is done internally.
*/
class SubModel : public BaseModel
{
private:
unsigned int iNodesPos;
unsigned int iTrianglesPos;
bool iHasInternalMemAlloc;
ShortBox iBox;
#ifdef _DEBUG_VIEW
G3D::Array<TriangleBox *> iDrawBox;
#endif
public:
SubModel() : BaseModel(){ };
SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos);
SubModel(G3D::AABSPTree<G3D::Triangle> *pTree);
~SubModel(void);
//Gets a 50 byte binary block
void initFromBinBlock(void *pBinBlock);
void fillRenderArray(G3D::Array<TriangleBox> &pArray, const TreeNode* pTreeNode);
void countNodesAndTriangles(G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles);
void fillContainer(const G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi);
inline const ShortBox& getReletiveBounds() const { return(iBox); }
inline void setReletiveBounds(const ShortVector& lo, const ShortVector& hi) { iBox.setLo(lo); iBox.setHi(hi); }
inline const G3D::AABox getAABoxBounds() const { return(G3D::AABox(iBox.getLo().getVector3() + getBasePosition(), iBox.getHi().getVector3()+ getBasePosition())); }
// get start pos bases on the global array
inline TriangleBox const* getTriangles() const { return &BaseModel::getTriangle(iTrianglesPos); }
inline TriangleBox * getTriangles() { return &BaseModel::getTriangle(iTrianglesPos); }
// get start pos bases on the global array
inline TreeNode const* getTreeNodes() const { return &BaseModel::getTreeNode(iNodesPos); }
inline TreeNode * getTreeNodes() { return &BaseModel::getTreeNode(iNodesPos); }
// internal method usign internal offset
inline const TreeNode& getTreeNode(int pPos) const { return(SubModel::getTreeNodes()[pPos]); }
// internal method usign internal offset
inline const TriangleBox& getTriangle(int pPos) const { return(SubModel::getTriangles()[pPos]); }
inline unsigned int getNodesPos() const { return(iNodesPos); }
inline unsigned int getTrianglesPos() const { return(iTrianglesPos); }
//unsigned int hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); }
void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::Ray& pRay, float& pMaxDist) const;
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false);
bool operator==(const SubModel& pSm2) const;
unsigned int hashCode() const { return BaseModel::getNTriangles(); }
};
unsigned int hashCode(const SubModel& pSm);
void getBounds(const SubModel& pSm, G3D::AABox& pAABox);
void getBounds(const SubModel* pSm, G3D::AABox& pAABox);
//====================================
} // VMAP
#endif

View file

@ -16,36 +16,42 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <G3D/Vector3.h>
#include <G3D/Triangle.h>
#include "WorldModel.h"
#include "TileAssembler.h"
#include "CoordModelMapping.h"
#include "ModelContainer.h"
#include "MapTree.h"
#include "BIH.h"
#include "VMapDefinitions.h"
#include <limits.h>
#include <string.h>
#include <set>
#include <iomanip>
#include <sstream>
#include <iomanip>
#ifdef _ASSEMBLER_DEBUG
FILE *g_df = NULL;
#endif
using G3D::Vector3;
using G3D::AABox;
using G3D::inf;
using std::pair;
using namespace G3D;
template<> struct BoundsTrait<VMAP::ModelSpawn*>
{
static void getBounds(const VMAP::ModelSpawn* const &obj, G3D::AABox& out) { out = obj->getBounds(); }
};
namespace VMAP
{
//=================================================================
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len)
{
if (fread(dest, sizeof(char), len, rf) != len) return false;
return memcmp(dest, compare, len) == 0;
}
Vector3 ModelPosition::transform(const Vector3& pIn) const
{
//return(pIn);
Vector3 out = pIn * iScale;
out = izMatrix * out;
out = ixMatrix * out;
out = iyMatrix * out;
out = iRotation * out;
return(out);
}
//=================================================================
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
@ -55,320 +61,302 @@ namespace VMAP
iSrcDir = pSrcDirName;
iDestDir = pDestDirName;
//mkdir(iDestDir);
init();
//init();
}
//=================================================================
TileAssembler::~TileAssembler()
{
delete iCoordModelMapping;
//delete iCoordModelMapping;
}
//=================================================================
void TileAssembler::init()
bool TileAssembler::convertWorld2()
{
iCoordModelMapping = new CoordModelMapping();
addWorldAreaMapId(0); //Azeroth
addWorldAreaMapId(1); //Kalimdor
addWorldAreaMapId(530); //Expansion01
addWorldAreaMapId(571); //Expansion02
}
//=================================================================
std::string getModNameFromModPosName(const std::string& pModPosName)
{
size_t spos = pModPosName.find_first_of('#');
std::string modelFileName = pModPosName.substr(0,spos);
return(modelFileName);
}
//=================================================================
unsigned int TileAssembler::getUniqueNameId(const std::string pName)
{
unsigned int result;
if(!iUniqueNameIds.containsKey(pName))
{
++iCurrentUniqueNameId;
iUniqueNameIds.set(pName, iCurrentUniqueNameId);
}
result = iUniqueNameIds.get(pName);
return result;
}
//=================================================================
std::string TileAssembler::getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName)
{
size_t spos;
char buffer[20];
std::string modelFileName = getModNameFromModPosName(pModPosName);
//std::string fext = pModPosName.substr(modelFileName.length(),pModPosName.length());
unsigned int fextId = getUniqueNameId(pModPosName);
sprintf(buffer, "_%07d",fextId);
std::string fext(buffer);
spos = modelFileName.find_last_of('/');
std::string fname = modelFileName.substr(spos+1, modelFileName.length());
spos = fname.find_last_of('.');
fname = fname.substr(0,spos);
sprintf(buffer, "%03u", pMapId);
std::string dirEntry(buffer);
dirEntry.append("_");
dirEntry.append(fname);
dirEntry.append(fext);
dirEntry.append(".vmap");
return(dirEntry);
}
//=================================================================
void emptyArray(Array<ModelContainer*>& mc)
{
int no=mc.size();
while(no > 0)
{
--no;
delete mc[no];
mc.remove(no);
}
}
//=================================================================
bool TileAssembler::convertWorld()
{
#ifdef _ASSEMBLER_DEBUG
# ifdef _DEBUG
::g_df = fopen("../TileAssembler_debug.txt", "wb");
# else
::g_df = fopen("../TileAssembler_release.txt", "wb");
# endif
#endif
std::string fname = iSrcDir;
fname.append("/");
fname.append("dir");
iCoordModelMapping->setModelNameFilterMethod(iFilterMethod);
printf("Read coordinate mapping...\n");
if(!iCoordModelMapping->readCoordinateMapping(fname))
std::set<std::string> spawnedModelFiles;
bool success = readMapSpawns();
if (!success)
return false;
Array<unsigned int> mapIds = iCoordModelMapping->getMaps();
if(mapIds.size() == 0)
// export Map data
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
{
printf("Fatal error: empty map list!\n");
return false;
}
for(int i=0; i<mapIds.size(); ++i)
// build global map tree
std::vector<ModelSpawn*> mapSpawns;
UniqueEntryMap::iterator entry;
printf("Calculating model bounds for map %u...\n", map_iter->first);
for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
{
unsigned int mapId = mapIds[i];
#ifdef _ASSEMBLER_DEBUG
if(mapId == 0) // "Azeroth" just for debug
// M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
if (entry->second.flags & MOD_M2)
{
for(int x=28; x<29; ++x) //debug
{
for(int y=28; y<29; ++y)
{
#else
// ignore DeeprunTram (369) it is too large for short vector and not important
// ignore test (13), Test (29) , development (451)
if(mapId != 369 && mapId != 13 && mapId != 29 && mapId != 451)
{
for(int x=0; x<66; ++x)
{
for(int y=0; y<66; ++y)
{
#endif
Array<ModelContainer*> mc;
std::string dirname;
char buffer[100];
if(iCoordModelMapping->isWorldAreaMap(mapId) && x<65 && y<65)
{
sprintf(buffer, "%03u_%d_%d",mapId,y,x); // Let's flip x and y here
dirname = std::string(buffer);
printf("%s...\n",dirname.c_str());
}
else
{
sprintf(buffer, "%03u",mapId);
dirname = std::string(buffer);
// prevent spam for small maps
if(x==0 && y==0)
printf("%s...\n",dirname.c_str());
}
bool result = fillModelContainerArray(dirname, mapId, x, y, mc);
emptyArray(mc);
if(!result)
return false;
}
}
}
}
#ifdef _ASSEMBLER_DEBUG
if(::g_df) fclose(::g_df);
#endif
return true;
}
//=================================================================
bool TileAssembler::fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, Array<ModelContainer*>& pMC)
{
ModelContainer* modelContainer;
NameCollection nameCollection = iCoordModelMapping->getFilenamesForCoordinate(pMapId, pXPos, pYPos);
if(nameCollection.size() == 0)
return true; // no data...
char dirfilename[500];
sprintf(dirfilename,"%s/%s.vmdir",iDestDir.c_str(),pDirFileName.c_str());
FILE *dirfile = fopen(dirfilename, "ab");
if(!dirfile)
{
printf("ERROR: Can't create file %s",dirfilename);
return false;
}
char destnamebuffer[500];
char fullnamedestnamebuffer[500];
if(nameCollection.iMainFiles.size() >0)
{
sprintf(destnamebuffer,"%03u_%i_%i.vmap",pMapId, pYPos, pXPos); // flip it here too
std::string checkDoubleStr = std::string(dirfilename);
checkDoubleStr.append("##");
checkDoubleStr.append(std::string(destnamebuffer));
// Check, if same file already is in the same dir file
if(!iCoordModelMapping->isAlreadyProcessedSingleFile(checkDoubleStr))
{
iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr);
fprintf(dirfile, "%s\n",destnamebuffer);
sprintf(fullnamedestnamebuffer,"%s/%s",iDestDir.c_str(),destnamebuffer);
modelContainer = processNames(nameCollection.iMainFiles, fullnamedestnamebuffer);
if(modelContainer)
pMC.append(modelContainer);
else
printf("warning: (if) problems in processing data for %s\n",destnamebuffer);
}
}
// process the large singe files
int pos = 0;
while(pos < nameCollection.iSingeFiles.size())
{
std::string destFileName = iDestDir;
destFileName.append("/");
std::string dirEntryName = getDirEntryNameFromModName(pMapId,nameCollection.iSingeFiles[pos]);
std::string checkDoubleStr = std::string(dirfilename);
checkDoubleStr.append("##");
checkDoubleStr.append(nameCollection.iSingeFiles[pos]);
// Check, if same file already is in the same dir file
if(!iCoordModelMapping->isAlreadyProcessedSingleFile(checkDoubleStr))
{
iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr);
fprintf(dirfile, "%s\n",dirEntryName.c_str());
destFileName.append(dirEntryName);
Array<std::string> positionarray;
positionarray.append(nameCollection.iSingeFiles[pos]);
if(!iCoordModelMapping->isAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos]))
{
modelContainer = processNames(positionarray, destFileName.c_str());
iCoordModelMapping->addAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos]);
if(modelContainer)
pMC.append(modelContainer);
else
printf("warning: (while) problems in processing data for %s\n",destFileName.c_str());
}
}
++pos;
}
fclose(dirfile);
return true;
}
//=================================================================
void removeEntriesFromTree(AABSPTree<SubModel *>* pTree)
{
Array<SubModel *> submodelArray;
pTree->getMembers(submodelArray);
int no = submodelArray.size();
while(no > 0)
{
--no;
delete submodelArray[no];
}
}
//=================================================================
ModelContainer* TileAssembler::processNames(const Array<std::string>& pPositions, const char* pDestFileName)
{
ModelContainer *modelContainer = 0;
Vector3 basepos = Vector3(0,0,0);
AABSPTree<SubModel *>* mainTree = new AABSPTree<SubModel *>();
int pos = 0;
bool result = true;
while(result && (pos < pPositions.size()))
{
std::string modelPosString = pPositions[pos];
std::string modelFileName = getModNameFromModPosName(modelPosString);
if(!fillModelIntoTree(mainTree, basepos, modelPosString, modelFileName))
{
result = false;
if (!calculateTransformedBound(entry->second))
break;
}
++pos;
}
if(result && mainTree->size() > 0)
else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
{
mainTree->balance();
modelContainer = new ModelContainer(mainTree);
modelContainer->writeFile(pDestFileName);
// TODO: remove extractor hack and uncomment below line:
//entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
entry->second.iBound = entry->second.iBound + Vector3(533.33333f*32, 533.33333f*32, 0.f);
}
removeEntriesFromTree(mainTree);
delete mainTree;
return(modelContainer);
mapSpawns.push_back(&(entry->second));
spawnedModelFiles.insert(entry->second.name);
}
printf("Creating map tree...\n");
BIH pTree;
pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::getBounds);
// ===> possibly move this code to StaticMapTree class
std::map<uint32, uint32> modelNodeIdx;
for (uint32 i=0; i<mapSpawns.size(); ++i)
modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
// write map tree file
std::stringstream mapfilename;
mapfilename << iDestDir << "/" << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
FILE *mapfile = fopen(mapfilename.str().c_str(), "wb");
if (!mapfile)
{
success = false;
printf("Cannot open %s\n", mapfilename.str().c_str());
break;
}
//general info
if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) success = false;
uint32 globalTileID = StaticMapTree::packTileID(65, 65);
pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) success = false;
// Nodes
if (success && fwrite("NODE", 4, 1, mapfile) != 1) success = false;
if (success) success = pTree.writeToFile(mapfile);
// global map spawns (WDT), if any (most instances)
if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) success = false;
for (TileMap::iterator glob=globalRange.first; glob != globalRange.second && success; ++glob)
{
success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
}
fclose(mapfile);
// <====
// write map tile files, similar to ADT files, only with extra BSP tree node info
TileMap &tileEntries = map_iter->second->TileEntries;
TileMap::iterator tile;
for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
{
const ModelSpawn &spawn = map_iter->second->UniqueEntries[tile->second];
if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
continue;
uint32 nSpawns = tileEntries.count(tile->first);
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << iDestDir << "/" << std::setw(3) << map_iter->first << "_";
uint32 x, y;
StaticMapTree::unpackTileID(tile->first, x, y);
tilefilename << std::setw(2) << x << "_" << std::setw(2) << y << ".vmtile";
FILE *tilefile = fopen(tilefilename.str().c_str(), "wb");
// file header
if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) success = false;
// write number of tile spawns
if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) success = false;
// write tile spawns
for (uint32 s=0; s<nSpawns; ++s)
{
if (s)
++tile;
const ModelSpawn &spawn2 = map_iter->second->UniqueEntries[tile->second];
success = success && ModelSpawn::writeToFile(tilefile, spawn2);
// MapTree nodes to update when loading tile:
std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) success = false;
}
fclose(tilefile);
}
// break; //test, extract only first map; TODO: remvoe this line
}
// export objects
std::cout << "\nConverting Model Files" << std::endl;
for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
{
std::cout << "Converting " << *mfile << std::endl;
if (!convertRawFile(*mfile))
{
std::cout << "error converting " << *mfile << std::endl;
success = false;
break;
}
}
//cleanup:
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
{
delete map_iter->second;
}
return success;
}
bool TileAssembler::readMapSpawns()
{
std::string fname = iSrcDir + "/dir_bin";
FILE *dirf = fopen(fname.c_str(), "rb");
if (!dirf)
{
printf("Could not read dir_bin file!\n");
return false;
}
printf("Read coordinate mapping...\n");
uint32 mapID, tileX, tileY, check=0;
G3D::Vector3 v1, v2;
ModelSpawn spawn;
while (!feof(dirf))
{
check = 0;
// read mapID, tileX, tileY, Flags, adtID, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
check += fread(&mapID, sizeof(uint32), 1, dirf);
if (check == 0) // EoF...
break;
check += fread(&tileX, sizeof(uint32), 1, dirf);
check += fread(&tileY, sizeof(uint32), 1, dirf);
if (!ModelSpawn::readFromFile(dirf, spawn))
break;
MapSpawns *current;
MapData::iterator map_iter = mapData.find(mapID);
if (map_iter == mapData.end())
{
printf("spawning Map %d\n", mapID);
mapData[mapID] = current = new MapSpawns();
}
else current = (*map_iter).second;
current->UniqueEntries.insert(pair<uint32, ModelSpawn>(spawn.ID, spawn));
current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
}
bool success = (ferror(dirf) == 0);
fclose(dirf);
return success;
}
bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
{
std::string modelFilename = iSrcDir + "/" + spawn.name;
ModelPosition modelPosition;
modelPosition.iDir = spawn.iRot;
modelPosition.iScale = spawn.iScale;
modelPosition.init();
FILE *rf = fopen(modelFilename.c_str(), "rb");
if (!rf)
{
printf("ERROR: Can't open model file: %s\n", modelFilename.c_str());
return false;
}
AABox modelBound;
bool boundEmpty=true;
char ident[8];
int readOperation = 1;
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++;
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
READ_OR_RETURN(&ident, 8);
CMP_OR_RETURN(ident, "VMAP003");
// we have to read one int. This is needed during the export and we have to skip it here
uint32 tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
uint32 groups, wmoRootId;
char blockId[5];
blockId[4] = 0;
int blocksize;
float *vectorarray = 0;
READ_OR_RETURN(&groups, sizeof(uint32));
READ_OR_RETURN(&wmoRootId, sizeof(uint32));
if (groups != 1) printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
{
fseek(rf, 3*sizeof(uint32) + 6*sizeof(float), SEEK_CUR);
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
READ_OR_RETURN(&blocksize, sizeof(int));
fseek(rf, blocksize, SEEK_CUR);
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
READ_OR_RETURN(&blocksize, sizeof(int));
fseek(rf, blocksize, SEEK_CUR);
// ---- vectors
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nvectors;
READ_OR_RETURN(&nvectors, sizeof(uint32));
if (nvectors >0)
{
vectorarray = new float[nvectors*3];
READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3);
}
else
{
std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
return false;
}
for (uint32 i=0, indexNo=0; indexNo<nvectors; indexNo++, i+=3)
{
Vector3 v = Vector3(vectorarray[i+0], vectorarray[i+1], vectorarray[i+2]);
v = modelPosition.transform(v);
if (boundEmpty)
modelBound = AABox(v, v), boundEmpty=false;
else
modelBound.merge(v);
}
delete[] vectorarray;
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
}
spawn.iBound = modelBound + spawn.iPos;
spawn.flags |= MOD_HAS_BOUND;
fclose(rf);
return true;
}
struct WMOLiquidHeader
{
int xverts, yverts, xtiles, ytiles;
float pos_x;
float pos_y;
float pos_z;
short type;
};
//=================================================================
bool TileAssembler::readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, AABSPTree<SubModel *> *pMainTree)
bool TileAssembler::convertRawFile(const std::string& pModelFilename)
{
bool success = true;
std::string filename = iSrcDir;
if(filename.length() >0)
if (filename.length() >0)
filename.append("/");
filename.append(pModelFilename);
FILE *rf = fopen(filename.c_str(), "rb");
if(!rf)
{
// depending on the extractor version, the data could be located in the root dir
std::string baseModelFilename = pModelFilename.substr((pModelFilename.find_first_of("/")+1),pModelFilename.length());
filename = iSrcDir;
if(filename.length() >0)
filename.append("/");
filename.append(baseModelFilename);
rf = fopen(filename.c_str(), "rb");
}
if(!rf)
if (!rf)
{
printf("ERROR: Can't open model file in form: %s",pModelFilename.c_str());
printf("... or form: %s",filename.c_str() );
@ -377,95 +365,74 @@ namespace VMAP
char ident[8];
int trianglecount =0;
#ifdef _ASSEMBLER_DEBUG
int startgroup = 0; //2;
int endgroup = INT_MAX; //2;
fprintf(::g_df,"-------------------------------------------------\n");
fprintf(::g_df,"%s\n", pModelFilename.c_str());
fprintf(::g_df,"-------------------------------------------------\n");
#else
int startgroup = 0;
int endgroup = INT_MAX;
#endif
int readOperation = 1;
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); return(false); }
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++;
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); return(false); }
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
READ_OR_RETURN(&ident, 8);
if(strcmp(ident, "VMAP001") == 0)
{
// OK, do nothing
}
else if(strcmp(ident, "VMAP002") == 0)
{
// we have to read one int. This is needed during the export and we have to skip it here
int tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(int));
CMP_OR_RETURN(ident, "VMAP003");
}
else
{
// wrong version
fclose(rf);
return(false);
}
G3D::uint32 groups;
// we have to read one int. This is needed during the export and we have to skip it here
uint32 tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
uint32 groups;
uint32 RootWMOID;
char blockId[5];
blockId[4] = 0;
int blocksize;
READ_OR_RETURN(&groups, sizeof(G3D::uint32));
READ_OR_RETURN(&groups, sizeof(uint32));
READ_OR_RETURN(&RootWMOID, sizeof(uint32));
for(int g=0;g<(int)groups;g++)
std::vector<GroupModel> groupsArray;
for (uint32 g=0; g<groups; ++g)
{
// group MUST NOT have more then 65536 indexes !! Array will have a problem with that !! (strange ...)
Array<int> tempIndexArray;
Array<Vector3> tempVertexArray;
std::vector<MeshTriangle> triangles;
std::vector<Vector3> vertexArray;
AABSPTree<Triangle> *gtree = new AABSPTree<Triangle>();
uint32 mogpflags, GroupWMOID;
READ_OR_RETURN(&mogpflags, sizeof(uint32));
READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
// add free gtree at fail
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); delete gtree; return(false); }
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); delete gtree; return(false); }
float bbox1[3], bbox2[3];
READ_OR_RETURN(bbox1, sizeof(float)*3);
READ_OR_RETURN(bbox2, sizeof(float)*3);
G3D::uint32 flags;
READ_OR_RETURN(&flags, sizeof(G3D::uint32));
uint32 liquidflags;
READ_OR_RETURN(&liquidflags, sizeof(uint32));
G3D::uint32 branches;
// will this ever be used? what is it good for anyway??
uint32 branches;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&branches, sizeof(G3D::uint32));
for(int b=0;b<(int)branches; b++)
READ_OR_RETURN(&branches, sizeof(uint32));
for (uint32 b=0; b<branches; ++b)
{
G3D::uint32 indexes;
uint32 indexes;
// indexes for each branch (not used jet)
READ_OR_RETURN(&indexes, sizeof(G3D::uint32));
READ_OR_RETURN(&indexes, sizeof(uint32));
}
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
READ_OR_RETURN(&blocksize, sizeof(int));
unsigned int nindexes;
READ_OR_RETURN(&nindexes, sizeof(G3D::uint32));
if(nindexes >0)
uint32 nindexes;
READ_OR_RETURN(&nindexes, sizeof(uint32));
if (nindexes >0)
{
unsigned short *indexarray = new unsigned short[nindexes*sizeof(unsigned short)];
READ_OR_RETURN(indexarray, nindexes*sizeof(unsigned short));
for(int i=0;i<(int)nindexes; i++)
uint16 *indexarray = new uint16[nindexes];
READ_OR_RETURN(indexarray, nindexes*sizeof(uint16));
for (uint32 i=0; i<nindexes; i+=3)
{
unsigned short val = indexarray[i];
tempIndexArray.append(val);
triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2]));
}
delete[] indexarray;
}
@ -474,128 +441,56 @@ namespace VMAP
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
unsigned int nvectors;
READ_OR_RETURN(&nvectors, sizeof(int));
uint32 nvectors;
READ_OR_RETURN(&nvectors, sizeof(uint32));
float *vectorarray = 0;
// add vectorarray free
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); delete gtree; delete[] vectorarray; return(false); }
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); delete gtree; delete[] vectorarray; return(false); }
if(nvectors >0)
if (nvectors >0)
{
vectorarray = new float[nvectors*sizeof(float)*3];
float *vectorarray = new float[nvectors*3];
READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3);
}
// ----- liquit
if(flags & 1)
for (uint32 i=0; i<nvectors; ++i)
{
// we have liquit -> not handled yet ... skip
vertexArray.push_back( Vector3(vectorarray + 3*i) );
}
delete[] vectorarray;
}
// ----- liquid
WmoLiquid *liquid = 0;
if (liquidflags& 1)
{
WMOLiquidHeader hlq;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "LIQU");
READ_OR_RETURN(&blocksize, sizeof(int));
fseek(rf, blocksize, SEEK_CUR);
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type);
uint32 size = hlq.xverts*hlq.yverts;
READ_OR_RETURN(liquid->GetHeightStorage(), size*sizeof(float));
size = hlq.xtiles*hlq.ytiles;
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
}
for(unsigned int i=0, indexNo=0; indexNo<nvectors; indexNo++)
{
Vector3 v = Vector3(vectorarray[i+2], vectorarray[i+1], vectorarray[i+0]);
i+=3;
v = pModelPosition.transform(v);
float swapy = v.y;
v.y = v.x;
v.x = swapy;
tempVertexArray.append(v);
}
// ---- calculate triangles
int rest = nindexes%3;
if(rest != 0)
{
nindexes -= rest;
}
for(unsigned int i=0;i<(nindexes);)
{
Triangle t = Triangle(tempVertexArray[tempIndexArray[i+2]], tempVertexArray[tempIndexArray[i+1]], tempVertexArray[tempIndexArray[i+0]] );
i+=3;
++trianglecount;
if(g>= startgroup && g <= endgroup)
{
gtree->insert(t);
}
}
groupsArray.push_back(GroupModel(mogpflags, GroupWMOID, AABox(Vector3(bbox1), Vector3(bbox2))));
groupsArray.back().setMeshData(vertexArray, triangles);
groupsArray.back().setLiquidData(liquid);
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
if(vectorarray != 0)
{
delete[] vectorarray;
}
if(gtree->size() >0)
{
gtree->balance();
SubModel *sm = new SubModel(gtree);
#ifdef _ASSEMBLER_DEBUG
if(::g_df) fprintf(::g_df,"group trianglies: %d, Tris: %d, Nodes: %d, gtree.triangles: %d\n", g, sm->getNTriangles(), sm->getNNodes(), gtree->memberTable.size());
if(sm->getNTriangles() != gtree->memberTable.size())
{
if(::g_df) fprintf(::g_df,"ERROR !!!! group trianglies: %d, Tris: %d, Nodes: %d, gtree.triangles: %d\n", g, sm->getNTriangles(), sm->getNNodes(), gtree->memberTable.size());
}
#endif
sm->setBasePosition(pModelPosition.iPos);
pMainTree->insert(sm);
}
delete gtree;
}
fclose(rf);
return true;
}
//=================================================================
bool TileAssembler::fillModelIntoTree(AABSPTree<SubModel *> *pMainTree, const Vector3& pBasePos, std::string& pPos, std::string& pModelFilename)
// write WorldModel
WorldModel model;
model.setRootWmoID(RootWMOID);
if (groupsArray.size())
{
ModelPosition modelPosition;
getModelPosition(pPos, modelPosition);
// all should be relative to object base position
modelPosition.moveToBasePos(pBasePos);
modelPosition.init();
return readRawFile(pModelFilename, modelPosition, pMainTree);
model.setGroupModels(groupsArray);
success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
}
//=================================================================
void TileAssembler::getModelPosition(std::string& pPosString, ModelPosition& pModelPosition)
{
float vposarray[3];
float vdirarray[3];
float scale;
size_t spos = pPosString.find_first_of('#');
std::string stripedPosString = pPosString.substr(spos+1,pPosString.length());
sscanf(stripedPosString.c_str(), "%f,%f,%f_%f,%f,%f_%f",
&vposarray[0],&vposarray[1],&vposarray[2],
&vdirarray[0],&vdirarray[1],&vdirarray[2],
&scale);
pModelPosition.iPos = Vector3(vposarray[0], vposarray[1], vposarray[2]);
pModelPosition.iDir = Vector3(vdirarray[0], vdirarray[1], vdirarray[2]);
pModelPosition.iScale = scale;
//std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
return success;
}
//==========================================
} // VMAP
}

View file

@ -19,14 +19,11 @@
#ifndef _TILEASSEMBLER_H_
#define _TILEASSEMBLER_H_
// load our modified version first !!
#include "AABSPTree.h"
#include <G3D/Vector3.h>
#include <G3D/Matrix3.h>
#include <map>
#include "CoordModelMapping.h"
#include "SubModel.h"
#include "ModelContainer.h"
#include "ModelInstance.h"
namespace VMAP
{
@ -39,55 +36,54 @@ namespace VMAP
class ModelPosition
{
private:
G3D::Matrix3 ixMatrix;
G3D::Matrix3 iyMatrix;
G3D::Matrix3 izMatrix;
G3D::Matrix3 iRotation;
public:
G3D::Vector3 iPos;
G3D::Vector3 iDir;
float iScale;
void init()
{
// Swap x and y the raw data uses the axis differently
ixMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitY(),-(G3D::pi()*iDir.x/180.0));
iyMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitX(),-(G3D::pi()*iDir.y/180.0));
izMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitZ(),-(G3D::pi()*iDir.z/180.0));
iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iDir.y/180.f, G3D::pi()*iDir.x/180.f, G3D::pi()*iDir.z/180.f);
}
G3D::Vector3 transform(const G3D::Vector3& pIn) const;
void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
};
typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
typedef std::multimap<uint32, uint32> TileMap;
struct MapSpawns
{
UniqueEntryMap UniqueEntries;
TileMap TileEntries;
};
typedef std::map<uint32, MapSpawns*> MapData;
//===============================================
class TileAssembler
{
private:
CoordModelMapping *iCoordModelMapping;
std::string iDestDir;
std::string iSrcDir;
bool (*iFilterMethod)(char *pName);
G3D::Table<std::string, unsigned int > iUniqueNameIds;
unsigned int iCurrentUniqueNameId;
MapData mapData;
public:
TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
virtual ~TileAssembler();
bool fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, G3D::Array<ModelContainer*>& pMC);
ModelContainer* processNames(const G3D::Array<std::string>& pPosFileNames, const char* pDestFileName);
bool convertWorld2();
bool readMapSpawns();
bool calculateTransformedBound(ModelSpawn &spawn);
void init();
bool convertWorld();
bool fillModelIntoTree(G3D::AABSPTree<SubModel *> *pMainTree, const G3D::Vector3& pBasePos, std::string& pPosFilename, std::string& pModelFilename);
void getModelPosition(std::string& pPosString, ModelPosition& pModelPosition);
bool readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, G3D::AABSPTree<SubModel *> *pMainTree);
void addWorldAreaMapId(unsigned int pMapId) { iCoordModelMapping->addWorldAreaMap(pMapId); }
bool convertRawFile(const std::string& pModelFilename);
void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
std::string getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName);
unsigned int getUniqueNameId(const std::string pName);
};
//===============================================
} // VMAP
#endif /*_TILEASSEMBLER_H_*/

View file

@ -1,37 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "TreeNode.h"
using namespace G3D;
namespace VMAP
{
TreeNode const* TreeNode::getChild(TreeNode const* pValueArray,int pNo) const
{
if(iChilds[pNo] != -1)
return(&pValueArray[iChilds[pNo]]);
else
return(NULL);
}
//=================================================================
//=================================================================
//=================================================================
}

View file

@ -1,223 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _TREENODE_H
#define _TREENODE_H
#include "ShortVector.h"
#include "ShortBox.h"
#include "NodeValueAccess.h"
#include "VMapTools.h"
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
namespace VMAP
{
/**
This Class is mainly taken from G3D/AABSPTree.h and modified to match our data structure.
It is the node within our static BSP-Trees.
It does not use pointers but indexes to access the values and other nodes.
*/
//=====================================================
class TreeNode
{
private:
/** Location along the specified axis */
float iSplitLocation;
// Offest or the clients
int iChilds[2];
//Position within the TriangleBox array
unsigned int iStartPosition;
G3D::Vector3::Axis iSplitAxis;
G3D::AABox iBounds;
unsigned short iNumberOfValues;
public:
TreeNode() {}
TreeNode(unsigned short pNValues, unsigned int pStartPosition)
{
iChilds[0] = -1;
iChilds[1] = -1;
iStartPosition = pStartPosition;
iNumberOfValues = pNValues;
}
bool hasChilds() const { return(iChilds[0] >= 0 || iChilds[1] >= 0); }
TreeNode const* getChild(TreeNode const* pValueArray, int pNo) const;
// pChildNo = 0 or 1
inline void setChildPos(int pChildNo, int pChildPosInTreeNodeArray) { iChilds[pChildNo] = pChildPosInTreeNodeArray; }
inline G3D::Vector3::Axis getSplitAxis() const { return(iSplitAxis); }
inline void setSplitAxis(G3D::Vector3::Axis a) { iSplitAxis = a; }
inline void setSplitLocation(float l) { iSplitLocation = l; }
inline void setBounds(const G3D::AABox& pBox) { iBounds = pBox; }
inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBounds.set(lo,hi); }
inline void getBounds(G3D::AABox& pBox) const { pBox.set(iBounds.low(),iBounds.high()); }
inline float getSplitLocation() const { return(iSplitLocation); }
inline unsigned short getNValues() const { return (iNumberOfValues); }
inline unsigned int getStartPosition() const { return(iStartPosition); }
inline bool operator==(const TreeNode& n) const
{
return ((iSplitLocation == n.iSplitLocation) &&
(iChilds[0] == n.iChilds[0]) && (iChilds[1] == n.iChilds[1]) &&
(iStartPosition == n.iStartPosition) &&
(iSplitAxis == n.iSplitAxis) &&
(iBounds == n.iBounds) &&
(iNumberOfValues == n.iNumberOfValues));
}
inline bool operator!=(const TreeNode& n) const
{
return !((iSplitLocation == n.iSplitLocation) &&
(iChilds[0] == n.iChilds[0]) && (iChilds[1] == n.iChilds[1]) &&
(iStartPosition == n.iStartPosition) &&
(iSplitAxis == n.iSplitAxis) &&
(iBounds == n.iBounds) &&
(iNumberOfValues == n.iNumberOfValues));
}
/** Returns true if the ray intersects this node */
bool intersects(const G3D::Ray& ray, float distance) const {
// See if the ray will ever hit this node or its children
G3D::Vector3 location;
bool alreadyInsideBounds = false;
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin(), ray.direction(), iBounds, location, alreadyInsideBounds);
bool canHitThisNode = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin()).squaredLength() < (distance*distance))));
return canHitThisNode;
}
template<typename RayCallback, typename TNode, typename TValue>
void intersectRay(
const G3D::Ray& ray,
RayCallback& intersectCallback,
float& distance,
const NodeValueAccess<TNode, TValue>& pNodeValueAccess,
bool pStopAtFirstHit,
bool intersectCallbackIsFast) const {
float enterDistance = distance;
if (! intersects(ray, distance)) {
// The ray doesn't hit this node, so it can't hit the children of the node.
return;
}
// Test for intersection against every object at this node.
for (unsigned int v = iStartPosition; v < (iNumberOfValues+iStartPosition); ++v) {
const TValue& nodeValue = pNodeValueAccess.getValue(v);
bool canHitThisObject = true;
if (! intersectCallbackIsFast) {
// See if
G3D::Vector3 location;
const G3D::AABox& bounds = nodeValue.getAABoxBounds();
bool alreadyInsideBounds = false;
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin(), ray.direction(), bounds, location, alreadyInsideBounds);
canHitThisObject = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin()).squaredLength() < (distance*distance))));
}
if (canHitThisObject) {
// It is possible that this ray hits this object. Look for the intersection using the
// callback.
intersectCallback(ray, &nodeValue, pStopAtFirstHit, distance);
}
if(pStopAtFirstHit && distance < enterDistance)
return;
}
// There are three cases to consider next:
//
// 1. the ray can start on one side of the splitting plane and never enter the other,
// 2. the ray can start on one side and enter the other, and
// 3. the ray can travel exactly down the splitting plane
enum {NONE = -1};
int firstChild = NONE;
int secondChild = NONE;
if (ray.origin()[iSplitAxis] < iSplitLocation) {
// The ray starts on the small side
firstChild = 0;
if (ray.direction()[iSplitAxis] > 0) {
// The ray will eventually reach the other side
secondChild = 1;
}
} else if (ray.origin()[iSplitAxis] > iSplitLocation) {
// The ray starts on the large side
firstChild = 1;
if (ray.direction()[iSplitAxis] < 0) {
secondChild = 0;
}
} else {
// The ray starts on the splitting plane
if (ray.direction()[iSplitAxis] < 0) {
// ...and goes to the small side
firstChild = 0;
} else if (ray.direction()[iSplitAxis] > 0) {
// ...and goes to the large side
firstChild = 1;
}
}
// Test on the side closer to the ray origin.
if ((firstChild != NONE) && iChilds[firstChild]>0) {
getChild(pNodeValueAccess.getNodePtr() , firstChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast);
if(pStopAtFirstHit && distance < enterDistance)
return;
}
if (ray.direction()[iSplitAxis] != 0) {
// See if there was an intersection before hitting the splitting plane.
// If so, there is no need to look on the far side and recursion terminates.
float distanceToSplittingPlane = (iSplitLocation - ray.origin()[iSplitAxis]) / ray.direction()[iSplitAxis];
if (distanceToSplittingPlane > distance) {
// We aren't going to hit anything else before hitting the splitting plane,
// so don't bother looking on the far side of the splitting plane at the other
// child.
return;
}
}
// Test on the side farther from the ray origin.
if ((secondChild != NONE) && iChilds[secondChild]>0) {
getChild(pNodeValueAccess.getNodePtr() , secondChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast);
}
}
};
}
#endif

View file

@ -20,11 +20,13 @@
#define _VMAPDEFINITIONS_H
#include <cstring>
#define LIQUID_TILE_SIZE (533.333f / 128.f)
namespace VMAP
{
//=====================================
#define MAX_CAN_FALL_DISTANCE 10.0f
const char VMAP_MAGIC[] = "VMAP_2.0";
const char VMAP_MAGIC[] = "VMAP_3.0";
class VMapDefinitions
{
@ -33,5 +35,21 @@ namespace VMAP
};
//======================================
// defined in TileAssembler.cpp currently...
bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len);
}
#ifndef NO_CORE_FUNCS
#include "Errors.h"
#include "Log.h"
#define ERROR_LOG(...) sLog.outError(__VA_ARGS__);
#else
#include <assert.h>
#define ASSERT(x) assert(x)
#define DEBUG_LOG(...) do{ printf(__VA_ARGS__); printf("\n"); } while(0)
#define DETAIL_LOG(...) do{ printf(__VA_ARGS__); printf("\n"); } while(0)
#define ERROR_LOG(...) do{ printf("ERROR:"); printf(__VA_ARGS__); printf("\n"); } while(0)
#endif
#endif // _VMAPDEFINITIONS_H

View file

@ -18,15 +18,41 @@
#include <sys/types.h>
#include "VMapFactory.h"
#include "VMapManager.h"
#include "VMapManager2.h"
using namespace G3D;
namespace VMAP
{
extern void chompAndTrim(std::string& str);
void chompAndTrim(std::string& str)
{
while(str.length() >0)
{
char lc = str[str.length()-1];
if(lc == '\r' || lc == '\n' || lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(0,str.length()-1);
}
else
{
break;
}
}
while(str.length() >0)
{
char lc = str[0];
if(lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(1,str.length()-1);
}
else
{
break;
}
}
}
VMapManager *gVMapManager = 0;
IVMapManager *gVMapManager = 0;
Table<unsigned int , bool>* iIgnoreSpellIds=0;
//===============================================
@ -88,7 +114,7 @@ namespace VMAP
IVMapManager* VMapFactory::createOrGetVMapManager()
{
if(gVMapManager == 0)
gVMapManager= new VMapManager(); // should be taken from config ... Please change if you like :-)
gVMapManager= new VMapManager2(); // should be taken from config ... Please change if you like :-)
return gVMapManager;
}

View file

@ -1,774 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "VMapManager.h"
#include "VMapDefinitions.h"
using namespace G3D;
namespace VMAP
{
//=========================================================
VMapManager::VMapManager()
{
#ifdef _VMAP_LOG_DEBUG
iCommandLogger.setFileName("vmapcmd.log");
iCommandLogger.setResetFile();
#endif
}
//=========================================================
VMapManager::~VMapManager(void)
{
Array<unsigned int > keyArray = iInstanceMapTrees.getKeys();
for(int i=0;i<keyArray.size(); ++i)
{
delete iInstanceMapTrees.get(keyArray[i]);
iInstanceMapTrees.remove(keyArray[i]);
}
}
//=========================================================
Vector3 VMapManager::convertPositionToInternalRep(float x, float y, float z) const
{
float pos[3];
pos[0] = y;
pos[1] = z;
pos[2] = x;
double full = 64.0*533.33333333;
double mid = full/2.0;
pos[0] = full- (pos[0] + mid);
pos[2] = full- (pos[2] + mid);
return(Vector3(pos));
}
//=========================================================
Vector3 VMapManager::convertPositionToMangosRep(float x, float y, float z) const
{
float pos[3];
pos[0] = z;
pos[1] = x;
pos[2] = y;
double full = 64.0*533.33333333;
double mid = full/2.0;
pos[0] = -((mid+pos[0])-full);
pos[1] = -((mid+pos[1])-full);
return(Vector3(pos));
}
//=========================================================
std::string VMapManager::getDirFileName(unsigned int pMapId, int x, int y) const
{
char name[FILENAMEBUFFER_SIZE];
sprintf(name, "%03u_%d_%d%s",pMapId, x, y, DIR_FILENAME_EXTENSION);
return(std::string(name));
}
//=========================================================
std::string VMapManager::getDirFileName(unsigned int pMapId) const
{
char name[FILENAMEBUFFER_SIZE];
sprintf(name, "%03d%s",pMapId, DIR_FILENAME_EXTENSION);
return(std::string(name));
}
//=========================================================
// remote last return or LF
void chomp(std::string& str)
{
while(str.length() >0)
{
char lc = str[str.length()-1];
if(lc == '\r' || lc == '\n')
{
str = str.substr(0,str.length()-1);
}
else
{
break;
}
}
}
//=========================================================
void chompAndTrim(std::string& str)
{
while(str.length() >0)
{
char lc = str[str.length()-1];
if(lc == '\r' || lc == '\n' || lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(0,str.length()-1);
}
else
{
break;
}
}
while(str.length() >0)
{
char lc = str[0];
if(lc == ' ' || lc == '"' || lc == '\'')
{
str = str.substr(1,str.length()-1);
}
else
{
break;
}
}
}
//=========================================================
// result false, if no more id are found
bool getNextMapId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId)
{
bool result = false;
unsigned int i;
for(i=pStartPos;i<pString.size(); ++i)
{
if(pString[i] == ',')
{
break;
}
}
if(i>pStartPos)
{
std::string idString = pString.substr(pStartPos, i-pStartPos);
pStartPos = i+1;
chompAndTrim(idString);
pId = atoi(idString.c_str());
result = true;
}
return(result);
}
//=========================================================
/**
Block maps from being used.
parameter: String of map ids. Delimiter = ","
e.g.: "0,1,590"
*/
void VMapManager::preventMapsFromBeingUsed(const char* pMapIdString)
{
if(pMapIdString != NULL)
{
unsigned int pos =0;
unsigned int id;
std::string confString(pMapIdString);
chompAndTrim(confString);
while(getNextMapId(confString, pos, id))
{
iIgnoreMapIds.set(id, true);
}
}
}
//=========================================================
int VMapManager::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
if(isMapLoadingEnabled() && !iIgnoreMapIds.containsKey(pMapId))
{
bool loaded = _loadMap(pBasePath, pMapId, x, y, false);
if(!loaded)
{
// if we can't load the map it might be splitted into tiles. Try that one and store the result
loaded = _loadMap(pBasePath, pMapId, x, y, true);
if(loaded)
{
iMapsSplitIntoTiles.set(pMapId, true);
}
}
if(loaded)
{
result = VMAP_LOAD_RESULT_OK;
// just for debugging
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillLoadTileCmd(x, y, pMapId);
iCommandLogger.appendCmd(c);
#endif
}
else
{
result = VMAP_LOAD_RESULT_ERROR;
}
}
return result;
}
//=========================================================
// load one tile (internal use only)
bool VMapManager::_loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad)
{
bool result = false;
std::string dirFileName;
if(pForceTileLoad || iMapsSplitIntoTiles.containsKey(pMapId))
{
dirFileName = getDirFileName(pMapId,x,y);
}
else
{
dirFileName = getDirFileName(pMapId);
}
MapTree* instanceTree;
if(!iInstanceMapTrees.containsKey(pMapId))
{
instanceTree = new MapTree(pBasePath);
iInstanceMapTrees.set(pMapId, instanceTree);
}
else
instanceTree = iInstanceMapTrees.get(pMapId);
unsigned int mapTileIdent = MAP_TILE_IDENT(x,y);
result = instanceTree->loadMap(dirFileName, mapTileIdent);
if(!result) // remove on fail
{
if(instanceTree->size() == 0)
{
iInstanceMapTrees.remove(pMapId);
delete instanceTree;
}
}
return(result);
}
//=========================================================
bool VMapManager::_existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad)
{
bool result = false;
std::string dirFileName;
if(pForceTileLoad || iMapsSplitIntoTiles.containsKey(pMapId))
{
dirFileName = getDirFileName(pMapId,x,y);
}
else
{
dirFileName = getDirFileName(pMapId);
}
std::string fb = pBasePath + dirFileName;
FILE* df = fopen(fb.c_str(), "rb");
if(df)
{
char lineBuffer[FILENAMEBUFFER_SIZE];
if (fgets(lineBuffer, FILENAMEBUFFER_SIZE-1, df) != 0)
{
std::string name = std::string(lineBuffer);
chomp(name);
if(name.length() >1)
{
std::string fb2 = pBasePath + name;
FILE* df2 = fopen(fb2.c_str(), "rb");
if(df2)
{
char magic[8];
fread(magic,1,8,df2);
if(!strncmp(VMAP_MAGIC,magic,8))
result = true;
fclose(df2);
}
}
}
fclose(df);
}
return result;
}
//=========================================================
bool VMapManager::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
std::string basePath = std::string(pBasePath);
if(basePath.length() > 0 && (basePath[basePath.length()-1] != '/' || basePath[basePath.length()-1] != '\\'))
{
basePath.append("/");
}
bool found = _existsMap(basePath, pMapId, x, y, false);
if(!found)
{
// if we can't load the map it might be splitted into tiles. Try that one and store the result
found = _existsMap(basePath, pMapId, x, y, true);
if(found)
{
iMapsSplitIntoTiles.set(pMapId, true);
}
}
return found;
}
//=========================================================
void VMapManager::unloadMap(unsigned int pMapId, int x, int y)
{
_unloadMap(pMapId, x, y);
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillUnloadTileCmd(pMapId, x,y);
iCommandLogger.appendCmd(c);
#endif
}
//=========================================================
void VMapManager::_unloadMap(unsigned int pMapId, int x, int y)
{
if(iInstanceMapTrees.containsKey(pMapId))
{
MapTree* instanceTree = iInstanceMapTrees.get(pMapId);
std::string dirFileName;
if(iMapsSplitIntoTiles.containsKey(pMapId))
{
dirFileName = getDirFileName(pMapId,x,y);
}
else
{
dirFileName = getDirFileName(pMapId);
}
unsigned int mapTileIdent = MAP_TILE_IDENT(x,y);
instanceTree->unloadMap(dirFileName, mapTileIdent);
if(instanceTree->size() == 0)
{
iInstanceMapTrees.remove(pMapId);
delete instanceTree;
}
}
}
//=========================================================
void VMapManager::unloadMap(unsigned int pMapId)
{
if(iInstanceMapTrees.containsKey(pMapId))
{
MapTree* instanceTree = iInstanceMapTrees.get(pMapId);
std::string dirFileName = getDirFileName(pMapId);
instanceTree->unloadMap(dirFileName, 0, true);
if(instanceTree->size() == 0)
{
iInstanceMapTrees.remove(pMapId);
delete instanceTree;
}
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillUnloadTileCmd(pMapId);
iCommandLogger.appendCmd(c);
#endif
}
}
//==========================================================
bool VMapManager::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2)
{
bool result = true;
if(isLineOfSightCalcEnabled() && iInstanceMapTrees.containsKey(pMapId))
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
if(pos1 != pos2)
{
MapTree* mapTree = iInstanceMapTrees.get(pMapId);
result = mapTree->isInLineOfSight(pos1, pos2);
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
// save the orig vectors
c.fillTestVisCmd(pMapId,Vector3(x1,y1,z1),Vector3(x2,y2,z2),result);
iCommandLogger.appendCmd(c);
#endif
}
}
return(result);
}
//=========================================================
/**
get the hit position and return true if we hit something
otherwise the result pos will be the dest pos
*/
bool VMapManager::getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist)
{
bool result = false;
rx=x2;
ry=y2;
rz=z2;
if(isLineOfSightCalcEnabled())
{
if(iInstanceMapTrees.containsKey(pMapId))
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
Vector3 resultPos;
MapTree* mapTree = iInstanceMapTrees.get(pMapId);
result = mapTree->getObjectHitPos(pos1, pos2, resultPos, pModifyDist);
resultPos = convertPositionToMangosRep(resultPos.x,resultPos.y,resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillTestObjectHitCmd(pMapId, pos1, pos2, resultPos, result);
iCommandLogger.appendCmd(c);
#endif
}
}
return result;
}
//=========================================================
/**
get height or INVALID_HEIGHT if to height was calculated
*/
//int gGetHeightCounter = 0;
float VMapManager::getHeight(unsigned int pMapId, float x, float y, float z)
{
float height = VMAP_INVALID_HEIGHT_VALUE; //no height
if(isHeightCalcEnabled() && iInstanceMapTrees.containsKey(pMapId))
{
Vector3 pos = convertPositionToInternalRep(x,y,z);
MapTree* mapTree = iInstanceMapTrees.get(pMapId);
height = mapTree->getHeight(pos);
if(!(height < inf()))
{
height = VMAP_INVALID_HEIGHT_VALUE; //no height
}
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillTestHeightCmd(pMapId,Vector3(x,y,z),height);
iCommandLogger.appendCmd(c);
#endif
}
return(height);
}
//=========================================================
/**
used for debugging
*/
bool VMapManager::processCommand(char *pCommand)
{
bool result = false;
std::string cmd = std::string(pCommand);
if(cmd == "startlog")
{
#ifdef _VMAP_LOG_DEBUG
iCommandLogger.enableWriting(true);
#endif
result = true;
}
else if(cmd == "stoplog")
{
#ifdef _VMAP_LOG_DEBUG
iCommandLogger.appendCmd(Command()); // Write stop command
iCommandLogger.enableWriting(false);
#endif
result = true;
}
else if(cmd.find_first_of("pos ") == 0)
{
float x,y,z;
sscanf(pCommand, "pos %f,%f,%f",&x,&y,&z);
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillSetPosCmd(convertPositionToInternalRep(x,y,z));
iCommandLogger.appendCmd(c);
iCommandLogger.enableWriting(false);
#endif
result = true;
}
return result;
}
//=========================================================
//=========================================================
//=========================================================
MapTree::MapTree(const char* pBaseDir)
{
iBasePath = std::string(pBaseDir);
if(iBasePath.length() > 0 && (iBasePath[iBasePath.length()-1] != '/' || iBasePath[iBasePath.length()-1] != '\\'))
{
iBasePath.append("/");
}
iTree = new AABSPTree<ModelContainer *>();
}
//=========================================================
MapTree::~MapTree()
{
Array<ModelContainer *> mcArray;
iTree->getMembers(mcArray);
int no = mcArray.size();
while(no >0)
{
--no;
delete mcArray[no];
}
delete iTree;
}
//=========================================================
// just for visual debugging with an external debug class
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
extern Vector3 p1,p2,p3,p4,p5,p6,p7;
extern Array<AABox>gBoxArray;
extern int gCount1, gCount2, gCount3, gCount4;
extern bool myfound;
#endif
#endif
//=========================================================
/**
return dist to hit or inf() if no hit
*/
float MapTree::getIntersectionTime(const Ray& pRay, float pMaxDist, bool pStopAtFirstHit)
{
float firstDistance = inf();
IntersectionCallBack<ModelContainer> intersectionCallBack;
float t = pMaxDist;
iTree->intersectRay(pRay, intersectionCallBack, t, pStopAtFirstHit, false);
#ifdef _DEBUG_VMAPS
{
if(t < pMaxDist)
{
myfound = true;
p4 = pRay.origin + pRay.direction*t;
}
}
#endif
if(t > 0 && t < inf() && pMaxDist > t)
{
firstDistance = t;
}
return firstDistance;
}
//=========================================================
bool MapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2)
{
bool result = true;
float maxDist = abs((pos2 - pos1).magnitude());
// direction with length of 1
Ray ray = Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
float resultDist = getIntersectionTime(ray, maxDist, true);
if(resultDist < maxDist)
{
result = false;
}
return result;
}
//=========================================================
/**
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
bool MapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist)
{
bool result;
float maxDist = abs((pPos2 - pPos1).magnitude());
Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
Ray ray = Ray::fromOriginAndDirection(pPos1, dir);
float dist = getIntersectionTime(ray, maxDist, false);
if(dist < maxDist)
{
pResultHitPos = pPos1 + dir * dist;
if(pModifyDist < 0)
{
if(abs((pResultHitPos - pPos1).magnitude()) > -pModifyDist)
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
else
{
pResultHitPos = pPos1;
}
}
else
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
result = true;
}
else
{
pResultHitPos = pPos2;
result = false;
}
return result;
}
//=========================================================
float MapTree::getHeight(const Vector3& pPos)
{
float height = inf();
Vector3 dir = Vector3(0,-1,0);
Ray ray = Ray::fromOriginAndDirection(pPos, dir); // direction with length of 1
float maxDist = VMapDefinitions::getMaxCanFallDistance();
float dist = getIntersectionTime(ray, maxDist, false);
if(dist < inf())
{
height = (pPos + dir * dist).y;
}
return(height);
}
//=========================================================
bool MapTree::PrepareTree()
{
iTree->balance();
return true;
}
bool MapTree::loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent)
{
bool result = true;
if(!hasDirFile(pDirFileName))
{
FilesInDir filesInDir;
result = false;
std::string fb = iBasePath + pDirFileName;
FILE* df = fopen(fb.c_str(), "rb");
if(df)
{
char lineBuffer[FILENAMEBUFFER_SIZE];
result = true;
bool newModelLoaded = false;
while(result && (fgets(lineBuffer, FILENAMEBUFFER_SIZE-1, df) != 0))
{
std::string name = std::string(lineBuffer);
chomp(name);
if(name.length() >1)
{
filesInDir.append(name);
ManagedModelContainer *mc;
if(!isAlreadyLoaded(name))
{
std::string fname = iBasePath;
fname.append(name);
mc = new ManagedModelContainer();
result = mc->readFile(fname.c_str());
if(result)
{
addModelContainer(name, mc);
newModelLoaded = true;
}
}
else
{
mc = getModelContainer(name);
}
mc->incRefCount();
}
}
if(result && newModelLoaded)
{
iTree->balance();
}
if(result && ferror(df) != 0)
{
result = false;
}
fclose(df);
if(result)
{
filesInDir.incRefCount();
addDirFile(pDirFileName, filesInDir);
setLoadedMapTile(pMapTileIdent);
}
}
}
else
{
// Already loaded, so just inc. the ref count if mapTileIdent is new
if(!containsLoadedMapTile(pMapTileIdent))
{
setLoadedMapTile(pMapTileIdent);
FilesInDir& filesInDir = getDirFiles(pDirFileName);
filesInDir.incRefCount();
}
}
return (result);
}
//=========================================================
void MapTree::unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce)
{
if(hasDirFile(dirFileName) && (pForce || containsLoadedMapTile(pMapTileIdent)))
{
if(containsLoadedMapTile(pMapTileIdent))
removeLoadedMapTile(pMapTileIdent);
FilesInDir& filesInDir = getDirFiles(dirFileName);
filesInDir.decRefCount();
if(filesInDir.getRefCount() <= 0)
{
Array<std::string> fileNames = filesInDir.getFiles();
bool treeChanged = false;
for(int i=0; i<fileNames.size(); ++i)
{
std::string name = fileNames[i];
ManagedModelContainer* mc = getModelContainer(name);
mc->decRefCount();
if(mc->getRefCount() <= 0)
{
iLoadedModelContainer.remove(name);
iTree->remove(mc);
delete mc;
treeChanged = true;
}
}
iLoadedDirFiles.remove(dirFileName);
if(treeChanged)
{
iTree->balance();
}
}
}
}
//=========================================================
//=========================================================
void MapTree::addModelContainer(const std::string& pName, ManagedModelContainer *pMc)
{
iLoadedModelContainer.set(pName, pMc);
iTree->insert(pMc);
}
//=========================================================
//=========================================================
//=========================================================
}

View file

@ -1,173 +0,0 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPMANAGER_H
#define _VMAPMANAGER_H
// load our modified version first !!
#include "AABSPTree.h"
#include "ManagedModelContainer.h"
#include "IVMapManager.h"
#ifdef _VMAP_LOG_DEBUG
#include "DebugCmdLogger.h"
#endif
#include <G3D/Table.h>
//===========================================================
#define DIR_FILENAME_EXTENSION ".vmdir"
#define FILENAMEBUFFER_SIZE 500
/**
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
Each global map or instance has its own dynamic BSP-Tree.
The loaded ModelContainers are included in one of these BSP-Trees.
Additionally a table to match map ids and map names is used.
*/
// Create a value describing the map tile
#define MAP_TILE_IDENT(x,y) ((x<<8) + y)
//===========================================================
namespace VMAP
{
//===========================================================
class FilesInDir
{
private:
int iRefCount;
G3D::Array<std::string> iFiles;
public:
FilesInDir() { iRefCount = 0; }
void append(const std::string& pName) { iFiles.append(pName); }
void incRefCount() { ++iRefCount; }
void decRefCount() { if(iRefCount > 0) --iRefCount; }
int getRefCount() { return iRefCount; }
const G3D::Array<std::string>& getFiles() const { return iFiles; }
};
//===========================================================
//===========================================================
//===========================================================
//===========================================================
class MapTree
{
private:
G3D::AABSPTree<ModelContainer *> *iTree;
// Key: filename, value ModelContainer
G3D::Table<std::string, ManagedModelContainer *> iLoadedModelContainer;
// Key: dir file name, value FilesInDir
G3D::Table<std::string, FilesInDir> iLoadedDirFiles;
// Store all the map tile idents that are loaded for that map
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
G3D::Table<unsigned int, bool> iLoadedMapTiles;
std::string iBasePath;
private:
float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit);
bool isAlreadyLoaded(const std::string& pName) const { return(iLoadedModelContainer.containsKey(pName)); }
void setLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.set(pTileIdent, true); }
void removeLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.remove(pTileIdent); }
bool hasLoadedMapTiles() const { return iLoadedMapTiles.size() > 0; }
bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
public:
ManagedModelContainer *getModelContainer(const std::string& pName) { return(iLoadedModelContainer.get(pName)); }
bool hasDirFile(const std::string& pDirName) const { return(iLoadedDirFiles.containsKey(pDirName)); }
FilesInDir& getDirFiles(const std::string& pDirName) const { return(iLoadedDirFiles.get(pDirName)); }
public:
MapTree(const char *pBasePath);
~MapTree();
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2);
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist);
float getHeight(const G3D::Vector3& pPos);
bool PrepareTree();
bool loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent);
void addModelContainer(const std::string& pName, ManagedModelContainer *pMc);
void unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce=false);
void getModelContainer(G3D::Array<ModelContainer *>& pArray ) { iTree->getMembers(pArray); }
void addDirFile(const std::string& pDirName, const FilesInDir& pFilesInDir) { iLoadedDirFiles.set(pDirName, pFilesInDir); }
size_t size() { return(iTree->size()); }
};
//===========================================================
class MapIdNames
{
public:
std::string iDirName;
std::string iMapGroupName;
};
//===========================================================
class VMapManager : public IVMapManager
{
private:
// Tree to check collision
G3D::Table<unsigned int , MapTree *> iInstanceMapTrees;
G3D::Table<unsigned int , bool> iMapsSplitIntoTiles;
G3D::Table<unsigned int , bool> iIgnoreMapIds;
#ifdef _VMAP_LOG_DEBUG
CommandFileRW iCommandLogger;
#endif
private:
bool _loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad=false);
void _unloadMap(unsigned int pMapId, int x, int y);
bool _existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad);
public:
// public for debug
G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
G3D::Vector3 convertPositionToMangosRep(float x, float y, float z) const;
std::string getDirFileName(unsigned int pMapId) const;
std::string getDirFileName(unsigned int pMapId, int x, int y) const;
MapTree* getInstanceMapTree(int pMapId) { return(iInstanceMapTrees.get(pMapId)); }
public:
VMapManager();
~VMapManager(void);
int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y);
bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId);
bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ;
/**
fill the hit pos and return true, if an object was hit
*/
bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist);
float getHeight(unsigned int pMapId, float x, float y, float z);
bool processCommand(char *pCommand); // for debug and extensions
void preventMapsFromBeingUsed(const char* pMapIdString);
};
}
#endif

View file

@ -0,0 +1,336 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include "VMapManager2.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
#include "VMapDefinitions.h"
using G3D::Vector3;
namespace VMAP
{
//=========================================================
VMapManager2::VMapManager2()
{
}
//=========================================================
VMapManager2::~VMapManager2(void)
{
for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
{
delete i->second;
}
for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
{
delete i->second.getModel();
}
}
//=========================================================
Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
{
Vector3 pos;
const float mid = 0.5f * 64.0f * 533.33333333f;
pos.x = mid - x;
pos.y = mid - y;
pos.z = z;
return pos;
}
//=========================================================
Vector3 VMapManager2::convertPositionToMangosRep(float x, float y, float z) const
{
Vector3 pos;
const float mid = 0.5f * 64.0f * 533.33333333f;
pos.x = mid - x;
pos.y = mid - y;
pos.z = z;
return pos;
}
//=========================================================
// move to MapTree too?
std::string VMapManager2::getMapFileName(unsigned int pMapId)
{
std::stringstream fname;
fname.width(3);
fname << std::setfill('0') << pMapId << std::string(MAP_FILENAME_EXTENSION2);
return fname.str();
}
//=========================================================
/**
Block maps from being used.
parameter: String of map ids. Delimiter = ","
e.g.: "0,1,590"
*/
void VMapManager2::preventMapsFromBeingUsed(const char* pMapIdString)
{
iIgnoreMapIds.clear();
if (pMapIdString != NULL)
{
std::string map_str;
std::stringstream map_ss;
map_ss.str(std::string(pMapIdString));
while (std::getline(map_ss, map_str, ','))
{
std::stringstream ss2(map_str);
int map_num = -1;
ss2 >> map_num;
if (map_num >= 0)
{
DETAIL_LOG("Ignoring Map %i for VMaps", map_num);
iIgnoreMapIds[map_num] = true;
// unload map in case it is loaded
unloadMap(map_num);
}
}
}
}
//=========================================================
int VMapManager2::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
if (isMapLoadingEnabled() && !iIgnoreMapIds.count(pMapId))
{
if (_loadMap(pMapId, pBasePath, x, y))
result = VMAP_LOAD_RESULT_OK;
else
result = VMAP_LOAD_RESULT_ERROR;
}
return result;
}
//=========================================================
// load one tile (internal use only)
bool VMapManager2::_loadMap(unsigned int pMapId, const std::string &basePath, uint32 tileX, uint32 tileY)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree == iInstanceMapTrees.end())
{
std::string mapFileName = getMapFileName(pMapId);
StaticMapTree *newTree = new StaticMapTree(pMapId, basePath);
if (!newTree->InitMap(mapFileName, this))
return false;
instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(pMapId, newTree)).first;
}
return instanceTree->second->LoadMapTile(tileX, tileY, this);
}
//=========================================================
void VMapManager2::unloadMap(unsigned int pMapId)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMap(this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(pMapId);
}
}
}
//=========================================================
void VMapManager2::unloadMap(unsigned int pMapId, int x, int y)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMapTile(x, y, this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(pMapId);
}
}
}
//==========================================================
bool VMapManager2::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2)
{
if (!isLineOfSightCalcEnabled()) return true;
bool result = true;
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
if (pos1 != pos2)
{
result = instanceTree->second->isInLineOfSight(pos1, pos2);
}
}
return result;
}
//=========================================================
/**
get the hit position and return true if we hit something
otherwise the result pos will be the dest pos
*/
bool VMapManager2::getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist)
{
bool result = false;
rx=x2;
ry=y2;
rz=z2;
if (isLineOfSightCalcEnabled())
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1);
Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2);
Vector3 resultPos;
result = instanceTree->second->getObjectHitPos(pos1, pos2, resultPos, pModifyDist);
resultPos = convertPositionToMangosRep(resultPos.x,resultPos.y,resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
}
}
return result;
}
//=========================================================
/**
get height or INVALID_HEIGHT if no height available
*/
float VMapManager2::getHeight(unsigned int pMapId, float x, float y, float z)
{
float height = VMAP_INVALID_HEIGHT_VALUE; //no height
if (isHeightCalcEnabled())
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x,y,z);
height = instanceTree->second->getHeight(pos);
if (!(height < G3D::inf()))
{
height = VMAP_INVALID_HEIGHT_VALUE; //no height
}
}
}
return height;
}
//=========================================================
bool VMapManager2::getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
bool result=false;
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x, y, z);
result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId);
// z is not touched by convertPositionToMangosRep(), so just copy
z = pos.z;
}
return(result);
}
bool VMapManager2::GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const
{
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId);
if (instanceTree != iInstanceMapTrees.end())
{
LocationInfo info;
Vector3 pos = convertPositionToInternalRep(x, y, z);
if (instanceTree->second->GetLocationInfo(pos, info))
{
floor = info.ground_Z;
type = info.hitModel->GetLiquidType();
if (ReqLiquidType && !(type & ReqLiquidType))
return false;
if (info.hitInstance->GetLiquidLevel(pos, info, level))
return true;
}
}
return false;
}
//=========================================================
WorldModel* VMapManager2::acquireModelInstance(const std::string &basepath, const std::string &filename)
{
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
WorldModel *worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
ERROR_LOG("VMapManager2: could not load '%s%s.vmo'!", basepath.c_str(), filename.c_str());
delete worldmodel;
return NULL;
}
DEBUG_LOG("VMapManager2: loading file '%s%s'.", basepath.c_str(), filename.c_str());
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
model->second.incRefCount();
return model->second.getModel();
}
void VMapManager2::releaseModelInstance(const std::string &filename)
{
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
ERROR_LOG("VMapManager2: trying to unload non-loaded file '%s'!", filename.c_str());
return;
}
if( model->second.decRefCount() == 0)
{
DEBUG_LOG("VMapManager2: unloading file '%s'", filename.c_str());
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}
}
//=========================================================
bool VMapManager2::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
return StaticMapTree::CanLoadMap(std::string(pBasePath), pMapId, x, y);
}
} // namespace VMAP

View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPMANAGER2_H
#define _VMAPMANAGER2_H
#include "IVMapManager.h"
#include "Utilities/UnorderedMap.h"
#include "Platform/Define.h"
#include <G3D/Vector3.h>
//===========================================================
#define MAP_FILENAME_EXTENSION2 ".vmtree"
#define FILENAMEBUFFER_SIZE 500
/**
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
Each global map or instance has its own dynamic BSP-Tree.
The loaded ModelContainers are included in one of these BSP-Trees.
Additionally a table to match map ids and map names is used.
*/
//===========================================================
namespace VMAP
{
class StaticMapTree;
class WorldModel;
class ManagedModel
{
public:
ManagedModel(): iModel(0), iRefCount(0) {}
void setModel(WorldModel *model) { iModel = model; }
WorldModel *getModel() { return iModel; }
void incRefCount() { ++iRefCount; }
int decRefCount() { return --iRefCount; }
protected:
WorldModel *iModel;
int iRefCount;
};
typedef UNORDERED_MAP<uint32 , StaticMapTree *> InstanceTreeMap;
typedef UNORDERED_MAP<std::string, ManagedModel> ModelFileMap;
class VMapManager2 : public IVMapManager
{
protected:
// Tree to check collision
ModelFileMap iLoadedModelFiles;
InstanceTreeMap iInstanceMapTrees;
// UNORDERED_MAP<unsigned int , bool> iMapsSplitIntoTiles;
UNORDERED_MAP<unsigned int , bool> iIgnoreMapIds;
bool _loadMap(uint32 pMapId, const std::string &basePath, uint32 tileX, uint32 tileY);
/* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
public:
// public for debug
G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
G3D::Vector3 convertPositionToMangosRep(float x, float y, float z) const;
static std::string getMapFileName(unsigned int pMapId);
VMapManager2();
~VMapManager2(void);
int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId);
bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ;
/**
fill the hit pos and return true, if an object was hit
*/
bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist);
float getHeight(unsigned int pMapId, float x, float y, float z);
bool processCommand(char *pCommand) { return false; } // for debug and extensions
void preventMapsFromBeingUsed(const char* pMapIdString);
bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const;
WorldModel* acquireModelInstance(const std::string &basepath, const std::string &filename);
void releaseModelInstance(const std::string &filename);
// what's the use of this? o.O
virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const
{
return getMapFileName(pMapId);
}
virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y);
};
}
#endif

View file

@ -0,0 +1,567 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "WorldModel.h"
#include "VMapDefinitions.h"
#include "MapTree.h"
using G3D::Vector3;
using G3D::Ray;
template<> struct BoundsTrait<VMAP::GroupModel>
{
static void getBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
};
namespace VMAP
{
bool IntersectTriangle(const MeshTriangle &tri, std::vector<Vector3>::const_iterator points, const G3D::Ray &ray, float &distance)
{
static const float EPS = 1e-5f;
// See RTR2 ch. 13.7 for the algorithm.
const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
const Vector3 p(ray.direction().cross(e2));
const float a = e1.dot(p);
if (abs(a) < EPS) {
// Determinant is ill-conditioned; abort early
return false;
}
const float f = 1.0f / a;
const Vector3 s(ray.origin() - points[tri.idx0]);
const float u = f * s.dot(p);
if ((u < 0.0f) || (u > 1.0f)) {
// We hit the plane of the m_geometry, but outside the m_geometry
return false;
}
const Vector3 q(s.cross(e1));
const float v = f * ray.direction().dot(q);
if ((v < 0.0f) || ((u + v) > 1.0f)) {
// We hit the plane of the triangle, but outside the triangle
return false;
}
const float t = f * e2.dot(q);
if ((t > 0.0f) && (t < distance))
{
// This is a new hit, closer than the previous one
distance = t;
/* baryCoord[0] = 1.0 - u - v;
baryCoord[1] = u;
baryCoord[2] = v; */
return true;
}
// This hit is after the previous hit, so ignore it
return false;
}
class TriBoundFunc
{
public:
TriBoundFunc(std::vector<Vector3> &vert): vertices(vert.begin()) {}
void operator()(const MeshTriangle &tri, G3D::AABox &out) const
{
G3D::Vector3 lo = vertices[tri.idx0];
G3D::Vector3 hi = lo;
lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
out = G3D::AABox(lo, hi);
}
protected:
const std::vector<Vector3>::const_iterator vertices;
};
// ===================== WmoLiquid ==================================
WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type):
iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
{
iHeight = new float[(width+1)*(height+1)];
iFlags = new uint8[width*height];
}
WmoLiquid::WmoLiquid(const WmoLiquid &other): iHeight(0), iFlags(0)
{
*this = other; // use assignment operator...
}
WmoLiquid::~WmoLiquid()
{
delete[] iHeight;
delete[] iFlags;
}
WmoLiquid& WmoLiquid::operator=(const WmoLiquid &other)
{
if (this == &other)
return *this;
iTilesX = other.iTilesX;
iTilesY = other.iTilesY;
iCorner = other.iCorner;
iType = other.iType;
delete iHeight;
delete iFlags;
if (other.iHeight)
{
iHeight = new float[(iTilesX+1)*(iTilesY+1)];
memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
}
else
iHeight = 0;
if (other.iFlags)
{
iFlags = new uint8[iTilesX * iTilesY];
memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
}
else
iFlags = 0;
return *this;
}
bool WmoLiquid::GetLiquidHeight(const Vector3 &pos, float &liqHeight) const
{
float tx_f = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
uint32 tx = uint32(tx_f);
if (tx<0 || tx >= iTilesX) return false;
float ty_f = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
uint32 ty = uint32(ty_f);
if (ty<0 || ty >= iTilesY) return false;
// check if tile shall be used for liquid level
// checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
return false;
// (dx, dy) coordinates inside tile, in [0,1]^2
float dx = tx_f - (float)tx;
float dy = ty_f - (float)ty;
/* Tesselate tile to two triangles (not sure if client does it exactly like this)
^ dy
|
1 x---------x (1,1)
| (b) / |
| / |
| / |
| / (a) |
x---------x---> dx
0 1
*/
const uint32 rowOffset = iTilesX + 1;
if (dx > dy) // case (a)
{
float sx = iHeight[tx+1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
float sy = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx+1 + ty * rowOffset];
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
}
else // case (b)
{
float sx = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx + (ty+1) * rowOffset];
float sy = iHeight[tx + (ty+1) * rowOffset] - iHeight[tx + ty * rowOffset];
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
}
return true;
}
uint32 WmoLiquid::GetFileSize()
{
return 2 * sizeof(uint32) +
sizeof(Vector3) +
(iTilesX + 1)*(iTilesY + 1) * sizeof(float) +
iTilesX * iTilesY;
}
bool WmoLiquid::writeToFile(FILE *wf)
{
bool result = true;
if (result && fwrite(&iTilesX, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iTilesY, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iCorner, sizeof(Vector3), 1, wf) != 1) result = false;
if (result && fwrite(&iType, sizeof(uint32), 1, wf) != 1) result = false;
uint32 size = (iTilesX + 1)*(iTilesY + 1);
if (result && fwrite(iHeight, sizeof(float), size, wf) != size) result = false;
size = iTilesX*iTilesY;
if (result && fwrite(iFlags, sizeof(uint8), size, wf) != size) result = false;
return result;
}
bool WmoLiquid::readFromFile(FILE *rf, WmoLiquid *&out)
{
bool result = true;
WmoLiquid *liquid = new WmoLiquid();
if (result && fread(&liquid->iTilesX, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&liquid->iTilesY, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&liquid->iCorner, sizeof(Vector3), 1, rf) != 1) result = false;
if (result && fread(&liquid->iType, sizeof(uint32), 1, rf) != 1) result = false;
uint32 size = (liquid->iTilesX + 1)*(liquid->iTilesY + 1);
liquid->iHeight = new float[size];
if (result && fread(liquid->iHeight, sizeof(float), size, rf) != size) result = false;
size = liquid->iTilesX * liquid->iTilesY;
liquid->iFlags = new uint8[size];
if (result && fread(liquid->iFlags, sizeof(uint8), size, rf) != size) result = false;
if (!result)
delete liquid;
out = liquid;
return result;
}
// ===================== GroupModel ==================================
GroupModel::GroupModel(const GroupModel &other):
iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(0)
{
if (other.iLiquid)
iLiquid = new WmoLiquid(*other.iLiquid);
}
void GroupModel::setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri)
{
vertices.swap(vert);
triangles.swap(tri);
TriBoundFunc bFunc(vertices);
meshTree.build(triangles, bFunc);
}
bool GroupModel::writeToFile(FILE *wf)
{
bool result = true;
uint32 chunkSize, count;
if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write vertices
if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
count = vertices.size();
chunkSize = sizeof(uint32)+ sizeof(Vector3)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
// write triangle mesh
if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
count = triangles.size();
chunkSize = sizeof(uint32)+ sizeof(MeshTriangle)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
// write mesh BIH
if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
if (result) result = meshTree.writeToFile(wf);
// write liquid data
if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
if (!iLiquid)
{
chunkSize = 0;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
return result;
}
chunkSize = iLiquid->GetFileSize();
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result) result = iLiquid->writeToFile(wf);
return result;
}
bool GroupModel::readFromFile(FILE *rf)
{
char chunk[8];
bool result = true;
uint32 chunkSize, count;
triangles.clear();
vertices.clear();
delete iLiquid;
iLiquid = 0;
if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read vertices
if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result) vertices.resize(count);
if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
// read triangle mesh
if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) triangles.resize(count);
if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
// read mesh BIH
if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
if (result) result = meshTree.readFromFile(rf);
// write liquid data
if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && chunkSize > 0)
result = WmoLiquid::readFromFile(rf, iLiquid);
return result;
}
struct GModelRayCallback
{
GModelRayCallback(const std::vector<MeshTriangle> &tris, const std::vector<Vector3> &vert):
vertices(vert.begin()), triangles(tris.begin()), hit(false) {}
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
{
bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
if (result) hit=true;
return hit;
}
std::vector<Vector3>::const_iterator vertices;
std::vector<MeshTriangle>::const_iterator triangles;
bool hit;
};
bool GroupModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
if (!triangles.size())
return false;
GModelRayCallback callback(triangles, vertices);
meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
return callback.hit;
}
bool GroupModel::IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const
{
if (!triangles.size() || !iBound.contains(pos))
return false;
GModelRayCallback callback(triangles, vertices);
Vector3 rPos = pos - 0.1f * down;
float dist = G3D::inf();
G3D::Ray ray(rPos, down);
bool hit = IntersectRay(ray, dist, false);
if (hit)
z_dist = dist - 0.1f;
return hit;
}
bool GroupModel::GetLiquidLevel(const Vector3 &pos, float &liqHeight) const
{
if (iLiquid)
return iLiquid->GetLiquidHeight(pos, liqHeight);
return false;
}
uint32 GroupModel::GetLiquidType() const
{
// convert to type mask, matching MAP_LIQUID_TYPE_* defines in Map.h
if (iLiquid)
return (1 << iLiquid->GetType());
return 0;
}
// ===================== WorldModel ==================================
void WorldModel::setGroupModels(std::vector<GroupModel> &models)
{
groupModels.swap(models);
groupTree.build(groupModels, BoundsTrait<GroupModel>::getBounds, 1);
}
struct WModelRayCallBack
{
WModelRayCallBack(const std::vector<GroupModel> &mod): models(mod.begin()), hit(false) {}
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool pStopAtFirstHit)
{
bool result = models[entry].IntersectRay(ray, distance, pStopAtFirstHit);
if (result) hit=true;
return hit;
}
std::vector<GroupModel>::const_iterator models;
bool hit;
};
bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
// small M2 workaround, maybe better make separate class with virtual intersection funcs
// in any case, there's no need to use a bound tree if we only have one submodel
if (groupModels.size() == 1)
return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
WModelRayCallBack isc(groupModels);
groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
return isc.hit;
}
class WModelAreaCallback {
public:
WModelAreaCallback(const std::vector<GroupModel> &vals, const Vector3 &down):
prims(vals.begin()), hit(vals.end()), minVol(G3D::inf()), zDist(G3D::inf()), zVec(down) {}
std::vector<GroupModel>::const_iterator prims;
std::vector<GroupModel>::const_iterator hit;
float minVol;
float zDist;
Vector3 zVec;
void operator()(const Vector3& point, uint32 entry)
{
float group_Z;
//float pVol = prims[entry].GetBound().volume();
//if(pVol < minVol)
//{
/* if (prims[entry].iBound.contains(point)) */
if (prims[entry].IsInsideObject(point, zVec, group_Z))
{
//minVol = pVol;
//hit = prims + entry;
if (group_Z < zDist)
{
zDist = group_Z;
hit = prims + entry;
}
#ifdef VMAP_DEBUG
const GroupModel &gm = prims[entry];
printf("%10u %8X %7.3f,%7.3f,%7.3f | %7.3f,%7.3f,%7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
#endif
}
//}
//std::cout << "trying to intersect '" << prims[entry].name << "'\n";
}
};
bool WorldModel::IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const
{
if (!groupModels.size())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.rootId = RootWMOID;
info.groupId = callback.hit->GetWmoID();
info.flags = callback.hit->GetMogpFlags();
info.result = true;
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const
{
if (!groupModels.size())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.hitModel = &(*callback.hit);
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::writeFile(const std::string &filename)
{
FILE *wf = fopen(filename.c_str(), "wb");
if (!wf)
return false;
bool result = true;
uint32 chunkSize, count;
result = fwrite(VMAP_MAGIC,1,8,wf) == 8;
if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
chunkSize = sizeof(uint32) + sizeof(uint32);
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write group models
count=groupModels.size();
if (count)
{
if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
//chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
//if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
for (uint32 i=0; i<groupModels.size() && result; ++i)
result = groupModels[i].writeToFile(wf);
// write group BIH
if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
if (result) result = groupTree.writeToFile(wf);
}
fclose(wf);
return result;
}
bool WorldModel::readFile(const std::string &filename)
{
FILE *rf = fopen(filename.c_str(), "rb");
if (!rf)
return false;
bool result = true;
uint32 chunkSize, count;
char chunk[8]; // Ignore the added magic header
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read group models
if (result && readChunk(rf, chunk, "GMOD", 4))
{
//if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) groupModels.resize(count);
//if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
for (uint32 i=0; i<count && result; ++i)
result = groupModels[i].readFromFile(rf);
// read group BIH
if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
if (result) result = groupTree.readFromFile(rf);
}
fclose(rf);
return result;
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _WORLDMODEL_H
#define _WORLDMODEL_H
#include <G3D/HashTrait.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "BIH.h"
#include "Platform/Define.h"
namespace VMAP
{
class TreeNode;
struct AreaInfo;
struct LocationInfo;
class MeshTriangle
{
public:
MeshTriangle(){};
MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) {};
uint32 idx0;
uint32 idx1;
uint32 idx2;
};
class WmoLiquid
{
public:
WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type);
WmoLiquid(const WmoLiquid &other);
~WmoLiquid();
WmoLiquid& operator=(const WmoLiquid &other);
bool GetLiquidHeight(const Vector3 &pos, float &liqHeight) const;
uint32 GetType() const { return iType; }
float *GetHeightStorage() { return iHeight; }
uint8 *GetFlagsStorage() { return iFlags; }
uint32 GetFileSize();
bool writeToFile(FILE *wf);
static bool readFromFile(FILE *rf, WmoLiquid *&liquid);
private:
WmoLiquid(): iHeight(0), iFlags(0) {};
uint32 iTilesX; //!< number of tiles in x direction, each
uint32 iTilesY;
Vector3 iCorner; //!< the lower corner
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
};
/*! holding additional info for WMO group files */
class GroupModel
{
public:
GroupModel(): iLiquid(0) {}
GroupModel(const GroupModel &other);
GroupModel(uint32 mogpFlags, uint32 groupWMOID, const AABox &bound):
iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(0) {}
~GroupModel() { delete iLiquid; }
//! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
void setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri);
void setLiquidData(WmoLiquid *liquid) { iLiquid = liquid; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const;
bool GetLiquidLevel(const Vector3 &pos, float &liqHeight) const;
uint32 GetLiquidType() const;
bool writeToFile(FILE *wf);
bool readFromFile(FILE *rf);
const G3D::AABox& GetBound() const { return iBound; }
uint32 GetMogpFlags() const { return iMogpFlags; }
uint32 GetWmoID() const { return iGroupWMOID; }
protected:
G3D::AABox iBound;
uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
uint32 iGroupWMOID;
std::vector<Vector3> vertices;
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid *iLiquid;
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
{
public:
WorldModel(): RootWMOID(0) {}
//! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
void setGroupModels(std::vector<GroupModel> &models);
void setRootWmoID(uint32 id) { RootWMOID = id; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const;
bool writeFile(const std::string &filename);
bool readFile(const std::string &filename);
protected:
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
};
} // namespace VMAP
#endif // _WORLDMODEL_H

View file

@ -451,16 +451,13 @@
<ClCompile Include="..\..\src\shared\ServiceWin32.cpp" />
<ClCompile Include="..\..\src\shared\Threading.cpp" />
<ClCompile Include="..\..\src\shared\Util.cpp" />
<ClCompile Include="..\..\src\shared\vmap\BaseModel.cpp" />
<ClCompile Include="..\..\src\shared\vmap\CoordModelMapping.cpp" />
<ClCompile Include="..\..\src\shared\vmap\DebugCmdLogger.cpp" />
<ClCompile Include="..\..\src\shared\vmap\ManagedModelContainer.cpp" />
<ClCompile Include="..\..\src\shared\vmap\ModelContainer.cpp" />
<ClCompile Include="..\..\src\shared\vmap\SubModel.cpp" />
<ClCompile Include="..\..\src\shared\vmap\BIH.cpp" />
<ClCompile Include="..\..\src\shared\vmap\MapTree.cpp" />
<ClCompile Include="..\..\src\shared\vmap\ModelInstance.cpp" />
<ClCompile Include="..\..\src\shared\vmap\TileAssembler.cpp" />
<ClCompile Include="..\..\src\shared\vmap\TreeNode.cpp" />
<ClCompile Include="..\..\src\shared\vmap\VMapFactory.cpp" />
<ClCompile Include="..\..\src\shared\vmap\VMapManager.cpp" />
<ClCompile Include="..\..\src\shared\vmap\VMapManager2.cpp" />
<ClCompile Include="..\..\src\shared\vmap\WorldModel.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\dep\include\mersennetwister\MersenneTwister.h" />
@ -498,23 +495,16 @@
<ClInclude Include="..\..\src\shared\Threading.h" />
<ClInclude Include="..\..\src\shared\Timer.h" />
<ClInclude Include="..\..\src\shared\Util.h" />
<ClInclude Include="..\..\src\shared\vmap\AABSPTree.h" />
<ClInclude Include="..\..\src\shared\vmap\BaseModel.h" />
<ClInclude Include="..\..\src\shared\vmap\CoordModelMapping.h" />
<ClInclude Include="..\..\src\shared\vmap\DebugCmdLogger.h" />
<ClInclude Include="..\..\src\shared\vmap\BIH.h" />
<ClInclude Include="..\..\src\shared\vmap\IVMapManager.h" />
<ClInclude Include="..\..\src\shared\vmap\ManagedModelContainer.h" />
<ClInclude Include="..\..\src\shared\vmap\ModelContainer.h" />
<ClInclude Include="..\..\src\shared\vmap\NodeValueAccess.h" />
<ClInclude Include="..\..\src\shared\vmap\ShortBox.h" />
<ClInclude Include="..\..\src\shared\vmap\ShortVector.h" />
<ClInclude Include="..\..\src\shared\vmap\SubModel.h" />
<ClInclude Include="..\..\src\shared\vmap\MapTree.h" />
<ClInclude Include="..\..\src\shared\vmap\ModelInstance.h" />
<ClInclude Include="..\..\src\shared\vmap\TileAssembler.h" />
<ClInclude Include="..\..\src\shared\vmap\TreeNode.h" />
<ClInclude Include="..\..\src\shared\vmap\VMapDefinitions.h" />
<ClInclude Include="..\..\src\shared\vmap\VMapFactory.h" />
<ClInclude Include="..\..\src\shared\vmap\VMapManager.h" />
<ClInclude Include="..\..\src\shared\vmap\VMapManager2.h" />
<ClInclude Include="..\..\src\shared\vmap\VMapTools.h" />
<ClInclude Include="..\..\src\shared\vmap\WorldModel.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="ACE_vc10.vcxproj">

View file

@ -721,31 +721,11 @@
Name="vmaps"
>
<File
RelativePath="..\..\src\shared\vmap\AABSPTree.h"
RelativePath="..\..\src\shared\vmap\BIH.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\BaseModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\BaseModel.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\CoordModelMapping.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\CoordModelMapping.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\DebugCmdLogger.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\DebugCmdLogger.h"
RelativePath="..\..\src\shared\vmap\BIH.h"
>
</File>
<File
@ -753,39 +733,19 @@
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ManagedModelContainer.cpp"
RelativePath="..\..\src\shared\vmap\MapTree.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ManagedModelContainer.h"
RelativePath="..\..\src\shared\vmap\MapTree.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ModelContainer.cpp"
RelativePath="..\..\src\shared\vmap\ModelInstance.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ModelContainer.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\NodeValueAccess.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ShortBox.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ShortVector.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\SubModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\SubModel.h"
RelativePath="..\..\src\shared\vmap\ModelInstance.h"
>
</File>
<File
@ -796,14 +756,6 @@
RelativePath="..\..\src\shared\vmap\TileAssembler.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\TreeNode.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\TreeNode.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapDefinitions.h"
>
@ -817,17 +769,25 @@
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapManager.cpp"
RelativePath="..\..\src\shared\vmap\VMapManager2.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapManager.h"
RelativePath="..\..\src\shared\vmap\VMapManager2.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapTools.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\WorldModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\WorldModel.h"
>
</File>
</Filter>
<File
RelativePath="..\..\src\shared\Common.cpp"

View file

@ -727,31 +727,11 @@
Name="vmaps"
>
<File
RelativePath="..\..\src\shared\vmap\AABSPTree.h"
RelativePath="..\..\src\shared\vmap\BIH.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\BaseModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\BaseModel.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\CoordModelMapping.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\CoordModelMapping.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\DebugCmdLogger.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\DebugCmdLogger.h"
RelativePath="..\..\src\shared\vmap\BIH.h"
>
</File>
<File
@ -759,39 +739,19 @@
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ManagedModelContainer.cpp"
RelativePath="..\..\src\shared\vmap\MapTree.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ManagedModelContainer.h"
RelativePath="..\..\src\shared\vmap\MapTree.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ModelContainer.cpp"
RelativePath="..\..\src\shared\vmap\ModelInstance.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ModelContainer.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\NodeValueAccess.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ShortBox.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\ShortVector.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\SubModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\SubModel.h"
RelativePath="..\..\src\shared\vmap\ModelInstance.h"
>
</File>
<File
@ -802,14 +762,6 @@
RelativePath="..\..\src\shared\vmap\TileAssembler.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\TreeNode.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\TreeNode.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapDefinitions.h"
>
@ -823,17 +775,25 @@
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapManager.cpp"
RelativePath="..\..\src\shared\vmap\VMapManager2.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapManager.h"
RelativePath="..\..\src\shared\vmap\VMapManager2.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\VMapTools.h"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\WorldModel.cpp"
>
</File>
<File
RelativePath="..\..\src\shared\vmap\WorldModel.h"
>
</File>
</Filter>
<File
RelativePath="..\..\src\shared\Common.cpp"