diff options
author | click <none@none> | 2010-06-05 00:59:25 +0200 |
---|---|---|
committer | click <none@none> | 2010-06-05 00:59:25 +0200 |
commit | e77716188861d4aa83b227a90e04a66b63baeb1f (patch) | |
tree | ce72764181a760314ec851f7535052dcf75649db /src | |
parent | 1426c2970f42a2d065198806f750bf5dd28d580b (diff) |
HIGHLY EXPERIMENTAL - USE AT YOUR OWN RISK
Implement the use of the new vmap3-format by Lynx3d (mad props to you for this, and thanks for the talks earlier)
+ reduced Vmap size to less than one third, and improve precision
+ indoor/outdoor check which allows automatic unmounting of players
+ additional area information from WMOAreaTable.dbc, removed existing "hacks"
+ WMO liquid information for swimming and fishing correctly in buildings/cities/caves/instances (lava and slime WILL hurt from now on!)
- buildfiles for windows are not properly done, and will need to be sorted out
NOTE: Do NOT annoy Lynx3d about this, any issues with this "port" is entirely our fault !
THIS REVISION IS CONSIDERED UNSTABLE AND CONTAINS WORK IN PROGRESS - USE AT YOUR OWN RISK!
--HG--
branch : trunk
Diffstat (limited to 'src')
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 |