aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/Creature.cpp2
-rw-r--r--src/game/DBCEnums.h4
-rw-r--r--src/game/DBCStores.cpp42
-rw-r--r--src/game/DBCStores.h3
-rw-r--r--src/game/DBCStructure.h17
-rw-r--r--src/game/DBCfmt.h1
-rw-r--r--src/game/Level1.cpp9
-rw-r--r--src/game/Map.cpp340
-rw-r--r--src/game/Map.h9
-rw-r--r--src/game/Player.cpp23
-rw-r--r--src/game/Player.h2
-rw-r--r--src/game/Spell.cpp13
-rw-r--r--src/game/SpellEffects.cpp15
-rw-r--r--src/game/Unit.cpp12
-rw-r--r--src/game/Unit.h1
-rw-r--r--src/game/World.cpp1
-rw-r--r--src/game/World.h1
-rw-r--r--src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp4
-rw-r--r--src/scripts/outland/black_temple/boss_teron_gorefiend.cpp2
-rw-r--r--src/shared/vmap/AABSPTree.h1590
-rw-r--r--src/shared/vmap/BIH.cpp304
-rw-r--r--src/shared/vmap/BIH.h391
-rw-r--r--src/shared/vmap/BaseModel.cpp98
-rw-r--r--src/shared/vmap/BaseModel.h102
-rw-r--r--src/shared/vmap/CMakeLists.txt32
-rw-r--r--src/shared/vmap/CoordModelMapping.cpp200
-rw-r--r--src/shared/vmap/CoordModelMapping.h143
-rw-r--r--src/shared/vmap/DebugCmdLogger.cpp131
-rw-r--r--src/shared/vmap/DebugCmdLogger.h119
-rw-r--r--src/shared/vmap/IVMapManager.h19
-rw-r--r--src/shared/vmap/ManagedModelContainer.cpp38
-rw-r--r--src/shared/vmap/ManagedModelContainer.h52
-rw-r--r--src/shared/vmap/MapTree.cpp450
-rw-r--r--src/shared/vmap/MapTree.h97
-rw-r--r--src/shared/vmap/ModelContainer.cpp378
-rw-r--r--src/shared/vmap/ModelContainer.h111
-rw-r--r--src/shared/vmap/ModelInstance.cpp219
-rw-r--r--src/shared/vmap/ModelInstance.h81
-rw-r--r--src/shared/vmap/NodeValueAccess.h51
-rw-r--r--src/shared/vmap/ShortBox.h151
-rw-r--r--src/shared/vmap/ShortVector.h137
-rw-r--r--src/shared/vmap/SubModel.cpp251
-rw-r--r--src/shared/vmap/SubModel.h105
-rw-r--r--src/shared/vmap/TileAssembler.cpp770
-rw-r--r--src/shared/vmap/TileAssembler.h57
-rw-r--r--src/shared/vmap/TreeNode.cpp40
-rw-r--r--src/shared/vmap/TreeNode.h226
-rw-r--r--src/shared/vmap/VMapDefinitions.h20
-rw-r--r--src/shared/vmap/VMapFactory.cpp45
-rw-r--r--src/shared/vmap/VMapFactory.h9
-rw-r--r--src/shared/vmap/VMapManager.cpp776
-rw-r--r--src/shared/vmap/VMapManager.h176
-rw-r--r--src/shared/vmap/VMapManager2.cpp336
-rw-r--r--src/shared/vmap/VMapManager2.h114
-rw-r--r--src/shared/vmap/VMapTools.h11
-rw-r--r--src/shared/vmap/WorldModel.cpp535
-rw-r--r--src/shared/vmap/WorldModel.h123
-rw-r--r--src/trinitycore/trinitycore.conf.dist7
58 files changed, 3348 insertions, 5648 deletions
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp
index bbb81bd3c9b..fbfae17a48b 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -1539,7 +1539,7 @@ bool Creature::FallGround()
float x, y, z;
GetPosition(x, y, z);
- float ground_Z = GetMap()->GetVmapHeight(x, y, z);
+ float ground_Z = GetMap()->GetHeight(x, y, z);
if (fabs(ground_Z - z) < 0.1f)
return false;
diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h
index 28eef9f714e..bd72026d753 100644
--- a/src/game/DBCEnums.h
+++ b/src/game/DBCEnums.h
@@ -250,8 +250,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_OUTDOOR_PVP2 = 0x08000000, // Wintergrasp and it's subzones
AREA_FLAG_NO_FLY_ZONE = 0x20000000 // Marks zones where you cannot fly
};
diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp
index fff32dff566..ba0e3af9a64 100644
--- a/src/game/DBCStores.cpp
+++ b/src/game/DBCStores.cpp
@@ -32,11 +32,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);
DBCStorage <AreaPOIEntry> sAreaPOIStore(AreaPOIEntryfmt);
static AreaFlagByAreaID sAreaFlagByAreaID;
-static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files
+static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files
+
+static WMOAreaInfoByTripple sWMOAreaInfoByTripple;
DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt);
DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt);
@@ -161,7 +182,8 @@ static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt);
DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage <VehicleEntry> sVehicleStore(VehicleEntryfmt);
DBCStorage <VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt);
-DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
+DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore(WMOAreaTableEntryfmt);
+DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt);
DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt);
@@ -542,6 +564,14 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc");
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,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,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapOverlayStore, dbcPath,"WorldMapOverlay.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc");
@@ -635,6 +665,14 @@ 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);
diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h
index acf5ac8ec41..b14814e07a1 100644
--- a/src/game/DBCStores.h
+++ b/src/game/DBCStores.h
@@ -37,6 +37,8 @@ AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id);
AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id);
uint32 GetAreaFlagByMapId(uint32 mapid);
+WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid);
+
uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId);
enum ContentLevels
@@ -155,6 +157,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;
diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h
index 584fc70984c..0fc0d1251f5 100644
--- a/src/game/DBCStructure.h
+++ b/src/game/DBCStructure.h
@@ -1815,6 +1815,23 @@ struct VehicleSeatEntry
bool IsUsable() const { return m_flags & 0x2000000; }
};
+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
diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h
index bed688cc126..1f1b010a6fd 100644
--- a/src/game/DBCfmt.h
+++ b/src/game/DBCfmt.h
@@ -114,6 +114,7 @@ const char TaxiPathNodeEntryfmt[]="diiifffiixx";
const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii";
const char VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx";
const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx";
+const char WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx";
const char WorldMapAreaEntryfmt[]="xinxffffixx";
const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx";
const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
index b06e3628a27..11189d519a0 100644
--- a/src/game/Level1.cpp
+++ b/src/game/Level1.cpp
@@ -741,6 +741,15 @@ bool ChatHandler::HandleGPSCommand(const char* args)
uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0;
uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0;
+ if(have_vmap)
+ {
+ if(map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()))
+ PSendSysMessage("You are outdoors");
+ 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>"),
diff --git a/src/game/Map.cpp b/src/game/Map.cpp
index 53efb35e373..f8e48d3a326 100644
--- a/src/game/Map.cpp
+++ b/src/game/Map.cpp
@@ -1746,220 +1746,105 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
}
}
-float Map::GetVmapHeight(float x, float y, float z) const
+inline bool IsOutdoorWMO(uint32 mogpFlags, int32 adtId, int32 rootId, int32 groupId, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry)
{
- float mapHeight;
-
- mapHeight = GetHeight(x, y, z, false);
- if (fabs(mapHeight - z) < 0.1)
- return mapHeight;
+ bool outdoor = true;
- VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
- if (!vmgr->isLineOfSightCalcEnabled())
- return mapHeight;
+ if(wmoEntry && atEntry)
+ {
+ if(atEntry->flags & AREA_FLAG_OUTSIDE)
+ return true;
+ if(atEntry->flags & AREA_FLAG_INSIDE)
+ return false;
+ }
- float vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f, z + 2.0f - mapHeight);
- if (vmapHeight > VMAP_INVALID_HEIGHT_VALUE)
- return vmapHeight;
+ outdoor = mogpFlags&0x8;
- return mapHeight;
+ if(wmoEntry)
+ {
+ if(wmoEntry->Flags & 4)
+ return true;
+ if((wmoEntry->Flags & 2)!=0)
+ outdoor = false;
+ }
+ return outdoor;
}
-uint16 Map::GetAreaFlag(float x, float y, float z) const
+bool Map::IsOutdoors(float x, float y, float z) const
{
- uint16 areaflag;
- 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(GetId());
-
- //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 2817: // Argent Tournament Grounds (Icecrown)
- if (x < 8445.0f && x > 8390.0f && y > 640.0f && y < 689.0f && 548.0f < z && z < 562.0f)
- areaflag = 2875; // Sunreaver Pavilion (Icecrown)
- else if (x > 8587.0f && x < 8629.0f && y > 646.0f && y < 686.0f && 548.0f < z && z < 562.0f)
- areaflag = 2879; // Silver Covenant Pavilion (Icecrown)
- break;
- case 2227: // The Foot Steppes (Storm Peaks)
- case 2207: // Sifreldar Village (Storm Peaks)
- if (6924.0f < x && x < 6980.0f && -1520.0f < y && y < -1432.0f && 838.0f < z && z < 843.0f)
- areaflag = 2213; // The Forlorn Mine (Storm Peaks)
- break;
- case 2209: // Brunnhildar Village (Storm Peaks)
- if (6885.0f < x && x < 6938.0f && -1200.0f < y && y < -1138.0f && 801.0f < z && z < 809.0f)
- areaflag = 2213; // The Forlorn Mine (Storm Peaks)
- break;
- case 166: // Storm Peaks
- if (6812.0f < x && x < 7048.0f && -1463.0f < y && y < -1200.0f && 807.0f < z && z < 843.0f)
- areaflag = 2213; // The Forlorn Mine (Storm Peaks)
- break;
- case 446: // (Stonelaton Mountains)
- case 944: // Boulderslide Ravine (Stonelaton Mountains)
- if (-128.0f < x && x < 35.0f && 221.0f < y && y < 456.0f && 87.0f < z && z < 130.0f)
- areaflag = 1019; // Boulderslide Cavern (Stonelaton Mountains)
- break;
- case 272: // Palemane Rock (Mulgore)
- if (-2466.0f < x && x < -2295.0f && 366.0f < y && y < 530.0f && 40.0f < z && z < 70.0f)
- areaflag = 668; // Palemane Rock (Mulgore)
- break;
- case 65535: // Multipe places.
- if (-128.0f < x && x < 35.0f && 221.0f < y && y < 456.0f && 87.0f < z && z < 130.0f)
- areaflag = 1019; // Boulderslide Cavern (Stonelaton Mountains)
- else if (-2466.0f < x && x < -2295.0f && 366.0f < y && y < 530.0f && 40.0f < z && z < 70.0f)
- areaflag = 668; // Palemane Rock (Mulgore)
- 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;
- // Dalaran
- case 2492: // Forlorn Woods (Crystalsong Forest)
- case 2371: // Valley of Echoes (Icecrown Glacier)
- if (x > 5568.0f && x < 6022.0f && y > 374.0f && y < 918.0f && z > 563.0f)
- {
- areaflag = 2153;
- if (y - 1.41f * x + 7649.55f > 0) // Violet Hold
- {
- if (y < 595.0f)
- areaflag = 2540;
- }
- else if (y + 2.91 * x - 17522.57f < 0) // Krasus landing
- areaflag = 2531;
- }
- 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 < 918.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;
- }
- }
+ uint32 mogpFlags;
+ int32 adtId, rootId, groupId;
- // The Eventide (Dalaran), fast check against diagonal box with lower limit
- if (z > 635.0f && x+y < 6375.0f && x+y > 6295.0f && x-y < 5106.0f && x-y > 4972.0f)
- {
- areaflag = 2543;
- break;
- }
+ // no wmo found? -> outside by default
+ if(!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
+ return true;
- // The Violet Hold (Dalaran), fast check
- if (x < 5791.0f && y > 404.0f && y < 595.0f)
- {
- areaflag = 2540;
- break;
- }
+ 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);
+}
- // 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;
- }
+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;
+}
- // The Violet Citadel (Dalaran)
- if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
- {
- areaflag = 2696;
- break;
- }
- }
+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;
- // Dalaran
- areaflag = 2153;
- }
- break;
- // Maw of Neltharion (cave)
- case 164: // Dragonblight
- case 1797: // Obsidian Dragonshrine (Dragonblight)
- 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
- else
- {
- if (x > 1299.0f && x < 1839.0f && y > 10.0f && y < 440.0f) areaflag = 685;
- }
- 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;
+ 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_mapEntry->MapID);
}
+ if (isOutdoors)
+ {
+ if (haveAreaInfo)
+ *isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
+ else
+ *isOutdoors = true;
+ }
return areaflag;
-}
+ }
uint8 Map::GetTerrainType(float x, float y) const
{
@@ -1971,10 +1856,51 @@ uint8 Map::GetTerrainType(float x, float y) const
ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) const
{
- if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
- return gmap->getLiquidStatus(x, y, z, ReqLiquidType, data);
- else
- return LIQUID_MAP_NO_WATER;
+ ZLiquidStatus 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))
+ {
+ sLog.outDebug("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))
+ {
+ LiquidData map_data;
+ ZLiquidStatus 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
@@ -2013,17 +1939,15 @@ 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, float min_depth) const
+bool Map::IsInWater(float x, float y, float pZ, LiquidData *data) const
{
// Check surface in x, y point for liquid
if (const_cast<Map*>(this)->GetGrid(x, y))
{
LiquidData liquid_status;
- if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status))
- {
- if (liquid_status.level - liquid_status.depth_level > min_depth)
+ LiquidData *liquid_ptr = data ? data : &liquid_status;
+ if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr))
return true;
- }
}
return false;
}
diff --git a/src/game/Map.h b/src/game/Map.h
index a192390ba37..ceb526b8244 100644
--- a/src/game/Map.h
+++ b/src/game/Map.h
@@ -314,14 +314,17 @@ class Map : public GridRefManager<NGridType>, public Trinity::ObjectLevelLockabl
// 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;
- float GetVmapHeight(float x, float y, float z) const;
- bool IsInWater(float x, float y, float z, float min_depth = 2.0f) const;
ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0) const;
- uint16 GetAreaFlag(float x, float y, float z) const;
+ uint16 GetAreaFlag(float x, float y, float z, bool *isOutdoors=0) const;
+ 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;
+
uint8 GetTerrainType(float x, float y) const;
float GetWaterLevel(float x, float y) const;
+ bool IsInWater(float x, float y, float z, LiquidData *data = 0) const;
bool IsUnderWater(float x, float y, float z) const;
static uint32 GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id);
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 43965f5646a..2221098a9be 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -1065,7 +1065,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);
}
}
@@ -4574,7 +4576,7 @@ bool Player::FallGround(uint8 FallMode)
float x, y, z;
GetPosition(x, y, z);
- float ground_Z = GetMap()->GetVmapHeight(x, y, z);
+ float ground_Z = GetMap()->GetHeight(x, y, z);
float z_diff = 0.0f;
if ((z_diff = fabs(ground_Z - z)) < 0.1f)
return false;
@@ -6123,7 +6125,7 @@ bool Player::SetPosition(float x, float y, float z, float orientation, bool tele
if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE))
GetSession()->SendCancelTrade();
- CheckExploreSystem();
+ CheckAreaExploreAndOutdoor();
return true;
}
@@ -6197,7 +6199,7 @@ void Player::SendMovieStart(uint32 MovieId)
SendDirectMessage(&data);
}
-void Player::CheckExploreSystem()
+void Player::CheckAreaExploreAndOutdoor()
{
if (!isAlive())
return;
@@ -6213,8 +6215,13 @@ void Player::CheckExploreSystem()
GetSession()->HandleOnAreaChange(GetAreaEntryByAreaID(m_AreaID));
}
- uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ());
- if (areaFlag == 0xffff)
+ bool isOutdoor;
+ uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ(), &isOutdoor);
+
+ if (sWorld.getConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
+ RemoveAurasWithAttribute(SPELL_ATTR_OUTDOORS_ONLY);
+
+ if (areaFlag==0xffff)
return;
int offset = areaFlag / 32;
@@ -22019,8 +22026,8 @@ void Player::UpdateUnderwaterState(Map* m, float x, float y, float z)
{
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER|UNDERWATER_INLAVA|UNDERWATER_INSLIME|UNDERWARER_INDARKWATER);
// Small hack for enable breath in WMO
- if (IsInWater())
- m_MirrorTimerFlags|=UNDERWATER_INWATER;
+ /* if (IsInWater())
+ m_MirrorTimerFlags|=UNDERWATER_INWATER; */
return;
}
diff --git a/src/game/Player.h b/src/game/Player.h
index af8c5fb971d..d6de8679323 100644
--- a/src/game/Player.h
+++ b/src/game/Player.h
@@ -1845,7 +1845,7 @@ class Player : public Unit, public GridObject<Player>
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; }
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index b039767c463..5016e5ebc2b 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -4404,6 +4404,17 @@ 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)
@@ -5248,7 +5259,7 @@ SpellCastResult Spell::CheckCast(bool strict)
{
float x, y, z;
m_caster->GetPosition(x, y, z);
- float ground_Z = m_caster->GetMap()->GetVmapHeight(x, y, z);
+ float ground_Z = m_caster->GetMap()->GetHeight(x, y, z);
if (fabs(ground_Z - z) < 0.1f)
return SPELL_FAILED_DONT_REPORT;
break;
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
index 4f0c693efbb..2f7486d53d9 100644
--- a/src/game/SpellEffects.cpp
+++ b/src/game/SpellEffects.cpp
@@ -50,10 +50,10 @@
#include "BattleGroundEY.h"
#include "BattleGroundWS.h"
#include "OutdoorPvPMgr.h"
-#include "VMapFactory.h"
#include "Language.h"
#include "SocialMgr.h"
#include "Util.h"
+#include "VMapFactory.h"
#include "TemporarySummon.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
@@ -7309,13 +7309,8 @@ void Spell::EffectTransmitted(uint32 effIndex)
Map *cMap = m_caster->GetMap();
if (goinfo->type == GAMEOBJECT_TYPE_FISHINGNODE)
{
- //dirty way to hack serpent shrine pool
- if (cMap->GetId() == 548 && m_caster->GetDistance(36.69, -416.38, -19.9645) <= 16)//center of strange pool
- {
- fx = 36.69+irand(-8,8);//random place for the bobber
- fy = -416.38+irand(-8,8);
- fz = -19.9645;//serpentshrine water level
- }else if (!cMap->IsInWater(fx, fy, fz-0.5f, 0.5f)) // Hack to prevent fishing bobber from failing to land on fishing hole
+ LiquidData 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);
@@ -7323,8 +7318,8 @@ void Spell::EffectTransmitted(uint32 effIndex)
}
// replace by water level in this case
- if (cMap->GetId() != 548)//if map is not serpentshrine caverns
- 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)
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index be0ee41f576..720e177075f 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -4169,6 +4169,18 @@ void Unit::RemoveAurasByType(AuraType auraType, uint64 casterGUID, Aura * except
}
}
+void Unit::RemoveAurasWithAttribute(uint32 flags)
+{
+ for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
+ {
+ SpellEntry const *spell = iter->second->GetBase()->GetSpellProto();
+ if (spell->Attributes & flags)
+ RemoveAura(iter);
+ else
+ ++iter;
+ }
+}
+
void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
{
// single target auras from other casters
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 567fba03370..a2f4b2cd388 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -1575,6 +1575,7 @@ class Unit : public WorldObject
void RemoveAurasByType(AuraType auraType, uint64 casterGUID = 0, Aura * except = NULL, bool negative = true, bool positive = true);
void RemoveNotOwnSingleTargetAuras(uint32 newPhase = 0x0);
void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = NULL);
+ void RemoveAurasWithAttribute(uint32 flags);
void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, uint64 casterGUID);
void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except=0);
void RemoveMovementImpairingAuras();
diff --git a/src/game/World.cpp b/src/game/World.cpp
index 1629aa61af9..0b00621dc72 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -1188,6 +1188,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outString("Using DataDir %s",m_dataPath.c_str());
}
+ m_configs[CONFIG_VMAP_INDOOR_CHECK] = sConfig.GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableLOS = sConfig.GetBoolDefault("vmap.enableLOS", false);
bool enableHeight = sConfig.GetBoolDefault("vmap.enableHeight", false);
std::string ignoreMapIds = sConfig.GetStringDefault("vmap.ignoreMapIds", "");
diff --git a/src/game/World.h b/src/game/World.h
index 37e869f7ddf..2a4f963bfdb 100644
--- a/src/game/World.h
+++ b/src/game/World.h
@@ -279,6 +279,7 @@ enum WorldConfigs
CONFIG_STATS_SAVE_ONLY_ON_LOGOUT,
CONFIG_BG_XP_FOR_KILL,
CONFIG_RANDOM_BG_RESET_HOUR,
+ CONFIG_VMAP_INDOOR_CHECK,
CONFIG_VALUE_COUNT
};
diff --git a/src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp b/src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
index 5dea18434a6..c4cf9ede397 100644
--- a/src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
+++ b/src/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
@@ -1126,7 +1126,7 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
float x,y,z;
me->GetPosition(x,y,z);
- z = me->GetMap()->GetVmapHeight(x, y, z);
+ z = me->GetMap()->GetHeight(x, y, z);
me->GetMotionMaster()->MovePoint(0,x,y,z);
me->GetMap()->CreatureRelocation(me, x,y,z,0);
}
@@ -1238,7 +1238,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
{
float x,y,z;
me->GetPosition(x,y,z);
- z = me->GetMap()->GetVmapHeight(x, y, z);
+ z = me->GetMap()->GetHeight(x, y, z);
me->GetMotionMaster()->MovePoint(0,x,y,z);
me->GetMap()->CreatureRelocation(me, x,y,z,0);
hyjal_trashAI::JustDied(victim);
diff --git a/src/scripts/outland/black_temple/boss_teron_gorefiend.cpp b/src/scripts/outland/black_temple/boss_teron_gorefiend.cpp
index 4c3887e2ba3..670edc4d869 100644
--- a/src/scripts/outland/black_temple/boss_teron_gorefiend.cpp
+++ b/src/scripts/outland/black_temple/boss_teron_gorefiend.cpp
@@ -399,7 +399,7 @@ struct boss_teron_gorefiendAI : public ScriptedAI
float X = CalculateRandomLocation(pTarget->GetPositionX(), 20);
float Y = CalculateRandomLocation(pTarget->GetPositionY(), 20);
float Z = pTarget->GetPositionZ();
- Z = me->GetMap()->GetVmapHeight(X, Y, Z);
+ Z = me->GetMap()->GetHeight(X, Y, Z);
Creature* DoomBlossom = me->SummonCreature(CREATURE_DOOM_BLOSSOM, X, Y, Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000);
if (DoomBlossom)
{
diff --git a/src/shared/vmap/AABSPTree.h b/src/shared/vmap/AABSPTree.h
deleted file mode 100644
index 1a7b17cdaa2..00000000000
--- a/src/shared/vmap/AABSPTree.h
+++ /dev/null
@@ -1,1590 +0,0 @@
-/**
- @file AABSPTree.h
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
- @created 2004-01-11
- @edited 2007-02-16
-
- Copyright 2000-2007, Morgan McGuire.
- All rights reserved.
-
- */
-
-#ifndef G3D_AABSPTREE_H
-#define G3D_AABSPTREE_H
-
-#include "VMapTools.h"
-
-#include "G3D/platform.h"
-#include "G3D/Array.h"
-#include "G3D/Table.h"
-#include "G3D/Vector3.h"
-#include "G3D/AABox.h"
-#include "G3D/Sphere.h"
-#include "G3D/Box.h"
-#include "G3D/Triangle.h"
-#include "G3D/Ray.h"
-#include "G3D/GCamera.h"
-#if 0
-#include "G3D/BinaryInput.h"
-#include "G3D/BinaryOutput.h"
-#endif
-#include "G3D/CollisionDetection.h"
-#include "G3D/GCamera.h"
-#include <algorithm>
-
-// If defined, in debug mode the tree is checked for consistency
-// as a way of detecting corruption due to implementation bugs
-// #define VERIFY_TREE
-
-inline void getBounds(const G3D::Vector3& v, G3D::AABox& out) {
- out = G3D::AABox(v);
-}
-
-inline void getBounds(const G3D::AABox& a, G3D::AABox& out) {
- out = a;
-}
-
-inline void getBounds(const G3D::Sphere& s, G3D::AABox& out) {
- s.getBounds(out);
-}
-
-inline void getBounds(const G3D::Box& b, G3D::AABox& out) {
- b.getBounds(out);
-}
-
-inline void getBounds(const G3D::Triangle& t, G3D::AABox& out) {
- t.getBounds(out);
-}
-
-inline void getBounds(const G3D::Vector3* v, G3D::AABox& out) {
- out = G3D::AABox(*v);
-}
-
-inline void getBounds(const G3D::AABox* a, G3D::AABox& out) {
- getBounds(*a, out);
-}
-
-inline void getBounds(const G3D::Sphere* s, G3D::AABox& out) {
- s->getBounds(out);
-}
-
-inline void getBounds(const G3D::Box* b, G3D::AABox& out) {
- b->getBounds(out);
-}
-
-inline void getBounds(const G3D::Triangle* t, G3D::AABox& out) {
- t->getBounds(out);
-}
-namespace G3D {
- namespace _internal {
-
- /**
- Wraps a pointer value so that it can be treated as the instance itself;
- convenient for inserting pointers into a Table but using the
- object equality instead of pointer equality.
- */
- template<class Type>
- class Indirector {
- public:
- Type* handle;
-
- inline Indirector(Type* h) : handle(h) {}
-
- inline Indirector() : handle(NULL) {}
-
- /** Returns true iff the values referenced by the handles are equivalent. */
- inline bool operator==(const Indirector& m) {
- return *handle == *(m.handle);
- }
-
- inline bool operator==(const Type& m) {
- return *handle == m;
- }
-
- inline size_t hashCode() const {
- return handle->hashCode();
- }
- };
- } // namespace internal
-} // namespace G3D
-
-template <class Handle>
-struct GHashCode< G3D::_internal::Indirector<Handle> >
-{
- size_t operator()(const G3D::_internal::Indirector<Handle>& key) const { return key.hashCode(); }
-};
-
-namespace G3D {
-
-/**
- A set that supports spatial queries using an axis-aligned
- BSP tree for speed.
-
- AABSPTree allows you to quickly find objects in 3D that lie within
- a box or along a ray. For large sets of objects it is much faster
- than testing each object for a collision.
-
- AABSPTree is as powerful as but more general than a Quad Tree, Oct
- Tree, or KD Tree, but less general than an unconstrained BSP tree
- (which is much slower to create).
-
- Internally, objects
- are arranged into an axis-aligned BSP-tree according to their
- axis-aligned bounds. This increases the cost of insertion to
- O(log n) but allows fast overlap queries.
-
- <B>Template Parameters</B>
- <DT>The template parameter <I>T</I> must be one for which
- the following functions are all overloaded:
-
- <P><CODE>void ::getBounds(const T&, G3D::AABox&);</CODE>
- <DT><CODE>bool ::operator==(const T&, const T&);</CODE>
- <DT><CODE>unsigned int ::hashCode(const T&);</CODE>
- <DT><CODE>T::T();</CODE> <I>(public constructor of no arguments)</I>
-
- G3D provides these for common classes like G3D::Vector3 and G3D::Sphere.
- If you use a custom class, or a pointer to a custom class, you will need
- to define those functions.
-
- <B>Moving %Set Members</B>
- <DT>It is important that objects do not move without updating the
- AABSPTree. If the axis-aligned bounds of an object are about
- to change, AABSPTree::remove it before they change and
- AABSPTree::insert it again afterward. For objects
- where the hashCode and == operator are invariant with respect
- to the 3D position,
- you can use the AABSPTree::update method as a shortcut to
- insert/remove an object in one step after it has moved.
-
- Note: Do not mutate any value once it has been inserted into AABSPTree. Values
- are copied interally. All AABSPTree iterators convert to pointers to constant
- values to reinforce this.
-
- If you want to mutate the objects you intend to store in a AABSPTree
- simply insert <I>pointers</I> to your objects instead of the objects
- themselves, and ensure that the above operations are defined. (And
- actually, because values are copied, if your values are large you may
- want to insert pointers anyway, to save space and make the balance
- operation faster.)
-
- <B>Dimensions</B>
- Although designed as a 3D-data structure, you can use the AABSPTree
- for data distributed along 2 or 1 axes by simply returning bounds
- that are always zero along one or more dimensions.
-
-*/
-namespace _AABSPTree {
-
- /** Wrapper for a value that includes a cache of its bounds.
- Except for the test value used in a set-query operation, there
- is only ever one instance of the handle associated with any
- value and the memberTable and Nodes maintain pointers to that
- heap-allocated value.
- */
- template<class TValue>
- class Handle {
- public:
- /** The bounds of each object are constrained to AABox::maxFinite */
- AABox bounds;
-
- /** Center of bounds. We cache this value to avoid recomputing it
- during the median sort, and because MSVC 6 std::sort goes into
- an infinite loop if we compute the midpoint on the fly (possibly
- a floating point roundoff issue, where B<A and A<B both are true).*/
- Vector3 center;
-
- TValue value;
-
- Handle<TValue>() {}
-
- inline Handle<TValue>(const TValue& v) : value(v) {
- getBounds(v, bounds);
- bounds = bounds.intersect(AABox::maxFinite());
- center = bounds.center();
- }
-
- inline bool operator==(const Handle<TValue>& other) const {
- return (*value).operator==(*other.value);
- }
-
- inline size_t hashCode() const {
- return value->hashCode();
- }
- };
-
- template<>
- class Handle<Triangle> {
- public:
- /** The bounds of each object are constrained to AABox::maxFinite */
- AABox bounds;
-
- /** Center of bounds. We cache this value to avoid recomputing it
- during the median sort, and because MSVC 6 std::sort goes into
- an infinite loop if we compute the midpoint on the fly (possibly
- a floating point roundoff issue, where B<A and A<B both are true).*/
- Vector3 center;
-
- Triangle value;
-
- Handle<Triangle>() {}
-
- inline Handle<Triangle>(const Triangle& v) : value(v) {
- getBounds(v, bounds);
- bounds = bounds.intersect(AABox::maxFinite());
- center = bounds.center();
- }
-
- inline bool operator==(const Handle<Triangle>& other) const {
- return value.operator==(other.value);
- }
-
- inline size_t hashCode() const {
- return value.hashCode();
- }
- };
-}
-
-template<class T> class AABSPTree {
-protected:
-public:
-
- /** Returns the bounds of the sub array. Used by makeNode. */
- static AABox computeBounds(
- const Array<_AABSPTree::Handle<T>*>& point,
- int beginIndex,
- int endIndex) {
-
- Vector3 lo = Vector3::inf();
- Vector3 hi = -lo;
-
- for (int p = beginIndex; p <= endIndex; ++p) {
- lo = lo.min(point[p]->bounds.low());
- hi = hi.max(point[p]->bounds.high());
- }
-
- return AABox(lo, hi);
- }
-
- /** Compares centers */
- class CenterComparator {
- public:
- Vector3::Axis sortAxis;
-
- CenterComparator(Vector3::Axis a) : sortAxis(a) {}
-
- inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const {
- float a = A->center[sortAxis];
- float b = B->center[sortAxis];
-
- if (a < b) {
- return 1;
- } else if (a > b) {
- return -1;
- } else {
- return 0;
- }
- }
- };
-
- /** Compares bounds for strict >, <, or overlap*/
- class BoundsComparator {
- public:
- Vector3::Axis sortAxis;
-
- BoundsComparator(Vector3::Axis a) : sortAxis(a) {}
-
- inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const {
- const AABox& a = A->bounds;
- const AABox& b = B->bounds;
-
- if (a.high()[sortAxis] < b.low()[sortAxis]) {
- return 1;
- } else if (a.low()[sortAxis] > b.high()[sortAxis]) {
- return -1;
- } else {
- return 0;
- }
- }
- };
-
- /** Compares bounds to the sort location */
- class Comparator {
- public:
- Vector3::Axis sortAxis;
- float sortLocation;
-
- Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {}
-
- inline int operator()(_AABSPTree::Handle<T>* /*ignore*/, const _AABSPTree::Handle<T>* handle) const {
- const AABox& box = handle->bounds;
- debugAssert(ignore == NULL);
-
- if (box.high()[sortAxis] < sortLocation) {
- // Box is strictly below the sort location
- return -1;
- } else if (box.low()[sortAxis] > sortLocation) {
- // Box is strictly above the sort location
- return 1;
- } else {
- // Box overlaps the sort location
- return 0;
- }
- }
- };
-
- // Using System::malloc with this class provided no speed improvement.
- class Node {
- public:
-
- /** Spatial bounds on all values at this node and its children, based purely on
- the parent's splitting planes. May be infinite. */
- AABox splitBounds;
-
- Vector3::Axis splitAxis;
-
- /** Location along the specified axis */
- float splitLocation;
-
- /** child[0] contains all values strictly
- smaller than splitLocation along splitAxis.
-
- child[1] contains all values strictly
- larger.
-
- Both may be NULL if there are not enough
- values to bother recursing.
- */
- Node* child[2];
-
- /** Array of values at this node (i.e., values
- straddling the split plane + all values if
- this is a leaf node).
-
- This is an array of pointers because that minimizes
- data movement during tree building, which accounts
- for about 15% of the time cost of tree building.
- */
- Array<_AABSPTree::Handle<T> * > valueArray;
-
- /** For each object in the value array, a copy of its bounds.
- Packing these into an array at the node level
- instead putting them in the valueArray improves
- cache coherence, which is about a 3x performance
- increase when performing intersection computations.
- */
- Array<AABox> boundsArray;
-
- /** Creates node with NULL children */
- Node() {
- splitAxis = Vector3::X_AXIS;
- splitLocation = 0;
- splitBounds = AABox(-Vector3::inf(), Vector3::inf());
- for (int i = 0; i < 2; ++i) {
- child[i] = NULL;
- }
- }
-
- /**
- Doesn't clone children.
- */
- Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) {
- splitAxis = other.splitAxis;
- splitLocation = other.splitLocation;
- splitBounds = other.splitBounds;
- for (int i = 0; i < 2; ++i) {
- child[i] = NULL;
- }
- }
-
- /** Copies the specified subarray of pt into point, NULLs the children.
- Assumes a second pass will set splitBounds. */
- Node(const Array<_AABSPTree::Handle<T> * >& pt) : valueArray(pt) {
- splitAxis = Vector3::X_AXIS;
- splitLocation = 0;
- for (int i = 0; i < 2; ++i) {
- child[i] = NULL;
- }
-
- boundsArray.resize(valueArray.size());
- for (int i = 0; i < valueArray.size(); ++i) {
- boundsArray[i] = valueArray[i]->bounds;
- }
- }
-
- /** Deletes the children (but not the values) */
- ~Node() {
- for (int i = 0; i < 2; ++i) {
- delete child[i];
- }
- }
-
- /** Returns true if this node is a leaf (no children) */
- inline bool isLeaf() const {
- return (child[0] == NULL) && (child[1] == NULL);
- }
-
- /**
- Recursively appends all handles and children's handles
- to the array.
- */
- void getHandles(Array<_AABSPTree::Handle<T> * >& handleArray) const {
- handleArray.append(valueArray);
- for (int i = 0; i < 2; ++i) {
- if (child[i] != NULL) {
- child[i]->getHandles(handleArray);
- }
- }
- }
-
- void verifyNode(const Vector3& lo, const Vector3& hi) {
- // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
- // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
-
- debugAssert(lo == splitBounds.low());
- debugAssert(hi == splitBounds.high());
-
- for (int i = 0; i < valueArray.length(); ++i) {
- const AABox& b = valueArray[i]->bounds;
- debugAssert(b == boundsArray[i]);
-
- for (int axis = 0; axis < 3; ++axis) {
- debugAssert(b.low()[axis] <= b.high()[axis]);
- debugAssert(b.low()[axis] >= lo[axis]);
- debugAssert(b.high()[axis] <= hi[axis]);
- }
- }
-
- if (child[0] || child[1]) {
- debugAssert(lo[splitAxis] < splitLocation);
- debugAssert(hi[splitAxis] > splitLocation);
- }
-
- Vector3 newLo = lo;
- newLo[splitAxis] = splitLocation;
- Vector3 newHi = hi;
- newHi[splitAxis] = splitLocation;
-
- if (child[0] != NULL) {
- child[0]->verifyNode(lo, newHi);
- }
-
- if (child[1] != NULL) {
- child[1]->verifyNode(newLo, hi);
- }
- }
-
-#if 0
- /**
- Stores the locations of the splitting planes (the structure but not the content)
- so that the tree can be quickly rebuilt from a previous configuration without
- calling balance.
- */
- static void serializeStructure(const Node* n, BinaryOutput& bo) {
- if (n == NULL) {
- bo.writeUInt8(0);
- } else {
- bo.writeUInt8(1);
- n->splitBounds.serialize(bo);
- serialize(n->splitAxis, bo);
- bo.writeFloat32(n->splitLocation);
- for (int c = 0; c < 2; ++c) {
- serializeStructure(n->child[c], bo);
- }
- }
- }
-
- /** Clears the member table */
- static Node* deserializeStructure(BinaryInput& bi) {
- if (bi.readUInt8() == 0) {
- return NULL;
- } else {
- Node* n = new Node();
- n->splitBounds.deserialize(bi);
- deserialize(n->splitAxis, bi);
- n->splitLocation = bi.readFloat32();
- for (int c = 0; c < 2; ++c) {
- n->child[c] = deserializeStructure(bi);
- }
- }
- }
-#endif
- /** Returns the deepest node that completely contains bounds. */
- Node* findDeepestContainingNode(const AABox& bounds) {
-
- // See which side of the splitting plane the bounds are on
- if (bounds.high()[splitAxis] < splitLocation) {
- // Bounds are on the low side. Recurse into the child
- // if it exists.
- if (child[0] != NULL) {
- return child[0]->findDeepestContainingNode(bounds);
- }
- } else if (bounds.low()[splitAxis] > splitLocation) {
- // Bounds are on the high side, recurse into the child
- // if it exists.
- if (child[1] != NULL) {
- return child[1]->findDeepestContainingNode(bounds);
- }
- }
-
- // There was no containing child, so this node is the
- // deepest containing node.
- return this;
- }
-
- /** Appends all members that intersect the box.
- If useSphere is true, members that pass the box test
- face a second test against the sphere. */
- void getIntersectingMembers(
- const AABox& box,
- const Sphere& sphere,
- Array<T>& members,
- bool useSphere) const {
-
- // Test all values at this node
- for (int v = 0; v < boundsArray.size(); ++v) {
- const AABox& bounds = boundsArray[v];
- if (bounds.intersects(box) &&
- (! useSphere || bounds.intersects(sphere))) {
- members.append(valueArray[v]->value);
- }
- }
-
- // If the left child overlaps the box, recurse into it
- if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
- child[0]->getIntersectingMembers(box, sphere, members, useSphere);
- }
-
- // If the right child overlaps the box, recurse into it
- if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
- child[1]->getIntersectingMembers(box, sphere, members, useSphere);
- }
- }
-
- /**
- Recurse through the tree, assigning splitBounds fields.
- */
- void assignSplitBounds(const AABox& myBounds) {
- splitBounds = myBounds;
-
- AABox childBounds[2];
- myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
-
-# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
- // Verify the split
- for (int v = 0; v < boundsArray.size(); ++v) {
- const AABox& bounds = boundsArray[v];
- debugAssert(myBounds.contains(bounds));
- }
-# endif
-
- for (int c = 0; c < 2; ++c) {
- if (child[c]) {
- child[c]->assignSplitBounds(childBounds[c]);
- }
- }
- }
-
- /** Returns true if the ray intersects this node */
- bool intersects(const Ray& ray, float distance) const {
- // See if the ray will ever hit this node or its children
- Vector3 location;
- bool alreadyInsideBounds = false;
- bool rayWillHitBounds =
- VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
- ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds);
-
- bool canHitThisNode = (alreadyInsideBounds ||
- (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
-
- return canHitThisNode;
- }
-
- template<typename RayCallback>
- void intersectRay(
- const Ray& ray,
- RayCallback& intersectCallback,
- float& distance,
- 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 (int v = 0; v < valueArray.size(); ++v) {
- bool canHitThisObject = true;
-
- if (! intersectCallbackIsFast) {
- // See if
- Vector3 location;
- const AABox& bounds = boundsArray[v];
- bool alreadyInsideBounds = false;
- bool rayWillHitBounds =
- VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
- ray.origin, ray.direction, bounds, location, alreadyInsideBounds);
-
- canHitThisObject = (alreadyInsideBounds ||
- (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
- }
-
- if (canHitThisObject) {
- // It is possible that this ray hits this object. Look for the intersection using the
- // callback.
- const T& value = valueArray[v]->value;
- intersectCallback(ray, value, 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[splitAxis] < splitLocation) {
-
- // The ray starts on the small side
- firstChild = 0;
-
- if (ray.direction[splitAxis] > 0) {
- // The ray will eventually reach the other side
- secondChild = 1;
- }
-
- } else if (ray.origin[splitAxis] > splitLocation) {
-
- // The ray starts on the large side
- firstChild = 1;
-
- if (ray.direction[splitAxis] < 0) {
- secondChild = 0;
- }
- } else {
- // The ray starts on the splitting plane
- if (ray.direction[splitAxis] < 0) {
- // ...and goes to the small side
- firstChild = 0;
- } else if (ray.direction[splitAxis] > 0) {
- // ...and goes to the large side
- firstChild = 1;
- }
- }
-
- // Test on the side closer to the ray origin.
- if ((firstChild != NONE) && child[firstChild]) {
- child[firstChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
- if(pStopAtFirstHit && distance < enterDistance)
- return;
- }
-
- if (ray.direction[splitAxis] != 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 = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis];
- 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) && child[secondChild]) {
- child[secondChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
- }
-
- }
- };
-
- /**
- Recursively subdivides the subarray.
-
- Clears the source array as soon as it is no longer needed.
-
- Call assignSplitBounds() on the root node after making a tree.
- */
- Node* makeNode(
- Array<_AABSPTree::Handle<T> * >& source,
- int valuesPerNode,
- int numMeanSplits,
- Array<_AABSPTree::Handle<T> * >& temp) {
-
- Node* node = NULL;
-
- if (source.size() <= valuesPerNode) {
- // Make a new leaf node
- node = new Node(source);
-
- // Set the pointers in the memberTable
- for (int i = 0; i < source.size(); ++i) {
- memberTable.set(Member(source[i]), node);
- }
- source.clear();
-
- } else {
- // Make a new internal node
- node = new Node();
-
- const AABox bounds = computeBounds(source, 0, source.size() - 1);
- const Vector3 extent = bounds.high() - bounds.low();
-
- Vector3::Axis splitAxis = extent.primaryAxis();
-
- float splitLocation;
-
- // Arrays for holding the children
- Array<_AABSPTree::Handle<T> * > lt, gt;
-
- if (numMeanSplits <= 0) {
-
- source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis));
-
- // Choose the split location to be the center of whatever fell in the center
- splitLocation = node->valueArray[0]->center[splitAxis];
-
- // Some of the elements in the lt or gt array might really overlap the split location.
- // Move them as needed.
- for (int i = 0; i < lt.size(); ++i) {
- const AABox& bounds = lt[i]->bounds;
- if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
- node->valueArray.append(lt[i]);
- // Remove this element and process the new one that
- // is swapped in in its place.
- lt.fastRemove(i); --i;
- }
- }
-
- for (int i = 0; i < gt.size(); ++i) {
- const AABox& bounds = gt[i]->bounds;
- if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
- node->valueArray.append(gt[i]);
- // Remove this element and process the new one that
- // is swapped in in its place.
- gt.fastRemove(i); --i;
- }
- }
-
- if ((node->valueArray.size() > (source.size() / 2)) &&
- (source.size() > 6)) {
- // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the
- // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean,
- // or geometric mean). This implementation falls back on the extents mean, since that case is already handled
- // below.
- numMeanSplits = 1;
- }
- }
-
- // Note: numMeanSplits may have been increased by the code in the previous case above in order to
- // force a re-partition.
-
- if (numMeanSplits > 0) {
- // Split along the mean
- splitLocation = (bounds.high()[splitAxis] +
- bounds.low()[splitAxis]) / 2.0;
-
- source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation));
-
- // The Comparator ensures that elements are strictly on the correct side of the split
- }
-
-# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
- debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size());
- // Verify that all objects ended up on the correct side of the split.
- // (i.e., make sure that the Array partition was correct)
- for (int i = 0; i < lt.size(); ++i) {
- const AABox& bounds = lt[i]->bounds;
- debugAssert(bounds.high()[splitAxis] < splitLocation);
- }
-
- for (int i = 0; i < gt.size(); ++i) {
- const AABox& bounds = gt[i]->bounds;
- debugAssert(bounds.low()[splitAxis] > splitLocation);
- }
-
- for (int i = 0; i < node->valueArray.size(); ++i) {
- const AABox& bounds = node->valueArray[i]->bounds;
- debugAssert(bounds.high()[splitAxis] >= splitLocation);
- debugAssert(bounds.low()[splitAxis] <= splitLocation);
- }
-# endif
-
- // The source array is no longer needed
- source.clear();
-
- node->splitAxis = splitAxis;
- node->splitLocation = splitLocation;
-
- // Update the bounds array and member table
- node->boundsArray.resize(node->valueArray.size());
- for (int i = 0; i < node->valueArray.size(); ++i) {
- _AABSPTree::Handle<T> * v = node->valueArray[i];
- node->boundsArray[i] = v->bounds;
- memberTable.set(Member(v), node);
- }
-
- if (lt.size() > 0) {
- node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp);
- }
-
- if (gt.size() > 0) {
- node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp);
- }
-
- }
-
- return node;
- }
-
- /**
- Recursively clone the passed in node tree, setting
- pointers for members in the memberTable as appropriate.
- called by the assignment operator.
- */
- Node* cloneTree(Node* src) {
- Node* dst = new Node(*src);
-
- // Make back pointers
- for (int i = 0; i < dst->valueArray.size(); ++i) {
- memberTable.set(Member(dst->valueArray[i]), dst);
- }
-
- // Clone children
- for (int i = 0; i < 2; ++i) {
- if (src->child[i] != NULL) {
- dst->child[i] = cloneTree(src->child[i]);
- }
- }
-
- return dst;
- }
-
- /**
- Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but
- stores only Handle* internally to avoid memory copies.
- */
- typedef _internal::Indirector<_AABSPTree::Handle<T> > Member;
-
- typedef Table<Member, Node*> MemberTable;
-
- /** Maps members to the node containing them */
- MemberTable memberTable;
-
- Node* root;
-
-public:
-
- /** To construct a balanced tree, insert the elements and then call
- AABSPTree::balance(). */
- AABSPTree() : root(NULL) {}
-
- AABSPTree(const AABSPTree& src) : root(NULL) {
- *this = src;
- }
-
- AABSPTree& operator=(const AABSPTree& src) {
- delete root;
- // Clone tree takes care of filling out the memberTable.
- root = cloneTree(src.root);
- return *this;
- }
-
- ~AABSPTree() {
- clear();
- }
-
- /**
- Throws out all elements of the set.
- */
- void clear() {
- typedef typename Table<_internal::Indirector<_AABSPTree::Handle<T> >, Node* >::Iterator It;
-
- // Delete all handles stored in the member table
- It cur = memberTable.begin();
- It end = memberTable.end();
- while (cur != end) {
- delete cur->key.handle;
- cur->key.handle = NULL;
- ++cur;
- }
- memberTable.clear();
-
- // Delete the tree structure itself
- delete root;
- root = NULL;
- }
-
- size_t size() const {
- return memberTable.size();
- }
-
- /**
- Inserts an object into the set if it is not
- already present. O(log n) time. Does not
- cause the tree to be balanced.
- */
- void insert(const T& value) {
- if (contains(value)) {
- // Already in the set
- return;
- }
-
- _AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(value);
-
- if (root == NULL) {
- // This is the first node; create a root node
- root = new Node();
- }
-
- Node* node = root->findDeepestContainingNode(h->bounds);
-
- // Insert into the node
- node->valueArray.append(h);
- node->boundsArray.append(h->bounds);
-
- // Insert into the node table
- Member m(h);
- memberTable.set(m, node);
- }
-
- /** Inserts each elements in the array in turn. If the tree
- begins empty (no structure and no elements), this is faster
- than inserting each element in turn. You still need to balance
- the tree at the end.*/
- void insert(const Array<T>& valueArray) {
- if (root == NULL) {
- // Optimized case for an empty tree; don't bother
- // searching or reallocating the root node's valueArray
- // as we incrementally insert.
- root = new Node();
- root->valueArray.resize(valueArray.size());
- root->boundsArray.resize(root->valueArray.size());
- for (int i = 0; i < valueArray.size(); ++i) {
- // Insert in opposite order so that we have the exact same
- // data structure as if we inserted each (i.e., order is reversed
- // from array).
- _AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(valueArray[i]);
- int j = valueArray.size() - i - 1;
- root->valueArray[j] = h;
- root->boundsArray[j] = h->bounds;
- memberTable.set(Member(h), root);
- }
-
- } else {
- // Insert at appropriate tree depth.
- for (int i = 0; i < valueArray.size(); ++i) {
- insert(valueArray[i]);
- }
- }
- }
-
- /**
- Returns true if this object is in the set, otherwise
- returns false. O(1) time.
- */
- bool contains(const T& value) {
- // Temporarily create a handle and member
- _AABSPTree::Handle<T> h(value);
- return memberTable.containsKey(Member(&h));
- }
-
- /**
- Removes an object from the set in O(1) time.
- It is an error to remove members that are not already
- present. May unbalance the tree.
-
- Removing an element never causes a node (split plane) to be removed...
- nodes are only changed when the tree is rebalanced. This behavior
- is desirable because it allows the split planes to be serialized,
- and then deserialized into an empty tree which can be repopulated.
- */
- void remove(const T& value) {
- debugAssertM(contains(value),
- "Tried to remove an element from a "
- "AABSPTree that was not present");
-
- // Get the list of elements at the node
- _AABSPTree::Handle<T> h(value);
- Member m(&h);
-
- Array<_AABSPTree::Handle<T> * >& list = memberTable[m]->valueArray;
-
- _AABSPTree::Handle<T>* ptr = NULL;
-
- // Find the element and remove it
- for (int i = list.length() - 1; i >= 0; --i) {
- if (list[i]->value == value) {
- // This was the element. Grab the pointer so that
- // we can delete it below
- ptr = list[i];
-
- // Remove the handle from the node
- list.fastRemove(i);
-
- // Remove the corresponding bounds
- memberTable[m]->boundsArray.fastRemove(i);
- break;
- }
- }
-
- // Remove the member
- memberTable.remove(m);
-
- // Delete the handle data structure
- delete ptr;
- ptr = NULL;
- }
-
- /**
- If the element is in the set, it is removed.
- The element is then inserted.
-
- This is useful when the == and hashCode methods
- on <I>T</I> are independent of the bounds. In
- that case, you may call update(v) to insert an
- element for the first time and call update(v)
- again every time it moves to keep the tree
- up to date.
- */
- void update(const T& value) {
- if (contains(value)) {
- remove(value);
- }
- insert(value);
- }
-
- /**
- Rebalances the tree (slow). Call when objects
- have moved substantially from their original positions
- (which unbalances the tree and causes the spatial
- queries to be slow).
-
- @param valuesPerNode Maximum number of elements to put at
- a node.
-
- @param numMeanSplits numMeanSplits = 0 gives a
- fully axis aligned BSP-tree, where the balance operation attempts to balance
- the tree so that every splitting plane has an equal number of left
- and right children (i.e. it is a <B>median</B> split along that axis).
- This tends to maximize average performance.
-
- You can override this behavior by
- setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
- creates a full oct-tree, which tends to optimize peak performance at the expense of
- average performance. It tends to have better clustering behavior when
- members are not uniformly distributed.
- */
- void balance(int valuesPerNode = 5, int numMeanSplits = 3) {
- if (root == NULL) {
- // Tree is empty
- return;
- }
-
- // Get all handles and delete the old tree structure
- Node* oldRoot = root;
- for (int c = 0; c < 2; ++c) {
- if (root->child[c] != NULL) {
- root->child[c]->getHandles(root->valueArray);
-
- // Delete the child; this will delete all structure below it
- delete root->child[c];
- root->child[c] = NULL;
- }
- }
-
- Array<_AABSPTree::Handle<T> * > temp;
- // Make a new root. Work with a copy of the value array because
- // makeNode clears the source array as it progresses
- Array<_AABSPTree::Handle<T> * > copy(oldRoot->valueArray);
- root = makeNode(copy, valuesPerNode, numMeanSplits, temp);
-
- // Throw away the old root node
- delete oldRoot;
- oldRoot = NULL;
-
- // Walk the tree, assigning splitBounds. We start with unbounded
- // space. This will override the current member table.
- root->assignSplitBounds(AABox::maxFinite());
-
-# ifdef _DEBUG
- // Ensure that the balanced tree is till correct
- root->verifyNode(Vector3::minFinite(), Vector3::maxFinite());
-# endif
- }
-
-protected:
-
- /**
- @param parentMask The mask that this node returned from culledBy.
- */
- static void getIntersectingMembers(
- const Array<Plane>& plane,
- Array<T>& members,
- Node* node,
- uint32 parentMask) {
-
- int dummy;
-
- if (parentMask == 0) {
- // None of these planes can cull anything
- for (int v = node->valueArray.size() - 1; v >= 0; --v) {
- members.append(node->valueArray[v]->value);
- }
-
- // Iterate through child nodes
- for (int c = 0; c < 2; ++c) {
- if (node->child[c]) {
- getIntersectingMembers(plane, members, node->child[c], 0);
- }
- }
- } else {
-
- // Test values at this node against remaining planes
- for (int v = node->boundsArray.size() - 1; v >= 0; --v) {
- if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) {
- members.append(node->valueArray[v]->value);
- }
- }
-
- uint32 childMask = 0xFFFFFF;
-
- // Iterate through child nodes
- for (int c = 0; c < 2; ++c) {
- if (node->child[c] &&
- ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
- // This node was not culled
- getIntersectingMembers(plane, members, node->child[c], childMask);
- }
- }
- }
- }
-
-public:
-
- /**
- Returns all members inside the set of planes.
- @param members The results are appended to this array.
- */
- void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
- if (root == NULL) {
- return;
- }
-
- getIntersectingMembers(plane, members, root, 0xFFFFFF);
- }
-
- /**
- Typically used to find all visible
- objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
- <B>not<B> culled by frustum.
-
- Example:
- <PRE>
- Array<Object*> visible;
- tree.getIntersectingMembers(camera.frustum(), visible);
- // ... Draw all objects in the visible array.
- </PRE>
- @param members The results are appended to this array.
- */
- void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
- Array<Plane> plane;
-
- for (int i = 0; i < frustum.faceArray.size(); ++i) {
- plane.append(frustum.faceArray[i].plane);
- }
-
- getIntersectingMembers(plane, members);
- }
-
- /**
- C++ STL style iterator variable. See beginBoxIntersection().
- The iterator overloads the -> (dereference) operator, so this
- acts like a pointer to the current member.
- */
- // This iterator turns Node::getIntersectingMembers into a
- // coroutine. It first translates that method from recursive to
- // stack based, then captures the system state (analogous to a Scheme
- // continuation) after each element is appended to the member array,
- // and allowing the computation to be restarted.
- class BoxIntersectionIterator {
- private:
- friend class AABSPTree<T>;
-
- /** True if this is the "end" iterator instance */
- bool isEnd;
-
- /** The box that we're testing against. */
- AABox box;
-
- /** Node that we're currently looking at. Undefined if isEnd
- is true. */
- Node* node;
-
- /** Nodes waiting to be processed */
- // We could use backpointers within the tree and careful
- // state management to avoid ever storing the stack-- but
- // it is much easier this way and only inefficient if the
- // caller uses post increment (which they shouldn't!).
- Array<Node*> stack;
-
- /** The next index of current->valueArray to return.
- Undefined when isEnd is true.*/
- int nextValueArrayIndex;
-
- BoxIntersectionIterator() : isEnd(true) {}
-
- BoxIntersectionIterator(const AABox& b, const Node* root) :
- isEnd(root == NULL), box(b),
- node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
-
- // We intentionally start at the "-1" index of the current
- // node so we can use the preincrement operator to move
- // ourselves to element 0 instead of repeating all of the
- // code from the preincrement method. Note that this might
- // cause us to become the "end" instance.
- ++(*this);
- }
-
- public:
-
- inline bool operator!=(const BoxIntersectionIterator& other) const {
- return ! (*this == other);
- }
-
- bool operator==(const BoxIntersectionIterator& other) const {
- if (isEnd) {
- return other.isEnd;
- } else if (other.isEnd) {
- return false;
- } else {
- // Two non-end iterators; see if they match. This is kind of
- // silly; users shouldn't call == on iterators in general unless
- // one of them is the end iterator.
- if ((box != other.box) || (node != other.node) ||
- (nextValueArrayIndex != other.nextValueArrayIndex) ||
- (stack.length() != other.stack.length())) {
- return false;
- }
-
- // See if the stacks are the same
- for (int i = 0; i < stack.length(); ++i) {
- if (stack[i] != other.stack[i]) {
- return false;
- }
- }
-
- // We failed to find a difference; they must be the same
- return true;
- }
- }
-
- /**
- Pre increment.
- */
- BoxIntersectionIterator& operator++() {
- ++nextValueArrayIndex;
-
- bool foundIntersection = false;
- while (! isEnd && ! foundIntersection) {
-
- // Search for the next node if we've exhausted this one
- while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
- // If we entered this loop, then the iterator has exhausted the elements at
- // node (possibly because it just switched to a child node with no members).
- // This loop continues until it finds a node with members or reaches
- // the end of the whole intersection search.
-
- // If the right child overlaps the box, push it onto the stack for
- // processing.
- if ((node->child[1] != NULL) &&
- (box.high()[node->splitAxis] > node->splitLocation)) {
- stack.push(node->child[1]);
- }
-
- // If the left child overlaps the box, push it onto the stack for
- // processing.
- if ((node->child[0] != NULL) &&
- (box.low()[node->splitAxis] < node->splitLocation)) {
- stack.push(node->child[0]);
- }
-
- if (stack.length() > 0) {
- // Go on to the next node (which may be either one of the ones we
- // just pushed, or one from farther back the tree).
- node = stack.pop();
- nextValueArrayIndex = 0;
- } else {
- // That was the last node; we're done iterating
- isEnd = true;
- }
- }
-
- // Search for the next intersection at this node until we run out of children
- while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
- if (box.intersects(node->boundsArray[nextValueArrayIndex])) {
- foundIntersection = true;
- } else {
- ++nextValueArrayIndex;
- // If we exhaust this node, we'll loop around the master loop
- // to find a new node.
- }
- }
- }
-
- return *this;
- }
-
- private:
- /**
- Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code.
- */
- BoxIntersectionIterator operator++(int);
- /*{
- BoxIntersectionIterator old = *this;
- ++this;
- return old;
- }*/
-
- public:
-
- /** Overloaded dereference operator so the iterator can masquerade as a pointer
- to a member */
- const T& operator*() const {
- alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
- return node->valueArray[nextValueArrayIndex]->value;
- }
-
- /** Overloaded dereference operator so the iterator can masquerade as a pointer
- to a member */
- T const * operator->() const {
- alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
- return &(stack.last()->valueArray[nextValueArrayIndex]->value);
- }
-
- /** Overloaded cast operator so the iterator can masquerade as a pointer
- to a member */
- operator T*() const {
- alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
- return &(stack.last()->valueArray[nextValueArrayIndex]->value);
- }
- };
-
- /**
- Iterates through the members that intersect the box
- */
- BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
- return BoxIntersectionIterator(box, root);
- }
-
- BoxIntersectionIterator endBoxIntersection() const {
- // The "end" iterator instance
- return BoxIntersectionIterator();
- }
-
- /**
- Appends all members whose bounds intersect the box.
- See also AABSPTree::beginBoxIntersection.
- */
- void getIntersectingMembers(const AABox& box, Array<T>& members) const {
- if (root == NULL) {
- return;
- }
- root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
- }
-
- /**
- Invoke a callback for every member along a ray until the closest intersection is found.
-
- @param callback either a function or an instance of a class with an overloaded operator() of the form:
-
- <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object
- before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to
- the intersection, otherwise leaves it unmodified. A common example is:
-
- <pre>
- class Entity {
- public:
-
- void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) {
- float d = maxDist;
-
- // ... search for intersection distance d
-
- if ((d > 0) && (d < maxDist)) {
- // Intersection occured
- maxDist = d;
- outLocation = ...;
- outNormal = ...;
- }
- }
- };
-
- // Finds the surface normal and location of the first intersection with the scene
- class Intersection {
- public:
- Entity* closestEntity;
- Vector3 hitLocation;
- Vector3 hitNormal;
-
- void operator()(const Ray& ray, const Entity* entity, float& distance) {
- entity->intersect(ray, distance, hitLocation, hitNormal);
- }
- };
-
- AABSPTree<Entity*> scene;
-
- Intersection intersection;
- float distance = inf();
- scene.intersectRay(camera.worldRay(x, y), intersection, distance);
- </pre>
-
- @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection.
- On return, this is set to the distance to the first intersection encountered.
-
- @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked.
- If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true.
- */
- template<typename RayCallback>
- void intersectRay(
- const Ray& ray,
- RayCallback& intersectCallback,
- float& distance,
- bool pStopAtFirstHit,
- bool intersectCallbackIsFast = false) const {
-
- root->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
-
- }
-
- /**
- @param members The results are appended to this array.
- */
- void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
- if (root == NULL) {
- return;
- }
-
- AABox box;
- sphere.getBounds(box);
- root->getIntersectingMembers(box, sphere, members, true);
-
- }
-#if 0
- /**
- Stores the locations of the splitting planes (the structure but not the content)
- so that the tree can be quickly rebuilt from a previous configuration without
- calling balance.
- */
- void serializeStructure(BinaryOutput& bo) const {
- Node::serializeStructure(root, bo);
- }
-
- /** Clears the member table */
- void deserializeStructure(BinaryInput& bi) {
- clear();
- root = Node::deserializeStructure(bi);
- }
-#endif
- /**
- Returns an array of all members of the set. See also AABSPTree::begin.
- */
- void getMembers(Array<T>& members) const {
- Array<Member> temp;
- memberTable.getKeys(temp);
- for (int i = 0; i < temp.size(); ++i) {
- members.append(temp[i].handle->value);
- }
- }
-
- /**
- C++ STL style iterator variable. See begin().
- Overloads the -> (dereference) operator, so this acts like a pointer
- to the current member.
- */
- class Iterator {
- private:
- friend class AABSPTree<T>;
-
- // Note: this is a Table iterator, we are currently defining
- // Set iterator
- typename Table<Member, Node*>::Iterator it;
-
- Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {}
-
- public:
-
- inline bool operator!=(const Iterator& other) const {
- return !(*this == other);
- }
-
- bool operator==(const Iterator& other) const {
- return it == other.it;
- }
-
- /**
- Pre increment.
- */
- Iterator& operator++() {
- ++it;
- return *this;
- }
-
- private:
- /**
- Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code.
- */
- Iterator operator++(int);/* {
- Iterator old = *this;
- ++(*this);
- return old;
- }*/
- public:
-
- const T& operator*() const {
- return it->key.handle->value;
- }
-
- T* operator->() const {
- return &(it->key.handle->value);
- }
-
- operator T*() const {
- return &(it->key.handle->value);
- }
- };
-
- /**
- C++ STL style iterator method. Returns the first member.
- Use preincrement (++entry) to get to the next element (iteration
- order is arbitrary).
- Do not modify the set while iterating.
- */
- Iterator begin() const {
- return Iterator(memberTable.begin());
- }
-
- /**
- C++ STL style iterator method. Returns one after the last iterator
- element.
- */
- Iterator end() const {
- return Iterator(memberTable.end());
- }
-};
-
-}
-
-#endif
-
-
diff --git a/src/shared/vmap/BIH.cpp b/src/shared/vmap/BIH.cpp
new file mode 100644
index 00000000000..4bd6b3c701e
--- /dev/null
+++ b/src/shared/vmap/BIH.cpp
@@ -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));
+}
diff --git a/src/shared/vmap/BIH.h b/src/shared/vmap/BIH.h
new file mode 100644
index 00000000000..15ae90c23eb
--- /dev/null
+++ b/src/shared/vmap/BIH.h
@@ -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
diff --git a/src/shared/vmap/BaseModel.cpp b/src/shared/vmap/BaseModel.cpp
deleted file mode 100644
index 14c42c8039f..00000000000
--- a/src/shared/vmap/BaseModel.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/BaseModel.h b/src/shared/vmap/BaseModel.h
deleted file mode 100644
index 806bb2c365b..00000000000
--- a/src/shared/vmap/BaseModel.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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_*/
-
diff --git a/src/shared/vmap/CMakeLists.txt b/src/shared/vmap/CMakeLists.txt
index 76ba24e55d1..898e0d6c5f0 100644
--- a/src/shared/vmap/CMakeLists.txt
+++ b/src/shared/vmap/CMakeLists.txt
@@ -2,38 +2,28 @@
########### next target ###############
SET(vmaps_STAT_SRCS
- 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
+ VMapManager2.cpp
+ VMapManager2.h
VMapTools.h
+ WorldModel.cpp
+ WorldModel.h
)
include_directories(
+ ${CMAKE_SOURCE_DIR}/src/framework
${CMAKE_SOURCE_DIR}/dep/include/g3dlite
)
add_library(vmaps STATIC ${vmaps_STAT_SRCS})
-
diff --git a/src/shared/vmap/CoordModelMapping.cpp b/src/shared/vmap/CoordModelMapping.cpp
deleted file mode 100644
index 20fe733c48e..00000000000
--- a/src/shared/vmap/CoordModelMapping.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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;
- }
-
- //=================================================================
-
-}
-
diff --git a/src/shared/vmap/CoordModelMapping.h b/src/shared/vmap/CoordModelMapping.h
deleted file mode 100644
index 641f4d17f37..00000000000
--- a/src/shared/vmap/CoordModelMapping.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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 <cstdio>
-#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);
- }
- }
- inline bool isWorldAreaMap(unsigned int pMapId) { return(iWorldAreaGroups.contains(pMapId)); }
- void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
-
- };
-}
-#endif /*_COORDMODELMAPPING_H_*/
-
diff --git a/src/shared/vmap/DebugCmdLogger.cpp b/src/shared/vmap/DebugCmdLogger.cpp
deleted file mode 100644
index a0a40612f16..00000000000
--- a/src/shared/vmap/DebugCmdLogger.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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 <cstdio>
-
-#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);
- }
- //========================================================
-}
-
diff --git a/src/shared/vmap/DebugCmdLogger.h b/src/shared/vmap/DebugCmdLogger.h
deleted file mode 100644
index 775e529c55e..00000000000
--- a/src/shared/vmap/DebugCmdLogger.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/IVMapManager.h b/src/shared/vmap/IVMapManager.h
index 81911f74a7a..00629eb122c 100644
--- a/src/shared/vmap/IVMapManager.h
+++ b/src/shared/vmap/IVMapManager.h
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,19 +8,19 @@
*
* 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
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _IVMAPMANAGER_H
#define _IVMAPMANAGER_H
#include<string>
-#include "VMapDefinitions.h"
+#include <Platform/Define.h>
//===========================================================
@@ -63,7 +61,7 @@ namespace VMAP
virtual void unloadMap(unsigned int pMapId) = 0;
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0;
- virtual float getHeight(unsigned int pMapId, float x, float y, float z, float ray_lenght = MAX_CAN_FALL_DISTANCE) = 0;
+ virtual float getHeight(unsigned int pMapId, float x, float y, float z) = 0;
/**
test if we hit an object. return true if we hit one. rx,ry,rz will hold the hit position or the dest position, if no intersection was found
return a position, that is pReduceDist closer to the origin
@@ -96,8 +94,13 @@ 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;
};
}
#endif
-
diff --git a/src/shared/vmap/ManagedModelContainer.cpp b/src/shared/vmap/ManagedModelContainer.cpp
deleted file mode 100644
index dce666fb288..00000000000
--- a/src/shared/vmap/ManagedModelContainer.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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)
- {
- }
-
-}
-
diff --git a/src/shared/vmap/ManagedModelContainer.h b/src/shared/vmap/ManagedModelContainer.h
deleted file mode 100644
index e1ad2664fe8..00000000000
--- a/src/shared/vmap/ManagedModelContainer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/MapTree.cpp b/src/shared/vmap/MapTree.cpp
new file mode 100644
index 00000000000..8c77ee109f3
--- /dev/null
+++ b/src/shared/vmap/MapTree.cpp
@@ -0,0 +1,450 @@
+/*
+ * 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>
+
+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
+ std::cout << "trying to intersect '" << prims[entry].name << "'\n";
+#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
+ std::cout << "trying to intersect '" << prims[entry].name << "'\n";
+#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), /* iTree(0), */ 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();
+ // 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();
+ // 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
+ fclose(tf);
+ }
+ fclose(rf);
+ return success;
+ }
+
+ //=========================================================
+
+ bool StaticMapTree::InitMap(const std::string &fname, VMapManager2 *vm)
+ {
+ std::cout << "Initializing StaticMapTree '" << fname << "'\n";
+ 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
+ std::cout << "Map isTiled:" << bool(iIsTiled) << std::endl;
+#endif
+ if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
+ {
+ WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
+ std::cout << "StaticMapTree::InitMap(): loading " << spawn.name << std::endl;
+ 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;
+ std::cout << "error: could not acquire WorldModel pointer!\n";
+ }
+ }
+
+ 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)
+ {
+ std::cout << "Tree has not been initialized!\n";
+ return false;
+ }
+ bool result = true;
+
+ std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
+ FILE* tf = fopen(tilefile.c_str(), "rb");
+ if (tf)
+ {
+ 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)
+ {
+ // acquire model instance
+ WorldModel *model = vm->acquireModelInstance(iBasePath, spawn.name);
+ if (!model) std::cout << "error: could not acquire WorldModel pointer!\n";
+
+ // update tree
+ uint32 referencedVal;
+
+ fread(&referencedVal, sizeof(uint32), 1, tf);
+ if (!iLoadedSpawns.count(referencedVal))
+ {
+#ifdef VMAP_DEBUG
+ if (referencedVal > iNTreeValues)
+ {
+ std::cout << "invalid tree element! (" << referencedVal << "/" << iNTreeValues << ")\n";
+ continue;
+ }
+#endif
+ iTreeValues[referencedVal] = ModelInstance(spawn, model);
+ iLoadedSpawns[referencedVal] = 1;
+ }
+ else
+ {
+ ++iLoadedSpawns[referencedVal];
+#ifdef VMAP_DEBUG
+ if (iTreeValues[referencedVal].ID != spawn.ID) std::cout << "error: trying to load wrong spawn in node!\n";
+ else if (iTreeValues[referencedVal].name != spawn.name) std::cout << "error: name collision on GUID="<< spawn.ID << "\n";
+#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())
+ {
+ std::cout << "WARNING: trying to unload non-loaded tile. Map:" << iMapID << " X:" << tileX << " Y:" << tileY << std::endl;
+ 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;
+ 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))
+ {
+ std::cout << "error! trying to unload non-referenced model '" << spawn.name << "' (ID:" << spawn.ID << ")\n";
+ }
+ else if (--iLoadedSpawns[referencedNode] == 0)
+ {
+ //std::cout << "MapTree: removing '" << spawn.name << "' from tree\n";
+ iTreeValues[referencedNode].setUnloaded();
+ iLoadedSpawns.erase(referencedNode);
+ }
+ }
+ }
+ fclose(tf);
+ }
+ }
+ iLoadedTiles.erase(tile);
+ }
+
+}
diff --git a/src/shared/vmap/MapTree.h b/src/shared/vmap/MapTree.h
new file mode 100644
index 00000000000..7955cb92d68
--- /dev/null
+++ b/src/shared/vmap/MapTree.h
@@ -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
diff --git a/src/shared/vmap/ModelContainer.cpp b/src/shared/vmap/ModelContainer.cpp
deleted file mode 100644
index 98c10789901..00000000000
--- a/src/shared/vmap/ModelContainer.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/ModelContainer.h b/src/shared/vmap/ModelContainer.h
deleted file mode 100644
index 5b3eeb20bb2..00000000000
--- a/src/shared/vmap/ModelContainer.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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;
-
- ModelContainer (const ModelContainer& c): BaseModel(c) {}
- 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 const 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
-
diff --git a/src/shared/vmap/ModelInstance.cpp b/src/shared/vmap/ModelInstance.cpp
new file mode 100644
index 00000000000..677a08e147a
--- /dev/null
+++ b/src/shared/vmap/ModelInstance.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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"
+
+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)
+ {
+ //std::cout << "<object not loaded>\n";
+ return false;
+ }
+ float time = pRay.intersectionTime(iBound);
+ if (time == G3D::inf())
+ {
+// std::cout << "Ray does not hit '" << name << "'\n";
+
+ return false;
+ }
+// std::cout << "Ray crosses bound of '" << name << "'\n";
+/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
+ << " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
+ << " t/tmax:" << time << "/" << pMaxDist;
+ std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
+ << iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
+ // 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
+ std::cout << "<object not loaded>\n";
+#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
+ std::cout << "<object not loaded>\n";
+#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))
+ std::cout << "Error reading ModelSpawn!\n";
+ 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))
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ return false;
+ }
+ char nameBuff[500];
+ if (nameLen>500) // file names should never be that long, must be file error
+ {
+ std::cout << "Error reading ModelSpawn, file name too long!\n";
+ return false;
+ }
+ check = fread(nameBuff, sizeof(char), nameLen, rf);
+ if (check != nameLen)
+ {
+ std::cout << "Error reading ModelSpawn!\n";
+ 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;
+ }
+
+}
diff --git a/src/shared/vmap/ModelInstance.h b/src/shared/vmap/ModelInstance.h
new file mode 100644
index 00000000000..97b3ab632a1
--- /dev/null
+++ b/src/shared/vmap/ModelInstance.h
@@ -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
diff --git a/src/shared/vmap/NodeValueAccess.h b/src/shared/vmap/NodeValueAccess.h
deleted file mode 100644
index 50a03480fe3..00000000000
--- a/src/shared/vmap/NodeValueAccess.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/ShortBox.h b/src/shared/vmap/ShortBox.h
deleted file mode 100644
index bf451c6a58e..00000000000
--- a/src/shared/vmap/ShortBox.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/ShortVector.h b/src/shared/vmap/ShortVector.h
deleted file mode 100644
index 8093c3904c3..00000000000
--- a/src/shared/vmap/ShortVector.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/SubModel.cpp b/src/shared/vmap/SubModel.cpp
deleted file mode 100644
index 1f72fc6826d..00000000000
--- a/src/shared/vmap/SubModel.cpp
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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);
- }
- }
- //==========================================================
-
-}
-
diff --git a/src/shared/vmap/SubModel.h b/src/shared/vmap/SubModel.h
deleted file mode 100644
index 206f722ca61..00000000000
--- a/src/shared/vmap/SubModel.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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
-
diff --git a/src/shared/vmap/TileAssembler.cpp b/src/shared/vmap/TileAssembler.cpp
index a79f609fa49..d01b54a7564 100644
--- a/src/shared/vmap/TileAssembler.cpp
+++ b/src/shared/vmap/TileAssembler.cpp
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,44 +8,50 @@
*
* 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
+ * 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
+ * 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)
@@ -57,320 +61,300 @@ 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::set<std::string> spawnedModelFiles;
+ bool success = readMapSpawns();
+ if (!success)
+ return false;
- std::string getModNameFromModPosName(const std::string& pModPosName)
- {
- size_t spos = pModPosName.find_first_of('#');
- std::string modelFileName = pModPosName.substr(0,spos);
- return(modelFileName);
- }
+ // export Map data
+ for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
+ {
+ // build global map tree
+ std::vector<ModelSpawn*> mapSpawns;
+ UniqueEntryMap::iterator entry;
+ for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
+ {
+ // 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)
+ {
+ if (!calculateTransformedBound(entry->second))
+ break;
+ }
+ else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
+ {
+ // 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);
+ }
+ mapSpawns.push_back(&(entry->second));
+ spawnedModelFiles.insert(entry->second.name);
+ }
- //=================================================================
+ 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));
+ if (!modelNodeIdx.empty())
+ printf("min GUID: %u, max GUID: %u\n", modelNodeIdx.begin()->first, modelNodeIdx.rbegin()->first);
+
+ // 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;
+ }
- unsigned int TileAssembler::getUniqueNameId(const std::string pName)
- {
- unsigned int result;
+ //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]);
+ }
- if(!iUniqueNameIds.containsKey(pName))
- {
- ++iCurrentUniqueNameId;
- iUniqueNameIds.set(pName, iCurrentUniqueNameId);
- }
- result = iUniqueNameIds.get(pName);
- return result;
- }
+ fclose(mapfile);
- //=================================================================
+ // <====
- 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);
- }
+ // 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");
+ // 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;
+ }
+ }
- void emptyArray(Array<ModelContainer*>& mc)
- {
- int no=mc.size();
- while(no > 0)
+ //cleanup:
+ for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
{
- --no;
- delete mc[no];
- mc.remove(no);
+ delete map_iter->second;
}
+ return success;
}
- //=================================================================
- bool TileAssembler::convertWorld()
+ bool TileAssembler::readMapSpawns()
{
- #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))
- return false;
-
- Array<unsigned int> mapIds = iCoordModelMapping->getMaps();
- if(mapIds.size() == 0)
+ std::string fname = iSrcDir + "/dir_bin";
+ FILE *dirf = fopen(fname.c_str(), "rb");
+ if (!dirf)
{
- printf("Fatal error: empty map list!\n");
+ printf("Could not read dir_bin file!\n");
return false;
}
-
- for (int i=0; i<mapIds.size(); ++i)
+ printf("Read coordinate mapping...\n");
+ uint32 mapID, tileX, tileY, check=0;
+ G3D::Vector3 v1, v2;
+ ModelSpawn spawn;
+ while (!feof(dirf))
{
- unsigned int mapId = mapIds[i];
+ 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;
- #ifdef _ASSEMBLER_DEBUG
- if(mapId == 0) // "Azeroth" just for debug
+ MapSpawns *current;
+ MapData::iterator map_iter = mapData.find(mapID);
+ if (map_iter == mapData.end())
{
- 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;
- }
- }
+ 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));
}
- #ifdef _ASSEMBLER_DEBUG
- if(::g_df) fclose(::g_df);
- #endif
-
- return true;
+ bool success = (ferror(dirf) == 0);
+ fclose(dirf);
+ return success;
}
- //=================================================================
-
- bool TileAssembler::fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, Array<ModelContainer*>& pMC)
+ bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
{
- ModelContainer* modelContainer;
-
- NameCollection nameCollection = iCoordModelMapping->getFilenamesForCoordinate(pMapId, pXPos, pYPos);
- if(nameCollection.size() == 0)
- return true; // no data...
+ std::string modelFilename = iSrcDir + "/" + spawn.name;
+ ModelPosition modelPosition;
+ modelPosition.iDir = spawn.iRot;
+ modelPosition.iScale = spawn.iScale;
+ modelPosition.init();
- char dirfilename[500];
- sprintf(dirfilename,"%s/%s.vmdir",iDestDir.c_str(),pDirFileName.c_str());
- FILE *dirfile = fopen(dirfilename, "ab");
- if(!dirfile)
+ FILE *rf = fopen(modelFilename.c_str(), "rb");
+ if (!rf)
{
- printf("ERROR: Can't create file %s",dirfilename);
+ printf("ERROR: Can't open model file: %s\n", modelFilename.c_str());
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);
+ AABox modelBound;
+ bool boundEmpty=true;
+ char ident[8];
- Array<std::string> positionarray;
- positionarray.append(nameCollection.iSingeFiles[pos]);
+ int readOperation = 1;
- 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;
- }
+ // 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); }
- fclose(dirfile);
- return true;
- }
+ 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));
- void removeEntriesFromTree(AABSPTree<SubModel *>* pTree)
- {
- Array<SubModel *> submodelArray;
- pTree->getMembers(submodelArray);
- int no = submodelArray.size();
- while(no > 0)
- {
- --no;
- delete submodelArray[no];
- }
- }
+ 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());
- ModelContainer* TileAssembler::processNames(const Array<std::string>& pPositions, const char* pDestFileName)
- {
- ModelContainer *modelContainer = 0;
+ for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
+ {
+ fseek(rf, 3*sizeof(uint32) + 6*sizeof(float), SEEK_CUR);
- Vector3 basepos = Vector3(0,0,0);
- AABSPTree<SubModel *>* mainTree = new AABSPTree<SubModel *>();
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "GRP ");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ fseek(rf, blocksize, SEEK_CUR);
- int pos = 0;
+ // ---- indexes
+ READ_OR_RETURN(&blockId, 4);
+ CMP_OR_RETURN(blockId, "INDX");
+ READ_OR_RETURN(&blocksize, sizeof(int));
+ fseek(rf, blocksize, SEEK_CUR);
- bool result = true;
- while(result && (pos < pPositions.size()))
- {
- std::string modelPosString = pPositions[pos];
- std::string modelFileName = getModNameFromModPosName(modelPosString);
+ // ---- 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(!fillModelIntoTree(mainTree, basepos, modelPosString, modelFileName))
+ if (nvectors >0)
{
- result = false;
- break;
+ 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;
}
- ++pos;
- }
- if(result && mainTree->size() > 0)
- {
- mainTree->balance();
- modelContainer = new ModelContainer(mainTree);
- modelContainer->writeFile(pDestFileName);
- }
- removeEntriesFromTree(mainTree);
- delete mainTree;
+ 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);
- return(modelContainer);
+ 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() );
@@ -379,95 +363,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;
}
@@ -476,127 +439,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));
-
- 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); }
+ uint32 nvectors;
+ READ_OR_RETURN(&nvectors, sizeof(uint32));
- 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);
+ for (uint32 i=0; i<nvectors; ++i)
+ {
+ vertexArray.push_back( Vector3(vectorarray + 3*i) );
+ }
+ delete[] vectorarray;
}
- // ----- liquit
- if(flags & 1)
+ // ----- liquid
+ WmoLiquid *liquid = 0;
+ if (liquidflags& 1)
{
- // we have liquit -> not handled yet ... skip
+ 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)
- {
- ModelPosition modelPosition;
- getModelPosition(pPos, modelPosition);
- // all should be relative to object base position
- modelPosition.moveToBasePos(pBasePos);
- modelPosition.init();
-
- return readRawFile(pModelFilename, modelPosition, pMainTree);
- }
-
- //=================================================================
- 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;
+ // write WorldModel
+ WorldModel model;
+ model.setRootWmoID(RootWMOID);
+ if (groupsArray.size())
+ {
+ model.setGroupModels(groupsArray);
+ success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
+ }
+ //std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
+ return success;
}
- //==========================================
-} // VMAP
+}
diff --git a/src/shared/vmap/TileAssembler.h b/src/shared/vmap/TileAssembler.h
index aa21a93dd1a..b26735708af 100644
--- a/src/shared/vmap/TileAssembler.h
+++ b/src/shared/vmap/TileAssembler.h
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,25 +8,22 @@
*
* 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
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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
{
@@ -41,56 +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);
-
- void init();
- bool convertWorld();
+ bool convertWorld2();
+ bool readMapSpawns();
+ bool calculateTransformedBound(ModelSpawn &spawn);
- 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_*/
-
diff --git a/src/shared/vmap/TreeNode.cpp b/src/shared/vmap/TreeNode.cpp
deleted file mode 100644
index e3edb2fe087..00000000000
--- a/src/shared/vmap/TreeNode.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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);
- }
-
- //=================================================================
- //=================================================================
- //=================================================================
-}
-
diff --git a/src/shared/vmap/TreeNode.h b/src/shared/vmap/TreeNode.h
deleted file mode 100644
index 2aa943573da..00000000000
--- a/src/shared/vmap/TreeNode.h
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
-* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
-*
-* Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
-*
-* 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
-
diff --git a/src/shared/vmap/VMapDefinitions.h b/src/shared/vmap/VMapDefinitions.h
index 51743782526..e4a34cc1397 100644
--- a/src/shared/vmap/VMapDefinitions.h
+++ b/src/shared/vmap/VMapDefinitions.h
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,31 +8,35 @@
*
* 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
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPDEFINITIONS_H
#define _VMAPDEFINITIONS_H
#include <cstring>
+#define LIQUID_TILE_SIZE (533.333f / 128.f)
+
namespace VMAP
{
//=====================================
- #define MAX_CAN_FALL_DISTANCE 10.0
- const char VMAP_MAGIC[] = "VMAP_2.0";
+ #define MAX_CAN_FALL_DISTANCE 10.0f
+ const char VMAP_MAGIC[] = "VMAP_3.0";
class VMapDefinitions
{
public:
- static const double getMaxCanFallDistance() { return(MAX_CAN_FALL_DISTANCE); }
+ static float getMaxCanFallDistance() { return MAX_CAN_FALL_DISTANCE; }
};
//======================================
+
+ // defined in TileAssembler.cpp currently...
+ bool readChunk(FILE *rf, char *dest, const char *compare, uint32 len);
}
#endif
-
diff --git a/src/shared/vmap/VMapFactory.cpp b/src/shared/vmap/VMapFactory.cpp
index 2c5cd19d5dd..331acbace47 100644
--- a/src/shared/vmap/VMapFactory.cpp
+++ b/src/shared/vmap/VMapFactory.cpp
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,25 +8,51 @@
*
* 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
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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;
//===============================================
@@ -38,7 +62,7 @@ namespace VMAP
{
bool result = false;
unsigned int i;
- for (i=pStartPos; i<pString.size(); ++i)
+ for(i=pStartPos;i<pString.size(); ++i)
{
if(pString[i] == ',')
{
@@ -90,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;
}
@@ -110,4 +134,3 @@ namespace VMAP
}
}
}
-
diff --git a/src/shared/vmap/VMapFactory.h b/src/shared/vmap/VMapFactory.h
index fa015841f8d..8dc2c01938a 100644
--- a/src/shared/vmap/VMapFactory.h
+++ b/src/shared/vmap/VMapFactory.h
@@ -1,7 +1,5 @@
/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+ * 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
@@ -10,12 +8,12 @@
*
* 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
+ * 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPFACTORY_H
@@ -43,4 +41,3 @@ namespace VMAP
}
#endif
-
diff --git a/src/shared/vmap/VMapManager.cpp b/src/shared/vmap/VMapManager.cpp
deleted file mode 100644
index 4c651484616..00000000000
--- a/src/shared/vmap/VMapManager.cpp
+++ /dev/null
@@ -1,776 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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::convertPositionToTrinityRep(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 = convertPositionToTrinityRep(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 ray_lenght)
- {
- 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, ray_lenght);
- 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 ray_lenght)
- {
- float height = inf();
- Vector3 dir = Vector3(0,-1,0);
- Ray ray = Ray::fromOriginAndDirection(pPos, dir); // direction with length of 1
- float dist = getIntersectionTime(ray, ray_lenght, 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);
- }
- //=========================================================
- //=========================================================
- //=========================================================
-}
-
diff --git a/src/shared/vmap/VMapManager.h b/src/shared/vmap/VMapManager.h
deleted file mode 100644
index 8fe38400882..00000000000
--- a/src/shared/vmap/VMapManager.h
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
- *
- * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
- *
- * 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) { return(iLoadedModelContainer.containsKey(pName)); }
- void setLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.set(pTileIdent, true); }
- void removeLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.remove(pTileIdent); }
- bool hasLoadedMapTiles() { return(iLoadedMapTiles.size() > 0); }
- bool containsLoadedMapTile(unsigned int pTileIdent) { return(iLoadedMapTiles.containsKey(pTileIdent)); }
- public:
- ManagedModelContainer *getModelContainer(const std::string& pName) { return(iLoadedModelContainer.get(pName)); }
- const 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, float ray_lenght);
-
- 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); }
- const 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 convertPositionToTrinityRep(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, float ray_lenght);
-
- bool processCommand(char *pCommand); // for debug and extensions
-
- void preventMapsFromBeingUsed(const char* pMapIdString);
- };
-}
-#endif
-
diff --git a/src/shared/vmap/VMapManager2.cpp b/src/shared/vmap/VMapManager2.cpp
new file mode 100644
index 00000000000..c4c13fe7957
--- /dev/null
+++ b/src/shared/vmap/VMapManager2.cpp
@@ -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.5 * 64.0 * 533.33333333;
+ 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.5 * 64.0 * 533.33333333;
+ 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)
+ {
+ std::cout << "ingoring Map " << map_num << " for VMaps\n";
+ 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"))
+ {
+ std::cout << "VMapManager2: could not load '" << basepath << filename << ".vmo'!\n";
+ delete worldmodel;
+ return NULL;
+ }
+ std::cout << "VMapManager2: loading file '" << basepath << filename << "'.\n";
+ 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())
+ {
+ std::cout << "VMapManager2: trying to unload non-loaded file '" << filename << "'!\n";
+ return;
+ }
+ if( model->second.decRefCount() == 0)
+ {
+ std::cout << "VMapManager2: unloading file '" << filename << "'.\n";
+ 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
diff --git a/src/shared/vmap/VMapManager2.h b/src/shared/vmap/VMapManager2.h
new file mode 100644
index 00000000000..5f03b87b07f
--- /dev/null
+++ b/src/shared/vmap/VMapManager2.h
@@ -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
diff --git a/src/shared/vmap/VMapTools.h b/src/shared/vmap/VMapTools.h
index 86e3367773e..dbbd9af9271 100644
--- a/src/shared/vmap/VMapTools.h
+++ b/src/shared/vmap/VMapTools.h
@@ -1,7 +1,5 @@
/*
-* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
-*
-* Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
+* 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
@@ -10,12 +8,12 @@
*
* 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
+* 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
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _VMAPTOOLS_H
@@ -64,7 +62,7 @@ namespace VMAP
{
// Integer representation of a floating-point value.
-#define IR(x) ((G3D::uint32&)x)
+#define IR(x) (reinterpret_cast<G3D::uint32 const&>(x))
Inside = true;
const G3D::Vector3& MinB = box.low();
@@ -150,4 +148,3 @@ namespace VMAP
};
}
#endif
-
diff --git a/src/shared/vmap/WorldModel.cpp b/src/shared/vmap/WorldModel.cpp
new file mode 100644
index 00000000000..690c77577ae
--- /dev/null
+++ b/src/shared/vmap/WorldModel.cpp
@@ -0,0 +1,535 @@
+/*
+ * 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
+ {
+ uint32 tx = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
+ if (tx<0 || tx >= iTilesX) return false;
+ uint32 ty = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
+ if (ty<0 || ty >= iTilesY) return false;
+ // checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
+ if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
+ return false;
+ //placeholder...use only lower left corner vertex
+ liqHeight = /* iCorner.z + */ iHeight[tx + ty*(iTilesX+1)];
+ 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;
+ }
+}
diff --git a/src/shared/vmap/WorldModel.h b/src/shared/vmap/WorldModel.h
new file mode 100644
index 00000000000..f12efed4f5d
--- /dev/null
+++ b/src/shared/vmap/WorldModel.h
@@ -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
diff --git a/src/trinitycore/trinitycore.conf.dist b/src/trinitycore/trinitycore.conf.dist
index cdc563bcf2d..3b6d20e093d 100644
--- a/src/trinitycore/trinitycore.conf.dist
+++ b/src/trinitycore/trinitycore.conf.dist
@@ -174,7 +174,11 @@ EAIErrorLevel = 2
# Check LOS for pets, to avoid them going through walls etc.
# Default: 0 (disable, less CPU usage)
# 1 (enable, each pet attack command will check for LOS)
-#
+#
+# vmap.enableIndoorCheck
+# Enable/Disable VMap based indoor check to remove outdoor-only auras (mounts etc.)
+# Default: 0 (disabled)
+#
# DetectPosCollision
# Check final move position, summon position, etc for visible collision
# with other objects or wall (wall only if vmaps are enabled)
@@ -243,6 +247,7 @@ vmap.enableHeight = 0
vmap.ignoreMapIds = "369"
vmap.ignoreSpellIds = "7720"
vmap.petLOS = 0
+vmap.enableIndoorCheck = 0
DetectPosCollision = 1
TargetPosRecalculateRange = 1.5
UpdateUptimeInterval = 10