236c842934
* fix(DB/SmartAI): improve Harry surrendering during quest 'Gambling Debt' (#23598) * fix(DB/Quest): The Kalu'ak dailies reward 500 rep (#23600) * chore(DB): import pending files Referenced commit(s):fb03f41b2a* fix(DB/GameEvent): Remove midsummer pole in K3 (#23614) * chore(DB): import pending files Referenced commit(s):7b0000d6ee* fix(DB/SmartAI): increase reliability of quest event Foolish Endeavors (#23612) * chore(DB): import pending files Referenced commit(s):86f219abbc* fix(Scripts/AreaTrigger): players become stuck after Last Rites (#23613) * chore(DB): import pending files Referenced commit(s):c1a8047cf1* fix(Core/Vmaps): Fix inconsistency of hitInstance and hitModel to cause wrong area ids (#23233) Co-authored-by: ModoX <moardox@gmail.com> Co-authored-by: Shauren <shauren.trinity@gmail.com> Co-authored-by: Grimdhex <237474256+Grimdhex@users.noreply.github.com> Co-authored-by: sudlud <sudlud@users.noreply.github.com> * fix(DB/Gameobject): Sniffed Values for 'Wild Mustard' spawns (#23608) * fix(DB/SmartAI): remove large combat distance of Frostbrood Sentry (#23607) * chore(DB): import pending files Referenced commit(s):41d40b236f* fix(DB/ReputationRewardRate): Patch 3.0.0 gain for Northrend factions (#23597) * chore(DB): import pending files Referenced commit(s):067a898caa* fix(Core/Map): It should be ensured that the instance is unloaded only after the Creature Respawn. (#23103) * fix(Scripts/Northrend): Sniffing Out The Perpetrator horde (#23620) * fix(Scripts/Northrend): ensure Drakuru stays in place during Betrayal (#23619) * chore(DB): import pending files Referenced commit(s):928e145694* fix(DB/SmartAI): quest 'Reconnaissance Flight' (#23628) Co-authored-by: dr-j <dr-j@users.noreply.github.com> Co-authored-by: Killyana <morphone1@gmail.com> * fix(DB/QuestOfferReward): remove mention of a beta recipe in text (#23629) * fix(DB/Conditions): update quest conditions to drop chokers (#23610) * chore(DB): import pending files Referenced commit(s):bca8f7ce07* refactor(Core/PlayerScript): Delete OnPlayerChat, use OnPlayerCanUseChat (#23617) * fix(Core/SmartAI): startup warnings unused params (#23551) * fix(Core/Unit): Druid Talent Survival of the Fittest lacking immunity to creature daze (#23471) * fix(DB/SAI): Fix Fizzcrank Paradrop teleporters (#23633) * chore(DB): import pending files Referenced commit(s):94ba1c210d* fix(Core): Fix waterwalking after dying in instance (#23593) * fix(DB/SAI): don't remove all auras when mounting flamebringer (#23640) * chore(DB): import pending files Referenced commit(s):22f91f3802* fix(DB/SAI): Emerald Lasher goes out of the terrain when aggroed. (#23642) * chore(DB): import pending files Referenced commit(s):f9d6fe41de* fix(DB/SAI): Burning Depths Necromancer no longer stays in place. (#23641) * chore(DB): import pending files Referenced commit(s):1037471c8d* fix(DB/SAI): Remove SmartAI from Valkyrion Harpoon Gun. (#23646) * chore(DB): import pending files Referenced commit(s):8e3a7e6dcf* fix(DB/Creature): Fix Weakened Reanimated Frost Wyrm inhabit type (#23645) * chore(DB): import pending files Referenced commit(s):3baa18ef5b* fix(DB/Spell): Infectious Bites should stack from different casters (#23647) * chore(DB): import pending files Referenced commit(s):5aede412ab* fix(DB/SAI): Solve various issues with It Goes to 11... quest. (#23651) * fix(DB/Loot): Fireproof Satchel will now always drop the Ritual of Torch (#23585) * chore(DB): import pending files Referenced commit(s):1090c209b3* fix(Scripts/Northrend): Betrayal quest (#23650) * fix(Script/BlackTemple): Reliquary of Souls will use 45 degree in front to set incombat (#22938) * fix(Scripts/Spell): Fix Animal Blood spawning when it shouldn't (#23656) * fix(Scripts/BoreanTundra): Script Bloodspore Haze/Psychosis (#23657) * chore(DB): import pending files Referenced commit(s):baf7957e36* fix(DB/SAI): Sibling Rivalry quest credit if mounted (#23659) * chore(DB): import pending files Referenced commit(s):6919cc679d* fix(docs/license): use GPLv2 as MaNGOS-based project (#23655) * fix(Core/Achievements): a character can only have 1 race realm first (#23626) * chore: fix leftover license header (#23678) * fix(Scripts/HoL): Update Loken script (#23587) * fix(Scripts/DTK): Update King Dred script (#23572) * fix(DB/SAI): Bitter Departure quest credit (#23658) * chore(DB): import pending files Referenced commit(s):e595425578* fix(DB/Conditions): Ice Shard require Icy Imprisonment (#23661) * chore(DB): import pending files Referenced commit(s):8294652e77* fix(DB/Loot): add Scourge Curio drop to Lost Shandaral Spirit (#23686) * chore(DB): import pending files Referenced commit(s):b6ed4347fe* fix(DB/Gameobject): fix spell focus location for 'Will of the Titans' (#23683) * chore(DB): import pending files Referenced commit(s):388f18895d* fix(DB/Creature): update IOC Demolisher spells (#23685) * chore(DB): import pending files Referenced commit(s):cdfa50c990* fix(Scripts/Northrend): IOC boss cast ability Mortal Strike (#23684) * fix(Scripts/BoreanTundra): Fix Beryl Sorcerer engaging mobs (#23690) * fix(Core/Entities): Improve interactions between taxis and players regarding PvP flag. (#23681) * fix(DB/Creature): Peon Gakra should be an innkeeper (#23699) * chore(DB): import pending files Referenced commit(s):6abff4ac2b* fix(Scripts/SholazarBasin): Fix Song of Wind and Water double credit (#23707) * fix(DB/SAI): Reanimated Frost Wyrm engage after being hit by quest spell (#23697) * fix(DB/SAI): Timely respawn Nesingwary Trappers (#23703) * fix(DB/Creature): Fix Fjord Hawk Matriarch unit flags (#23696) * fix(DB/Conditions): Fix Fordragon Resolve target conditions (#23701) * chore(DB): import pending files Referenced commit(s):2942d63125* fix(DB/Script): Move Tailhorn Stag and Amberpine Woodsman behavior into SmartAI. (#23708) * fix(DB/Creature): Set Trigger flag on Steam Vent. (#23710) * chore(DB): import pending files Referenced commit(s):435ca302ef* fix(DB/SAI): To Stars' Rest! taxi flight (#23712) * chore(DB): import pending files Referenced commit(s):ab4d59ac9d* fix (DB/Creature): Set Surveyor Orlond flags. (#23714) * chore(DB): import pending files Referenced commit(s):e8ec77dca7* fix(DB/Loot): Fix Master Summoner Staff drop chance (#23717) * chore(DB): import pending files Referenced commit(s):182c055e6e* fix(Scripts/DTK): Fix Oh Novos! achievement (#23539) (#23718) * fix(Core/Spells): Remove King Mrgl-Mrgl costume on spell casting (#23713) * chore(DB): import pending files Referenced commit(s):8c963a11ce* fix(DB/Reputation): Utigarde Pinnacle normal reputation (#23719) * chore(DB): import pending files Referenced commit(s):88ed7d66d5* fix(Scripts/HoS): Clean up faction update hacks (#23720) * fix(DB/Reputation): Lower reputation according to rates handling (#23722) * fix(DB/Reputation): Oculus normal & UP correction (#23723) * chore(DB): import pending files Referenced commit(s):abc2cf3028* fix(Scripts/Oculus): Implement crossfaction support for drakes (#23704) * fix(DB/Quest): Correct prerequisite for Reclaimed Ration (#23736) Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> * fix(DB/Quest): Correct prerequisite for Salvaging Life's Strength (#23734) Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> * chore(DB): import pending files Referenced commit(s):afd8197588* fix(Core/Movement): Fix SummonMovementInform for summons (#23725) * refactor(Core/Movement): Fix Build (#23739) * fix(DB/SAI): Update Iron Rune Construct SAI to use DO_ACTION instead … (#23716) * chore(DB): import pending files Referenced commit(s):7cc39f78e2* fix(DB/SAI): Fix Flamebringer gossip interaction (#23740) * chore(DB): import pending files Referenced commit(s):9cb683cfcd* fix(DB/SAI): Nerub'ar member packs now attack together. (#23727) * chore(DB): import pending files Referenced commit(s):6f5a1b7ccc* fix(DB/SAI): Remove Harrison Johnes quest flag on escort accept (#23700) * chore(DB): import pending files Referenced commit(s):bacf15d356* Update crash issue template with log submission guidelines (#23754) * Merge * Updated OnPlayerChat method name to OnPlayerCanUseChat --------- Co-authored-by: sogladev <sogladev@gmail.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: 天鹭 <18535853+PkllonG@users.noreply.github.com> Co-authored-by: ModoX <moardox@gmail.com> Co-authored-by: Shauren <shauren.trinity@gmail.com> Co-authored-by: Grimdhex <237474256+Grimdhex@users.noreply.github.com> Co-authored-by: sudlud <sudlud@users.noreply.github.com> Co-authored-by: dr-j <dr-j@users.noreply.github.com> Co-authored-by: Killyana <morphone1@gmail.com> Co-authored-by: Undo <50205200+UndoUreche@users.noreply.github.com> Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com> Co-authored-by: killerwife <killerwife@gmail.com> Co-authored-by: Tereneckla <Tereneckla@pm.me> Co-authored-by: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> Co-authored-by: blinkysc <37940565+blinkysc@users.noreply.github.com> Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com> Co-authored-by: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Co-authored-by: Traesh <Traesh@users.noreply.github.com> Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
611 lines
21 KiB
C++
611 lines
21 KiB
C++
/*
|
|
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "TileAssembler.h"
|
|
#include "BoundingIntervalHierarchy.h"
|
|
#include "MapDefines.h"
|
|
#include "MapTree.h"
|
|
#include "VMapDefinitions.h"
|
|
#include <boost/filesystem.hpp>
|
|
#include <iomanip>
|
|
#include <set>
|
|
#include <sstream>
|
|
|
|
using G3D::Vector3;
|
|
using G3D::AABox;
|
|
using G3D::inf;
|
|
using std::pair;
|
|
|
|
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
|
|
{
|
|
Vector3 out = pIn * iScale;
|
|
out = iRotation * out;
|
|
return (out);
|
|
}
|
|
|
|
//=================================================================
|
|
|
|
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
|
|
: iDestDir(pDestDirName), iSrcDir(pSrcDirName)
|
|
{
|
|
boost::filesystem::create_directory(iDestDir);
|
|
//init();
|
|
}
|
|
|
|
TileAssembler::~TileAssembler()
|
|
{
|
|
//delete iCoordModelMapping;
|
|
}
|
|
|
|
bool TileAssembler::convertWorld2()
|
|
{
|
|
bool success = readMapSpawns();
|
|
if (!success)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 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;
|
|
printf("Calculating model bounds for map %u...\n", map_iter->first);
|
|
for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
printf("Creating map tree for map %u...\n", map_iter->first);
|
|
BIH pTree;
|
|
|
|
try
|
|
{
|
|
pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::GetBounds);
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
printf("Exception ""%s"" when calling pTree.build", e.what());
|
|
return false;
|
|
}
|
|
|
|
// ===> possibly move this code to StaticMapTree class
|
|
std::map<uint32, uint32> modelNodeIdx;
|
|
for (uint32 i = 0; i < mapSpawns.size(); ++i)
|
|
{
|
|
modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
|
|
}
|
|
|
|
// write map tree file
|
|
std::stringstream mapfilename;
|
|
mapfilename << iDestDir << '/' << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
|
|
FILE* mapfile = fopen(mapfilename.str().c_str(), "wb");
|
|
if (!mapfile)
|
|
{
|
|
success = false;
|
|
printf("Cannot open %s\n", mapfilename.str().c_str());
|
|
break;
|
|
}
|
|
|
|
//general info
|
|
if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) { success = false; }
|
|
uint32 globalTileID = StaticMapTree::packTileID(65, 65);
|
|
pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
|
|
char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
|
|
if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) { success = false; }
|
|
// Nodes
|
|
if (success && fwrite("NODE", 4, 1, mapfile) != 1) { success = false; }
|
|
if (success) { success = pTree.writeToFile(mapfile); }
|
|
// global map spawns (WDT), if any (most instances)
|
|
if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) { success = false; }
|
|
|
|
for (TileMap::iterator glob = globalRange.first; glob != globalRange.second && success; ++glob)
|
|
{
|
|
success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
|
|
}
|
|
|
|
fclose(mapfile);
|
|
|
|
// <====
|
|
|
|
// write map tile files, similar to ADT files, only with extra BSP tree node info
|
|
TileMap& tileEntries = map_iter->second->TileEntries;
|
|
TileMap::iterator tile;
|
|
for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
|
|
{
|
|
const ModelSpawn& spawn = map_iter->second->UniqueEntries[tile->second];
|
|
if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
|
|
{
|
|
continue;
|
|
}
|
|
uint32 nSpawns = tileEntries.count(tile->first);
|
|
std::stringstream tilefilename;
|
|
tilefilename.fill('0');
|
|
tilefilename << iDestDir << '/' << std::setw(3) << map_iter->first << '_';
|
|
uint32 x, y;
|
|
StaticMapTree::unpackTileID(tile->first, x, y);
|
|
tilefilename << std::setw(2) << x << '_' << std::setw(2) << y << ".vmtile";
|
|
if (FILE* tilefile = fopen(tilefilename.str().c_str(), "wb"))
|
|
{
|
|
// file header
|
|
if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) { success = false; }
|
|
// write number of tile spawns
|
|
if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) { success = false; }
|
|
// write tile spawns
|
|
for (uint32 s = 0; s < nSpawns; ++s)
|
|
{
|
|
if (s)
|
|
{
|
|
++tile;
|
|
}
|
|
const ModelSpawn& spawn2 = map_iter->second->UniqueEntries[tile->second];
|
|
success = success && ModelSpawn::writeToFile(tilefile, spawn2);
|
|
// MapTree nodes to update when loading tile:
|
|
std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
|
|
if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) { success = false; }
|
|
}
|
|
fclose(tilefile);
|
|
}
|
|
}
|
|
// break; //test, extract only first map; TODO: remvoe this line
|
|
}
|
|
|
|
// add an object models, listed in temp_gameobject_models file
|
|
exportGameobjectModels();
|
|
// export objects
|
|
std::cout << "\nConverting Model Files" << std::endl;
|
|
for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
|
|
{
|
|
std::cout << "Converting " << *mfile << std::endl;
|
|
if (!convertRawFile(*mfile))
|
|
{
|
|
std::cout << "error converting " << *mfile << std::endl;
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//cleanup:
|
|
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
|
|
{
|
|
delete map_iter->second;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool TileAssembler::readMapSpawns()
|
|
{
|
|
std::string fname = iSrcDir + "/dir_bin";
|
|
FILE* dirf = fopen(fname.c_str(), "rb");
|
|
if (!dirf)
|
|
{
|
|
printf("Could not read dir_bin file!\n");
|
|
return false;
|
|
}
|
|
printf("Read coordinate mapping...\n");
|
|
uint32 mapID, tileX, tileY, check = 0;
|
|
G3D::Vector3 v1, v2;
|
|
ModelSpawn spawn;
|
|
while (!feof(dirf))
|
|
{
|
|
// read mapID, tileX, tileY, Flags, NameSet, UniqueId, Pos, Rot, Scale, Bound_lo, Bound_hi, name
|
|
check = fread(&mapID, sizeof(uint32), 1, dirf);
|
|
if (check == 0) // EoF...
|
|
{
|
|
break;
|
|
}
|
|
check += fread(&tileX, sizeof(uint32), 1, dirf);
|
|
check += fread(&tileY, sizeof(uint32), 1, dirf);
|
|
if (!ModelSpawn::readFromFile(dirf, spawn))
|
|
{
|
|
break;
|
|
}
|
|
|
|
MapSpawns* current;
|
|
MapData::iterator map_iter = mapData.find(mapID);
|
|
if (map_iter == mapData.end())
|
|
{
|
|
printf("spawning Map %d\n", mapID);
|
|
mapData[mapID] = current = new MapSpawns();
|
|
}
|
|
else
|
|
{
|
|
current = map_iter->second;
|
|
}
|
|
|
|
current->UniqueEntries.emplace(spawn.ID, spawn);
|
|
current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
|
|
}
|
|
bool success = (ferror(dirf) == 0);
|
|
fclose(dirf);
|
|
return success;
|
|
}
|
|
|
|
bool TileAssembler::calculateTransformedBound(ModelSpawn& spawn)
|
|
{
|
|
std::string modelFilename(iSrcDir);
|
|
modelFilename.push_back('/');
|
|
modelFilename.append(spawn.name);
|
|
|
|
ModelPosition modelPosition;
|
|
modelPosition.iDir = spawn.iRot;
|
|
modelPosition.iScale = spawn.iScale;
|
|
modelPosition.init();
|
|
|
|
WorldModel_Raw raw_model;
|
|
if (!raw_model.Read(modelFilename.c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 groups = raw_model.groupsArray.size();
|
|
if (groups != 1)
|
|
{
|
|
printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
|
|
}
|
|
|
|
AABox modelBound;
|
|
bool boundEmpty = true;
|
|
|
|
for (uint32 g = 0; g < groups; ++g) // should be only one for M2 files...
|
|
{
|
|
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
|
|
|
|
if (vertices.empty())
|
|
{
|
|
std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
|
|
continue;
|
|
}
|
|
|
|
uint32 nvectors = vertices.size();
|
|
for (uint32 i = 0; i < nvectors; ++i)
|
|
{
|
|
Vector3 v = modelPosition.transform(vertices[i]);
|
|
|
|
if (boundEmpty)
|
|
{
|
|
modelBound = AABox(v, v), boundEmpty = false;
|
|
}
|
|
else
|
|
{
|
|
modelBound.merge(v);
|
|
}
|
|
}
|
|
}
|
|
spawn.iBound = modelBound + spawn.iPos;
|
|
spawn.flags |= MOD_HAS_BOUND;
|
|
return true;
|
|
}
|
|
|
|
#pragma pack(push, 1)
|
|
struct WMOLiquidHeader
|
|
{
|
|
int xverts, yverts, xtiles, ytiles;
|
|
float pos_x;
|
|
float pos_y;
|
|
float pos_z;
|
|
short material;
|
|
};
|
|
#pragma pack(pop)
|
|
//=================================================================
|
|
bool TileAssembler::convertRawFile(const std::string& pModelFilename)
|
|
{
|
|
bool success = true;
|
|
std::string filename = iSrcDir;
|
|
if (filename.length() > 0)
|
|
{
|
|
filename.push_back('/');
|
|
}
|
|
filename.append(pModelFilename);
|
|
|
|
WorldModel_Raw raw_model;
|
|
if (!raw_model.Read(filename.c_str()))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// write WorldModel
|
|
WorldModel model;
|
|
model.setRootWmoID(raw_model.RootWMOID);
|
|
if (!raw_model.groupsArray.empty())
|
|
{
|
|
std::vector<GroupModel> groupsArray;
|
|
|
|
uint32 groups = raw_model.groupsArray.size();
|
|
for (uint32 g = 0; g < groups; ++g)
|
|
{
|
|
GroupModel_Raw& raw_group = raw_model.groupsArray[g];
|
|
groupsArray.push_back(GroupModel(raw_group.mogpflags, raw_group.GroupWMOID, raw_group.bounds ));
|
|
groupsArray.back().setMeshData(raw_group.vertexArray, raw_group.triangles);
|
|
groupsArray.back().setLiquidData(raw_group.liquid);
|
|
}
|
|
|
|
model.setGroupModels(groupsArray);
|
|
}
|
|
|
|
success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
|
|
//std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
|
|
return success;
|
|
}
|
|
|
|
void TileAssembler::exportGameobjectModels()
|
|
{
|
|
FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb");
|
|
if (!model_list)
|
|
{
|
|
return;
|
|
}
|
|
|
|
char ident[8];
|
|
if (fread(ident, 1, 8, model_list) != 8 || memcmp(ident, VMAP::RAW_VMAP_MAGIC, 8) != 0)
|
|
{
|
|
fclose(model_list);
|
|
return;
|
|
}
|
|
|
|
FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb");
|
|
if (!model_list_copy)
|
|
{
|
|
fclose(model_list);
|
|
return;
|
|
}
|
|
|
|
fwrite(VMAP::VMAP_MAGIC, 1, 8, model_list_copy);
|
|
|
|
uint32 name_length, displayId;
|
|
uint8 isWmo;
|
|
char buff[500];
|
|
while (!feof(model_list))
|
|
{
|
|
if (fread(&displayId, sizeof(uint32), 1, model_list) != 1)
|
|
if (feof(model_list)) // EOF flag is only set after failed reading attempt
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (fread(&isWmo, sizeof(uint8), 1, model_list) != 1
|
|
|| fread(&name_length, sizeof(uint32), 1, model_list) != 1
|
|
|| name_length >= sizeof(buff)
|
|
|| fread(&buff, sizeof(char), name_length, model_list) != name_length)
|
|
{
|
|
std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl;
|
|
break;
|
|
}
|
|
|
|
std::string model_name(buff, name_length);
|
|
|
|
WorldModel_Raw raw_model;
|
|
if (!raw_model.Read((iSrcDir + "/" + model_name).c_str()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
spawnedModelFiles.insert(model_name);
|
|
AABox bounds;
|
|
bool boundEmpty = true;
|
|
for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g)
|
|
{
|
|
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
|
|
|
|
uint32 nvectors = vertices.size();
|
|
for (uint32 i = 0; i < nvectors; ++i)
|
|
{
|
|
Vector3& v = vertices[i];
|
|
if (boundEmpty)
|
|
{
|
|
bounds = AABox(v, v), boundEmpty = false;
|
|
}
|
|
else
|
|
{
|
|
bounds.merge(v);
|
|
}
|
|
}
|
|
}
|
|
|
|
fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
|
|
fwrite(&isWmo, sizeof(uint8), 1, model_list_copy);
|
|
fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
|
|
fwrite(&buff, sizeof(char), name_length, model_list_copy);
|
|
fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
|
|
fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
|
|
}
|
|
|
|
fclose(model_list);
|
|
fclose(model_list_copy);
|
|
}
|
|
// 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); }
|
|
#define READ_OR_RETURN_WITH_DELETE(V, S) if (fread((V), (S), 1, rf) != 1) { \
|
|
fclose(rf); printf("readfail, op = %i\n", readOperation); delete[] V; return(false); };
|
|
#define CMP_OR_RETURN(V, S) if (strcmp((V), (S)) != 0) { \
|
|
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
|
|
|
|
bool GroupModel_Raw::Read(FILE* rf)
|
|
{
|
|
char blockId[5];
|
|
blockId[4] = 0;
|
|
int blocksize;
|
|
int readOperation = 0;
|
|
|
|
READ_OR_RETURN(&mogpflags, sizeof(uint32));
|
|
READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
|
|
|
|
Vector3 vec1, vec2;
|
|
READ_OR_RETURN(&vec1, sizeof(Vector3));
|
|
|
|
READ_OR_RETURN(&vec2, sizeof(Vector3));
|
|
bounds.set(vec1, vec2);
|
|
|
|
READ_OR_RETURN(&liquidflags, sizeof(uint32));
|
|
|
|
// 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(uint32));
|
|
for (uint32 b = 0; b < branches; ++b)
|
|
{
|
|
uint32 indexes;
|
|
// indexes for each branch (not used jet)
|
|
READ_OR_RETURN(&indexes, sizeof(uint32));
|
|
}
|
|
|
|
// ---- indexes
|
|
READ_OR_RETURN(&blockId, 4);
|
|
CMP_OR_RETURN(blockId, "INDX");
|
|
READ_OR_RETURN(&blocksize, sizeof(int));
|
|
uint32 nindexes;
|
|
READ_OR_RETURN(&nindexes, sizeof(uint32));
|
|
if (nindexes > 0)
|
|
{
|
|
uint16* indexarray = new uint16[nindexes];
|
|
READ_OR_RETURN_WITH_DELETE(indexarray, nindexes * sizeof(uint16));
|
|
triangles.reserve(nindexes / 3);
|
|
for (uint32 i = 0; i < nindexes; i += 3)
|
|
{
|
|
triangles.push_back(MeshTriangle(indexarray[i], indexarray[i + 1], indexarray[i + 2]));
|
|
}
|
|
|
|
delete[] indexarray;
|
|
}
|
|
|
|
// ---- vectors
|
|
READ_OR_RETURN(&blockId, 4);
|
|
CMP_OR_RETURN(blockId, "VERT");
|
|
READ_OR_RETURN(&blocksize, sizeof(int));
|
|
uint32 nvectors;
|
|
READ_OR_RETURN(&nvectors, sizeof(uint32));
|
|
|
|
if (nvectors > 0)
|
|
{
|
|
float* vectorarray = new float[nvectors * 3];
|
|
READ_OR_RETURN_WITH_DELETE(vectorarray, nvectors * sizeof(float) * 3);
|
|
for (uint32 i = 0; i < nvectors; ++i)
|
|
{
|
|
vertexArray.push_back( Vector3(vectorarray + 3 * i));
|
|
}
|
|
|
|
delete[] vectorarray;
|
|
}
|
|
// ----- liquid
|
|
liquid = nullptr;
|
|
if (liquidflags & 3)
|
|
{
|
|
READ_OR_RETURN(&blockId, 4);
|
|
CMP_OR_RETURN(blockId, "LIQU");
|
|
READ_OR_RETURN(&blocksize, sizeof(int));
|
|
uint32 liquidType;
|
|
READ_OR_RETURN(&liquidType, sizeof(uint32));
|
|
if (liquidflags & 1)
|
|
{
|
|
WMOLiquidHeader hlq;
|
|
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
|
|
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), liquidType);
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
liquid = new WmoLiquid(0, 0, Vector3::zero(), liquidType);
|
|
liquid->GetHeightStorage()[0] = bounds.high().z;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
GroupModel_Raw::~GroupModel_Raw()
|
|
{
|
|
delete liquid;
|
|
}
|
|
|
|
bool WorldModel_Raw::Read(const char* path)
|
|
{
|
|
FILE* rf = fopen(path, "rb");
|
|
if (!rf)
|
|
{
|
|
printf("ERROR: Can't open raw model file: %s\n", path);
|
|
return false;
|
|
}
|
|
|
|
char ident[9];
|
|
ident[8] = '\0';
|
|
int readOperation = 0;
|
|
|
|
READ_OR_RETURN(&ident, 8);
|
|
CMP_OR_RETURN(ident, RAW_VMAP_MAGIC);
|
|
|
|
// 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;
|
|
READ_OR_RETURN(&groups, sizeof(uint32));
|
|
READ_OR_RETURN(&RootWMOID, sizeof(uint32));
|
|
|
|
groupsArray.resize(groups);
|
|
bool succeed = true;
|
|
for (uint32 g = 0; g < groups && succeed; ++g)
|
|
{
|
|
succeed = groupsArray[g].Read(rf);
|
|
}
|
|
|
|
if (succeed) /// rf will be freed inside Read if the function had any errors.
|
|
{
|
|
fclose(rf);
|
|
}
|
|
return succeed;
|
|
}
|
|
|
|
// drop of temporary use defines
|
|
#undef READ_OR_RETURN
|
|
#undef CMP_OR_RETURN
|
|
}
|