Tools: Added an experimental tool (still WIP) to replace the current mmaps generator
This commit is contained in:
1 parent
bb6ce2b2c9
commit
50f833a008
42 files changed
+2441
-1
No files matched your search
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
option(SERVERS "Build worldserver and authserver" 1)
|
option(SERVERS "Build worldserver and authserver" 1)
|
||||||
option(SCRIPTS "Build core with scripts included" 1)
|
option(SCRIPTS "Build core with scripts included" 1)
|
||||||
option(TOOLS "Build map/vmap extraction/assembler tools" 0)
|
option(TOOLS "Build map/vmap/mmap extraction/assembler tools" 0)
|
||||||
option(USE_SCRIPTPCH "Use precompiled headers when compiling scripts" 1)
|
option(USE_SCRIPTPCH "Use precompiled headers when compiling scripts" 1)
|
||||||
option(USE_COREPCH "Use precompiled headers when compiling servers" 1)
|
option(USE_COREPCH "Use precompiled headers when compiling servers" 1)
|
||||||
option(WITH_WARNINGS "Show all warnings during compile" 0)
|
option(WITH_WARNINGS "Show all warnings during compile" 0)
|
||||||
|
|||||||
@@ -12,3 +12,4 @@ add_subdirectory(map_extractor)
|
|||||||
add_subdirectory(vmap4_assembler)
|
add_subdirectory(vmap4_assembler)
|
||||||
add_subdirectory(vmap4_extractor)
|
add_subdirectory(vmap4_extractor)
|
||||||
add_subdirectory(mmaps_generator)
|
add_subdirectory(mmaps_generator)
|
||||||
|
add_subdirectory(mesh_extractor)
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef ADT_H
|
||||||
|
#define ADT_H
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "MapChunk.h"
|
||||||
|
#include "DoodadHandler.h"
|
||||||
|
#include "WorldModelHandler.h"
|
||||||
|
|
||||||
|
class ADT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ADT();
|
||||||
|
~ADT() { delete[] MapChunks; delete ObjectData; delete Data; }
|
||||||
|
|
||||||
|
ChunkedData* ObjectData;
|
||||||
|
ChunkedData* Data;
|
||||||
|
// This here is not a pointer, is an array of objects ( made this way to allow the dynamic allocation )
|
||||||
|
MapChunk* MapChunks;
|
||||||
|
MHDR Header;
|
||||||
|
// Can we dispose of this?
|
||||||
|
bool HasObjectData;
|
||||||
|
|
||||||
|
DoodadHandler* _DoodadHandler;
|
||||||
|
WorldModelHandler* _WorldModelHandler;
|
||||||
|
LiquidHandler* _LiquidHandler;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Copyright (C) 2005-2009 MaNGOS project <http://getmangos.com/>
|
||||||
|
# Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
|
||||||
|
#
|
||||||
|
# This file is free software; as a special exception the author gives
|
||||||
|
# unlimited permission to copy and/or distribute it, with or without
|
||||||
|
# modifications, as long as this notice is preserved.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
file(GLOB_RECURSE sources *.cpp *.h)
|
||||||
|
|
||||||
|
if( UNIX )
|
||||||
|
include_directories (
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/libmpq
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
elseif( WIN32 )
|
||||||
|
include_directories (
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Database
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Logging
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
|
||||||
|
${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/libmpq
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/libmpq/win
|
||||||
|
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
|
||||||
|
${ACE_INCLUDE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(MeshExtractor
|
||||||
|
${sources}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(MeshExtractor
|
||||||
|
mpq
|
||||||
|
Recast
|
||||||
|
Detour
|
||||||
|
${BZIP2_LIBRARIES}
|
||||||
|
${ZLIB_LIBRARIES}
|
||||||
|
${ACE_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(MeshExtractor mpq Recast Detour)
|
||||||
|
|
||||||
|
if( UNIX )
|
||||||
|
install(TARGETS MeshExtractor DESTINATION bin)
|
||||||
|
elseif( WIN32 )
|
||||||
|
install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#ifndef CACHE_H
|
||||||
|
#define CACHE_H
|
||||||
|
#include <string>
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
class WorldModelRoot;
|
||||||
|
class Model;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class GenericCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GenericCache() {}
|
||||||
|
const int32 FlushLimit = 1000;
|
||||||
|
|
||||||
|
void Insert(std::string key, T* val)
|
||||||
|
{
|
||||||
|
if (_items.size() > FlushLimit)
|
||||||
|
Clear();
|
||||||
|
_items.insert(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Get(std::string key)
|
||||||
|
{
|
||||||
|
UNORDERED_MAP<std::string, T*>::iterator itr = _items.find(key);
|
||||||
|
if (itr != _items.end())
|
||||||
|
return itr->second;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() { _items.clear(); }
|
||||||
|
private:
|
||||||
|
UNORDERED_MAP<std::string, T*> _items;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CacheClass
|
||||||
|
{
|
||||||
|
CacheClass() {}
|
||||||
|
GenericCache<Model> ModelCache;
|
||||||
|
GenericCache<WorldModelRoot> WorldModelCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CacheClass* Cache;
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include "Chunk.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
int32 Chunk::FindSubChunkOffset(std::string name)
|
||||||
|
{
|
||||||
|
// Reverse the name
|
||||||
|
name = std::string(name.rbegin(), name.rend());
|
||||||
|
if (name.size() != 4)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
FILE* stream = GetStream();
|
||||||
|
uint32 matched = 0;
|
||||||
|
while (ftell(stream) < Utils::Size(stream))
|
||||||
|
{
|
||||||
|
char b;
|
||||||
|
fread(&b, sizeof(char), 1, stream);
|
||||||
|
if (b == name[matched])
|
||||||
|
++matched;
|
||||||
|
else
|
||||||
|
matched = 0;
|
||||||
|
if (matched == 4)
|
||||||
|
return ftell(stream) - 4;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* Chunk::GetStream()
|
||||||
|
{
|
||||||
|
fseek(Stream, Offset, SEEK_SET);
|
||||||
|
return Stream;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef CHUNK_H
|
||||||
|
#define CHUNK_H
|
||||||
|
#include "Common.h"
|
||||||
|
#include <string>
|
||||||
|
class ChunkedData;
|
||||||
|
|
||||||
|
class Chunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {}
|
||||||
|
|
||||||
|
int32 FindSubChunkOffset(std::string name);
|
||||||
|
FILE* GetStream();
|
||||||
|
std::string Name;
|
||||||
|
uint32 Length;
|
||||||
|
uint32 Offset;
|
||||||
|
FILE* Stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "MPQManager.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) :
|
||||||
|
Stream(stream)
|
||||||
|
{
|
||||||
|
if (!Stream)
|
||||||
|
return;
|
||||||
|
Load(maxLength, chunksHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ )
|
||||||
|
{
|
||||||
|
Stream = MPQHandler->GetFile(file);
|
||||||
|
if (!Stream)
|
||||||
|
return;
|
||||||
|
Load(0, chunksHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChunkedData::Load( uint32 maxLength, uint32 chunksHint )
|
||||||
|
{
|
||||||
|
if (!maxLength)
|
||||||
|
maxLength = Utils::Size(Stream);
|
||||||
|
Chunks.reserve(chunksHint);
|
||||||
|
uint32 baseOffset = ftell(Stream);
|
||||||
|
uint32 calcOffset = 0;
|
||||||
|
while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength))
|
||||||
|
{
|
||||||
|
char nameBytes[5];
|
||||||
|
uint32 read = fread(&nameBytes, sizeof(char), 4, Stream);
|
||||||
|
nameBytes[read] = '\0';
|
||||||
|
std::string name = std::string(nameBytes);
|
||||||
|
// Utils::Reverse(nameBytes);
|
||||||
|
name = std::string(name.rbegin(), name.rend());
|
||||||
|
uint32 length;
|
||||||
|
fread(&length, sizeof(uint32), 1, Stream);
|
||||||
|
calcOffset += 8;
|
||||||
|
Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream));
|
||||||
|
calcOffset += length;
|
||||||
|
// save an extra seek at the end
|
||||||
|
if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength)
|
||||||
|
fseek(Stream, length, SEEK_CUR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChunkedData::GetFirstIndex( std::string name )
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Chunks.size(); ++i)
|
||||||
|
if (Chunks[i]->Name == name)
|
||||||
|
return i;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk* ChunkedData::GetChunkByName( std::string name )
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Chunks.size(); ++i)
|
||||||
|
if (Chunks[i]->Name == name)
|
||||||
|
return Chunks[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef CHNK_H
|
||||||
|
#define CHNK_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "Chunk.h"
|
||||||
|
|
||||||
|
class ChunkedData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300);
|
||||||
|
ChunkedData(std::string file, uint32 chunksHint = 300);
|
||||||
|
|
||||||
|
int GetFirstIndex(std::string name);
|
||||||
|
Chunk* GetChunkByName(std::string name);
|
||||||
|
|
||||||
|
void Load(uint32 maxLength, uint32 chunksHint);
|
||||||
|
std::vector<Chunk*> Chunks;
|
||||||
|
FILE* Stream;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef CONSTANTS_H
|
||||||
|
#define CONSTANTS_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
class Constants
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum TriangleType
|
||||||
|
{
|
||||||
|
TRIANGLE_TYPE_UNKNOWN,
|
||||||
|
TRIANGLE_TYPE_TERRAIN,
|
||||||
|
TRIANGLE_TYPE_WATER,
|
||||||
|
TRIANGLE_TYPE_DOODAD,
|
||||||
|
TRIANGLE_TYPE_WMO
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PolyArea
|
||||||
|
{
|
||||||
|
POLY_AREA_TERRAIN = 1,
|
||||||
|
POLY_AREA_WATER = 2,
|
||||||
|
POLY_AREA_ROAD = 3,
|
||||||
|
POLY_AREA_DANGER = 4,
|
||||||
|
};
|
||||||
|
static const float TileSize;
|
||||||
|
static const float MaxXY;
|
||||||
|
static const float ChunkSize;
|
||||||
|
static const float UnitSize;
|
||||||
|
static const float Origin[];
|
||||||
|
static const float PI;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#include "ContinentBuilder.h"
|
||||||
|
#include "WDT.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
void ContinentBuilder::Build()
|
||||||
|
{
|
||||||
|
for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef CONT_BUILDER_H
|
||||||
|
#define CONT_BUILDER_H
|
||||||
|
#include <string>
|
||||||
|
#include "WDT.h"
|
||||||
|
|
||||||
|
class ContinentBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContinentBuilder(std::string continent, WDT* wdt) : Continent(continent), TileMap(wdt) {}
|
||||||
|
void Build();
|
||||||
|
private:
|
||||||
|
std::string Continent;
|
||||||
|
WDT* TileMap;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#include "DoodadHandler.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "Cache.h"
|
||||||
|
#include "Model.h"
|
||||||
|
#include "g3d/Matrix4.h"
|
||||||
|
|
||||||
|
DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL)
|
||||||
|
{
|
||||||
|
if (!adt->HasObjectData)
|
||||||
|
return;
|
||||||
|
Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF");
|
||||||
|
if (mddf)
|
||||||
|
ReadDoodadDefinitions(mddf);
|
||||||
|
|
||||||
|
Chunk* mmid = adt->ObjectData->GetChunkByName("MMID");
|
||||||
|
Chunk* mmdx = adt->ObjectData->GetChunkByName("MMDX");
|
||||||
|
if (mmid && mmdx)
|
||||||
|
ReadDoodadPaths(mmid, mmdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoodadHandler::ProcessInternal( ChunkedData* subChunks )
|
||||||
|
{
|
||||||
|
if (!IsSane())
|
||||||
|
return;
|
||||||
|
Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD");
|
||||||
|
if (!doodadReferencesChunk)
|
||||||
|
return;
|
||||||
|
FILE* stream = doodadReferencesChunk->GetStream();
|
||||||
|
uint32 refCount = doodadReferencesChunk->Length / 4;
|
||||||
|
for (int i = 0; i < refCount; i++)
|
||||||
|
{
|
||||||
|
int32 index;
|
||||||
|
fread(&index, sizeof(int32), 1, stream);
|
||||||
|
if (index < 0 || index >= _definitions->size())
|
||||||
|
continue;
|
||||||
|
DoodadDefinition doodad = (*_definitions)[index];
|
||||||
|
if (_drawn.find(doodad.UniqueId) != _drawn.end())
|
||||||
|
continue;
|
||||||
|
_drawn.insert(doodad.UniqueId);
|
||||||
|
if (doodad.MmidIndex >= _paths->size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string path = (*_paths)[doodad.MmidIndex];
|
||||||
|
Model* model = Cache.ModelCache.Get(path);
|
||||||
|
if (!model)
|
||||||
|
{
|
||||||
|
model = new Model(path);
|
||||||
|
Cache.ModelCache.Insert(path, model);
|
||||||
|
}
|
||||||
|
if (!model->IsCollidable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Vertices.reserve(refCount * model->Vertices.size() * 0.2);
|
||||||
|
Triangles.reserve(refCount * model->Triangles.size() * 0.2);
|
||||||
|
|
||||||
|
InsertModelGeometry(doodad, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk )
|
||||||
|
{
|
||||||
|
int32 count = chunk->Length / 36;
|
||||||
|
_definitions = new std::vector<DoodadDefinition>;
|
||||||
|
_definitions->reserve(count);
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
DoodadDefinition def;
|
||||||
|
def.Read(stream);
|
||||||
|
_definitions->push_back(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data )
|
||||||
|
{
|
||||||
|
int paths = id->Length / 4;
|
||||||
|
_paths = new std::vector<std::string>();
|
||||||
|
_paths->reserve(paths);
|
||||||
|
for (int i = 0; i < paths; i++)
|
||||||
|
{
|
||||||
|
FILE* idStream = id->GetStream();
|
||||||
|
fseek(idStream, i * 4, SEEK_CUR);
|
||||||
|
uint32 offset;
|
||||||
|
fread(&offset, sizeof(uint32), 1, idStream);
|
||||||
|
FILE* dataStream = data->GetStream();
|
||||||
|
fseek(dataStream, offset + data->Offset, SEEK_SET);
|
||||||
|
_paths->push_back(Utils::ReadString(dataStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoodadHandler::InsertModelGeometry(DoodadDefinition def, Model* model)
|
||||||
|
{
|
||||||
|
G3D::Matrix4 transformation = Utils::GetTransformation(def);
|
||||||
|
uint32 vertOffset = Vertices.size();
|
||||||
|
for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr)
|
||||||
|
Vertices.push_back(Utils::VectorTransform(*itr, transformation));
|
||||||
|
for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr)
|
||||||
|
Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef DOOADHNDL_H
|
||||||
|
#define DOOADHNDL_H
|
||||||
|
#include "ObjectDataHandler.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "Model.h"
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class DoodadDefinition : IDefinition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32 MmidIndex;
|
||||||
|
uint32 UniqueId;
|
||||||
|
uint16 DecimalScale;
|
||||||
|
uint16 Flags;
|
||||||
|
|
||||||
|
float Scale() { return DecimalScale / 1024.0f; }
|
||||||
|
|
||||||
|
void Read(FILE* stream)
|
||||||
|
{
|
||||||
|
fread(&MmidIndex, sizeof(uint32), 1, stream);
|
||||||
|
fread(&UniqueId, sizeof(uint32), 1, stream);
|
||||||
|
Position = Vector3::Read(stream);
|
||||||
|
Rotation = Vector3::Read(stream);
|
||||||
|
fread(&DecimalScale, sizeof(uint16), 1, stream);
|
||||||
|
fread(&Flags, sizeof(uint16), 1, stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DoodadHandler : public ObjectDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DoodadHandler(ADT* adt);
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Triangle<uint32> > Triangles;
|
||||||
|
bool IsSane() { return _definitions && _paths; }
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ProcessInternal(ChunkedData* chunk);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReadDoodadDefinitions(Chunk* chunk);
|
||||||
|
void ReadDoodadPaths(Chunk* id, Chunk* data);
|
||||||
|
void InsertModelGeometry(DoodadDefinition def, Model* model);
|
||||||
|
std::set<uint32> _drawn;
|
||||||
|
std::vector<DoodadDefinition>* _definitions;
|
||||||
|
std::vector<std::string>* _paths;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#include "Geometry.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
|
||||||
|
Geometry::Geometry() : Transform(false)
|
||||||
|
{
|
||||||
|
Vertices.reserve(10000);
|
||||||
|
Triangles.reserve(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Geometry::CalculateBoundingBox( float*& min, float*& max )
|
||||||
|
{
|
||||||
|
min = new float[3];
|
||||||
|
max = new float[3];
|
||||||
|
|
||||||
|
for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
|
||||||
|
{
|
||||||
|
if (itr->x > max[0])
|
||||||
|
max[0] = itr->x;
|
||||||
|
if (itr->x < min[0])
|
||||||
|
min[0] = itr->x;
|
||||||
|
|
||||||
|
if (itr->y > max[1])
|
||||||
|
max[1] = itr->y;
|
||||||
|
if (itr->y < min[1])
|
||||||
|
min[1] = itr->y;
|
||||||
|
|
||||||
|
if (itr->z > max[2])
|
||||||
|
max[2] = itr->z;
|
||||||
|
if (itr->z < min[2])
|
||||||
|
min[2] = itr->z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Geometry::CalculateMinMaxHeight( float& min, float& max )
|
||||||
|
{
|
||||||
|
min = 0.0f;
|
||||||
|
max = 0.0f;
|
||||||
|
|
||||||
|
for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr)
|
||||||
|
{
|
||||||
|
if (Transform)
|
||||||
|
{
|
||||||
|
if (itr->y < min)
|
||||||
|
min = itr->y;
|
||||||
|
if (itr->y > max)
|
||||||
|
max = itr->y;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (itr->z < min)
|
||||||
|
min = itr->z;
|
||||||
|
if (itr->z > max)
|
||||||
|
max = itr->z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Geometry::AddData( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris )
|
||||||
|
{
|
||||||
|
uint32 vertOffset = Vertices.size();
|
||||||
|
for (std::vector<Vector3>::iterator itr = verts.begin(); itr != verts.end(); ++itr)
|
||||||
|
Vertices.push_back(Transform ? Utils::ToRecast(*itr) : *itr);
|
||||||
|
|
||||||
|
for (std::vector<Triangle<uint32> >::iterator itr = tris.begin(); itr != tris.end(); ++itr)
|
||||||
|
Triangles.push_back(Triangle<uint32>(itr->Type, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Geometry::GetRawData( float*& verts, int*& tris, uint8*& areas )
|
||||||
|
{
|
||||||
|
verts = new float[Vertices.size() * 3];
|
||||||
|
for (int i = 0; i < Vertices.size(); ++i)
|
||||||
|
{
|
||||||
|
Vector3& vert = Vertices[i];
|
||||||
|
verts[(i * 3) + 0] = vert.x;
|
||||||
|
verts[(i * 3) + 1] = vert.y;
|
||||||
|
verts[(i * 3) + 2] = vert.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
tris = new int[Triangles.size() * 3];
|
||||||
|
for (int i = 0; i < Triangles.size(); ++i)
|
||||||
|
{
|
||||||
|
Triangle<uint32>& tri = Triangles[i];
|
||||||
|
tris[(i * 3) + 0] = (int)tri.V0;
|
||||||
|
tris[(i * 3) + 1] = (int)tri.V1;
|
||||||
|
tris[(i * 3) + 2] = (int)tri.V2;
|
||||||
|
}
|
||||||
|
|
||||||
|
areas = new uint8[Triangles.size()];
|
||||||
|
for (int i = 0; i < Triangles.size(); i++)
|
||||||
|
{
|
||||||
|
switch (Triangles[i].Type)
|
||||||
|
{
|
||||||
|
case Constants::TRIANGLE_TYPE_WATER:
|
||||||
|
areas[i] = Constants::POLY_AREA_WATER;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
areas[i] = Constants::POLY_AREA_TERRAIN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef GEOMETRY_H
|
||||||
|
#define GEOMETRY_H
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
class Geometry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Geometry();
|
||||||
|
|
||||||
|
void CalculateBoundingBox(float*& min, float*& max);
|
||||||
|
void CalculateMinMaxHeight(float& min, float& max);
|
||||||
|
void AddData(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris);
|
||||||
|
void GetRawData(float*& verts, int*& tris, uint8*& areas);
|
||||||
|
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Triangle<uint32> > Triangles;
|
||||||
|
bool Transform;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "LiquidHandler.h"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef LIQUID_H
|
||||||
|
#define LIQUID_H
|
||||||
|
#include "ADT.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class LiquidHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ADT* Source;
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Triangle<uint32> > Triangles;
|
||||||
|
std::vector<MCNKLiquidData> MCNKData;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
#include "MPQ.h"
|
||||||
|
#include "MPQManager.h"
|
||||||
|
#include <deque>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
MPQArchive::MPQArchive(const char* filename)
|
||||||
|
{
|
||||||
|
int result = libmpq__archive_open(&mpq_a, filename, -1);
|
||||||
|
printf("Opening %s\n", filename);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case LIBMPQ_ERROR_OPEN :
|
||||||
|
printf("Error opening archive '%s': Does file really exist?\n", filename);
|
||||||
|
break;
|
||||||
|
case LIBMPQ_ERROR_FORMAT : /* bad file format */
|
||||||
|
printf("Error opening archive '%s': Bad file format\n", filename);
|
||||||
|
break;
|
||||||
|
case LIBMPQ_ERROR_SEEK : /* seeking in file failed */
|
||||||
|
printf("Error opening archive '%s': Seeking in file failed\n", filename);
|
||||||
|
break;
|
||||||
|
case LIBMPQ_ERROR_READ : /* Read error in archive */
|
||||||
|
printf("Error opening archive '%s': Read error in archive\n", filename);
|
||||||
|
break;
|
||||||
|
case LIBMPQ_ERROR_MALLOC : /* maybe not enough memory? :) */
|
||||||
|
printf("Error opening archive '%s': Maybe not enough memory\n", filename);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("Error opening archive '%s': Unknown error\n", filename);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQArchive::close()
|
||||||
|
{
|
||||||
|
libmpq__archive_close(mpq_a);
|
||||||
|
}
|
||||||
|
|
||||||
|
MPQFile::MPQFile(const char* filename):
|
||||||
|
eof(false), buffer(0), pointer(0), size(0)
|
||||||
|
{
|
||||||
|
for (std::deque<MPQArchive*>::iterator i = MPQHandler->Archives.begin(); i != MPQHandler->Archives.end();++i)
|
||||||
|
{
|
||||||
|
mpq_archive* mpq_a = (*i)->mpq_a;
|
||||||
|
|
||||||
|
uint32_t filenum;
|
||||||
|
if(libmpq__file_number(mpq_a, filename, &filenum))
|
||||||
|
continue;
|
||||||
|
libmpq__off_t transferred;
|
||||||
|
libmpq__file_unpacked_size(mpq_a, filenum, &size);
|
||||||
|
|
||||||
|
// HACK: in patch.mpq some files don't want to open and give 1 for filesize
|
||||||
|
if (size<=1) {
|
||||||
|
// printf("warning: file %s has size %d; cannot Read.\n", filename, size);
|
||||||
|
eof = true;
|
||||||
|
buffer = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer = new char[size];
|
||||||
|
|
||||||
|
//libmpq_file_getdata
|
||||||
|
libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
|
||||||
|
/*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
eof = true;
|
||||||
|
buffer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MPQFile::Read(void* dest, size_t bytes)
|
||||||
|
{
|
||||||
|
if (eof)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t rpos = pointer + bytes;
|
||||||
|
if (rpos > size) {
|
||||||
|
bytes = size - pointer;
|
||||||
|
eof = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest, &(buffer[pointer]), bytes);
|
||||||
|
|
||||||
|
pointer = rpos;
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQFile::seek(int offset)
|
||||||
|
{
|
||||||
|
pointer = offset;
|
||||||
|
eof = (pointer >= size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQFile::seekRelative(int offset)
|
||||||
|
{
|
||||||
|
pointer += offset;
|
||||||
|
eof = (pointer >= size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQFile::close()
|
||||||
|
{
|
||||||
|
if (buffer)
|
||||||
|
delete[] buffer;
|
||||||
|
buffer = 0;
|
||||||
|
eof = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* MPQFile::GetFileStream()
|
||||||
|
{
|
||||||
|
FILE* file = tmpfile();
|
||||||
|
fwrite(buffer, sizeof(char), size, file);
|
||||||
|
fseek(file, 0, SEEK_SET);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#ifndef MPQ_H
|
||||||
|
#define MPQ_H
|
||||||
|
|
||||||
|
#include "libmpq/mpq.h"
|
||||||
|
#include "Common.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class MPQArchive
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
mpq_archive_s *mpq_a;
|
||||||
|
|
||||||
|
MPQArchive(const char* filename);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void GetFileListTo(vector<string>& filelist) {
|
||||||
|
uint32_t filenum;
|
||||||
|
if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
|
||||||
|
libmpq__off_t size, transferred;
|
||||||
|
libmpq__file_unpacked_size(mpq_a, filenum, &size);
|
||||||
|
|
||||||
|
char *buffer = new char[size];
|
||||||
|
|
||||||
|
libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
|
||||||
|
|
||||||
|
char seps[] = "\n";
|
||||||
|
char* token;
|
||||||
|
|
||||||
|
token = strtok( buffer, seps );
|
||||||
|
uint32 counter = 0;
|
||||||
|
while ((token != NULL) && (counter < size)) {
|
||||||
|
//cout << token << endl;
|
||||||
|
token[strlen(token) - 1] = 0;
|
||||||
|
string s = token;
|
||||||
|
filelist.push_back(s);
|
||||||
|
counter += strlen(token) + 2;
|
||||||
|
token = strtok(NULL, seps);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MPQFile
|
||||||
|
{
|
||||||
|
//MPQHANDLE handle;
|
||||||
|
bool eof;
|
||||||
|
char *buffer;
|
||||||
|
libmpq__off_t pointer,size;
|
||||||
|
|
||||||
|
// disable copying
|
||||||
|
MPQFile(const MPQFile& /*f*/) {}
|
||||||
|
void operator=(const MPQFile& /*f*/) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
MPQFile(const char* filename); // filenames are not case sensitive
|
||||||
|
~MPQFile() { close(); }
|
||||||
|
size_t Read(void* dest, size_t bytes);
|
||||||
|
FILE* GetFileStream();
|
||||||
|
size_t getSize() { return size; }
|
||||||
|
size_t getPos() { return pointer; }
|
||||||
|
char* getBuffer() { return buffer; }
|
||||||
|
char* getPointer() { return buffer + pointer; }
|
||||||
|
bool isEof() { return eof; }
|
||||||
|
void seek(int offset);
|
||||||
|
void seekRelative(int offset);
|
||||||
|
void close();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void flipcc(char *fcc)
|
||||||
|
{
|
||||||
|
char t;
|
||||||
|
t=fcc[0];
|
||||||
|
fcc[0]=fcc[3];
|
||||||
|
fcc[3]=t;
|
||||||
|
t=fcc[1];
|
||||||
|
fcc[1]=fcc[2];
|
||||||
|
fcc[2]=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
#include "MPQManager.h"
|
||||||
|
#include "MPQ.h"
|
||||||
|
|
||||||
|
char* MPQManager::Files[] = {
|
||||||
|
"common.MPQ",
|
||||||
|
"common-2.MPQ",
|
||||||
|
"expansion.MPQ",
|
||||||
|
"lichking.MPQ",
|
||||||
|
"patch.MPQ",
|
||||||
|
"patch-2.MPQ",
|
||||||
|
"patch-3.MPQ"
|
||||||
|
};
|
||||||
|
|
||||||
|
void MPQManager::Initialize()
|
||||||
|
{
|
||||||
|
LoadMPQs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQManager::LoadMaps()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MPQManager::LoadMPQs()
|
||||||
|
{
|
||||||
|
// Load the locale MPQ files first
|
||||||
|
char filename[512];
|
||||||
|
|
||||||
|
/*sprintf(filename,"Data/%s/locale-%s.MPQ", langs[locale], langs[locale]);*/
|
||||||
|
Archives.push_front(new MPQArchive("Data/enUS/locale-enUS.MPQ"));
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
char ext[3] = "";
|
||||||
|
if (i)
|
||||||
|
sprintf(ext, "-%i", i + 1);
|
||||||
|
|
||||||
|
sprintf(filename, "Data/enUS/patch-enUS%s.MPQ", ext);
|
||||||
|
Archives.push_front(new MPQArchive(filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now load the common MPQ files
|
||||||
|
int count = sizeof(Files) / sizeof(char*);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
sprintf(filename, "Data/%s", Files[i]);
|
||||||
|
Archives.push_front(new MPQArchive(filename));
|
||||||
|
}
|
||||||
|
printf("Loaded %u MPQ files succesfully", Archives.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* MPQManager::GetFile( std::string path )
|
||||||
|
{
|
||||||
|
MPQFile file(path.c_str());
|
||||||
|
if (file.isEof())
|
||||||
|
return NULL;
|
||||||
|
return file.GetFileStream();
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef MPQ_MANAGER_H
|
||||||
|
#define MPQ_MANAGER_H
|
||||||
|
|
||||||
|
#include "MPQ.h"
|
||||||
|
|
||||||
|
class MPQManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MPQManager() {}
|
||||||
|
~MPQManager() {}
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void LoadMaps();
|
||||||
|
FILE* GetFile(std::string path);
|
||||||
|
|
||||||
|
std::deque<MPQArchive*> Archives;
|
||||||
|
|
||||||
|
static char* Files[];
|
||||||
|
protected:
|
||||||
|
void LoadMPQs();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern MPQManager* MPQHandler;
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#include "MapChunk.h"
|
||||||
|
#include "ADT.h"
|
||||||
|
|
||||||
|
MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : adt(_adt), Source(chunk), Vertices(NULL)
|
||||||
|
{
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
Header.Read(stream);
|
||||||
|
fseek(stream, chunk->Offset, SEEK_SET);
|
||||||
|
GenerateVertices(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapChunk::GenerateTriangles()
|
||||||
|
{
|
||||||
|
Triangles.reserve(256);
|
||||||
|
for (int y = 0; y < 8; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
{
|
||||||
|
if (HasHole(Header.Holes, x / 2, y / 2))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 topLeft = (17 * y) + x;
|
||||||
|
uint32 topRight = (17 * y) + x + 1;
|
||||||
|
uint32 bottomLeft = (17 * (y + 1)) + x;
|
||||||
|
uint32 bottomRight = (17 * (y + 1)) + x + 1;
|
||||||
|
uint32 center = (17 * y) + 9 + x;
|
||||||
|
|
||||||
|
uint8 triangleType = Constants::TRIANGLE_TYPE_TERRAIN;
|
||||||
|
if (ADT.LiquidHandler && ADT.LiquidHandler.MCNKData)
|
||||||
|
{
|
||||||
|
var data = ADT.LiquidHandler.MCNKData[Index];
|
||||||
|
uint32 maxHeight = std::max(
|
||||||
|
std::max(
|
||||||
|
std::max(std::max(Vertices[topLeft].z, Vertices[topRight].z), Vertices[bottomLeft].z),
|
||||||
|
Vertices[bottomRight].z), Vertices[center].z);
|
||||||
|
if (data && data->IsWater(x, y, maxHeight))
|
||||||
|
triangleType = Constants::TRIANGLE_TYPE_WATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
Triangles.push_back(new Triangle<uint8>(triangleType, topRight, topLeft, center));
|
||||||
|
Triangles.push_back(new Triangle<uint8>(triangleType, topLeft, bottomLeft, center));
|
||||||
|
Triangles.push_back(new Triangle<uint8>(triangleType, bottomLeft, bottomRight, center));
|
||||||
|
Triangles.push_back(new Triangle<uint8>(triangleType, bottomRight, topRight, center));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MapChunk::~MapChunk()
|
||||||
|
{
|
||||||
|
delete[] Vertices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapChunk::GenerateVertices( FILE* stream )
|
||||||
|
{
|
||||||
|
fseek(stream, Header.OffsetMCVT, SEEK_CUR);
|
||||||
|
int32 vertIndex = 0;
|
||||||
|
Vertices = new Vector3[125];
|
||||||
|
|
||||||
|
for (int j = 0; j < 17; j++)
|
||||||
|
{
|
||||||
|
int values = j % 2 ? 8 : 9;
|
||||||
|
for (int i = 0; i < values; i++)
|
||||||
|
{
|
||||||
|
float tmp;
|
||||||
|
fread(&tmp, sizeof(float), 1, stream);
|
||||||
|
Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp);
|
||||||
|
if (values == 8)
|
||||||
|
vert.y -= Constants::UnitSize * 0.5f;
|
||||||
|
Vertices[vertIndex++] = vert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MapChunk::HasHole( uint32 map, int x, int y )
|
||||||
|
{
|
||||||
|
return (map & 0x0000FFFF) & ((1 << x) << (y << 2));
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef MAPCHUNK_H
|
||||||
|
#define MAPCHUNK_H
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
#include <vector>
|
||||||
|
class ADT;
|
||||||
|
|
||||||
|
class MapChunk
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapChunk(ADT* _adt, Chunk* chunk);
|
||||||
|
~MapChunk();
|
||||||
|
|
||||||
|
void GenerateTriangles();
|
||||||
|
void GenerateVertices(FILE* stream);
|
||||||
|
static bool HasHole(uint32 map, int x, int y);
|
||||||
|
ADT* adt;
|
||||||
|
Chunk* Source;
|
||||||
|
MapChunkHeader Header;
|
||||||
|
Vector3* Vertices;
|
||||||
|
std::vector<Triangle<uint8> > Triangles;
|
||||||
|
int32 Index;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include "MPQManager.h"
|
||||||
|
#include "WDT.h"
|
||||||
|
#include "ContinentBuilder.h"
|
||||||
|
#include "Cache.h"
|
||||||
|
|
||||||
|
MPQManager* MPQHandler;
|
||||||
|
CacheClass* Cache;
|
||||||
|
|
||||||
|
void ExtractAllMaps()
|
||||||
|
{
|
||||||
|
WDT wdt("World\\maps\\DalaranPrison\\DalaranPrison.wdt");
|
||||||
|
if (!wdt.IsValid)
|
||||||
|
return;
|
||||||
|
printf("Model valid!");
|
||||||
|
if (wdt.IsGlobalModel)
|
||||||
|
{
|
||||||
|
printf("Unsupported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContinentBuilder builder("DalaranPrison", &wdt);
|
||||||
|
builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
system("pause");
|
||||||
|
Cache = new CacheClass();
|
||||||
|
MPQHandler = new MPQManager();
|
||||||
|
MPQHandler->Initialize();
|
||||||
|
MPQHandler->LoadMaps();
|
||||||
|
ExtractAllMaps();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#include "Model.h"
|
||||||
|
#include "MPQManager.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
Model::Model( std::string path )
|
||||||
|
{
|
||||||
|
Stream = MPQHandler->GetFile(Utils::FixModelPath(path));
|
||||||
|
Header.Read(Stream);
|
||||||
|
if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 &&
|
||||||
|
Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f)
|
||||||
|
{
|
||||||
|
IsCollidable = true;
|
||||||
|
ReadVertices(Stream);
|
||||||
|
ReadBoundingNormals(Stream);
|
||||||
|
ReadBoundingTriangles(Stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::ReadVertices( FILE* stream )
|
||||||
|
{
|
||||||
|
fseek(stream, Header.OffsetBoundingVertices, SEEK_SET);
|
||||||
|
Vertices.reserve(Header.CountBoundingVertices);
|
||||||
|
for (int i = 0; i < Header.CountBoundingVertices; ++i)
|
||||||
|
Vertices[i] = Vector3::Read(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::ReadBoundingTriangles( FILE* stream )
|
||||||
|
{
|
||||||
|
fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET);
|
||||||
|
Triangles.reserve(Header.CountBoundingTriangles / 3);
|
||||||
|
for (int i = 0; i < Header.CountBoundingTriangles / 3; i++)
|
||||||
|
{
|
||||||
|
Triangle<uint16> tri;
|
||||||
|
tri.Type = Constants::TRIANGLE_TYPE_DOODAD;
|
||||||
|
fread(&tri.V0, sizeof(uint16), 1, stream);
|
||||||
|
fread(&tri.V1, sizeof(uint16), 1, stream);
|
||||||
|
fread(&tri.V2, sizeof(uint16), 1, stream);
|
||||||
|
Triangles[i] = tri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::ReadBoundingNormals( FILE* stream )
|
||||||
|
{
|
||||||
|
fseek(stream, Header.OffsetBoundingNormals, SEEK_SET);
|
||||||
|
Normals.reserve(Header.CountBoundingNormals);
|
||||||
|
for (int i = 0; i < Header.CountBoundingNormals; i++)
|
||||||
|
Normals[i] = Vector3::Read(stream);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef MODEL_H
|
||||||
|
#define MODEL_H
|
||||||
|
#include <vector>
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
class Model
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Model(std::string path);
|
||||||
|
|
||||||
|
void ReadVertices(FILE* stream);
|
||||||
|
void ReadBoundingTriangles(FILE* stream);
|
||||||
|
void ReadBoundingNormals(FILE* stream);
|
||||||
|
ModelHeader Header;
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Vector3> Normals;
|
||||||
|
std::vector<Triangle<uint16> > Triangles;
|
||||||
|
bool IsCollidable;
|
||||||
|
FILE* Stream;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#include "ObjectDataHandler.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "ADT.h"
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
|
||||||
|
void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk )
|
||||||
|
{
|
||||||
|
if (!Source->HasObjectData)
|
||||||
|
return;
|
||||||
|
// fuck it blizzard, why is this crap necessary?
|
||||||
|
int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK");
|
||||||
|
if (firstIndex == -1)
|
||||||
|
return;
|
||||||
|
if (firstIndex + chunk->Index > Source->ObjectData->Chunks.size())
|
||||||
|
return;
|
||||||
|
Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index];
|
||||||
|
if (ourChunk->Length == 0)
|
||||||
|
return;
|
||||||
|
ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2);
|
||||||
|
ProcessInternal(subChunks);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef ODATA_HNDL_H
|
||||||
|
#define ODATA_HNDL_H
|
||||||
|
#include "ADT.h"
|
||||||
|
#include "MapChunk.h"
|
||||||
|
|
||||||
|
class ObjectDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ObjectDataHandler(ADT* _adt) : Source(_adt) {}
|
||||||
|
|
||||||
|
void ProcessMapChunk(MapChunk* chunk);
|
||||||
|
virtual void ProcessInternal(ChunkedData* data) = 0;
|
||||||
|
ADT* Source;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#include "TileBuilder.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
|
||||||
|
TileBuilder::TileBuilder(std::string world, int x, int y) : _Geometry(NULL), World(world), X(x), Y(y), MapId(608)
|
||||||
|
{
|
||||||
|
// Cell Size = TileSize / TileVoxelSize
|
||||||
|
// 1800 = TileVoxelSize
|
||||||
|
Config.cs = Constants::TileSize / 1800;
|
||||||
|
// Cell Height
|
||||||
|
Config.ch = 0.3f;
|
||||||
|
// Min Region Area = 6^2
|
||||||
|
Config.minRegionArea = 36;
|
||||||
|
// Merge Region Area = 12^2
|
||||||
|
Config.mergeRegionArea = 144;
|
||||||
|
Config.walkableSlopeAngle = 50.0f;
|
||||||
|
Config.detailSampleDist = 3.0f;
|
||||||
|
Config.detailSampleMaxError = 1.25f;
|
||||||
|
Config.walkableClimb = 1.0f / Config.ch;
|
||||||
|
Config.walkableHeight = 2.1f / Config.ch;
|
||||||
|
Config.walkableRadius = 0.6f / Config.cs;
|
||||||
|
Config.maxEdgeLen = Config.walkableRadius * 8;
|
||||||
|
Config.borderSize = Config.walkableRadius + 8;
|
||||||
|
Config.width = 1800;
|
||||||
|
Config.maxVertsPerPoly = 6;
|
||||||
|
Config.maxSimplificationError = 1.3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax )
|
||||||
|
{
|
||||||
|
float origin[3] = Constants::Origin;
|
||||||
|
bmin = new float[3];
|
||||||
|
bmax = new float[3];
|
||||||
|
bmin[0] = origin[0] + (Constants::TileSize * X);
|
||||||
|
bmin[2] = origin[2] + (Constants::TileSize * Y);
|
||||||
|
bmax[0] = origin[0] + (Constants::TileSize * (X + 1));
|
||||||
|
bmax[2] = origin[2] + (Constants::TileSize * (Y + 1));
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef TILE_BUILD_H
|
||||||
|
#define TILE_BUILD_H
|
||||||
|
#include <string>
|
||||||
|
#include "Recast.h"
|
||||||
|
|
||||||
|
#include "Geometry.h"
|
||||||
|
|
||||||
|
class TileBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TileBuilder(std::string world, int x, int y);
|
||||||
|
void CalculateTileBounds(float*& bmin, float*& bmax);
|
||||||
|
uint8* Build();
|
||||||
|
|
||||||
|
std::string World;
|
||||||
|
int X;
|
||||||
|
int Y;
|
||||||
|
int MapId;
|
||||||
|
rcConfig Config;
|
||||||
|
Geometry* _Geometry;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
#include "Utils.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include "g3d/Matrix4.h"
|
||||||
|
|
||||||
|
const float Constants::TileSize = 533.0f + (1/3.0f);
|
||||||
|
const float Constants::MaxXY = 32.0f * Constants::TileSize;
|
||||||
|
const float Constants::ChunkSize = Constants::TileSize / 16.0f;
|
||||||
|
const float Constants::UnitSize = Constants::ChunkSize / 8.0f;
|
||||||
|
const float Constants::Origin[] = { -Constants::MaxXY, 0.0f, -Constants::MaxXY };
|
||||||
|
const float Constants::PI = 3.1415926f;
|
||||||
|
|
||||||
|
void Utils::Reverse(char word[])
|
||||||
|
{
|
||||||
|
int len = strlen(word);
|
||||||
|
for (int i = 0;i < len / 2; i++)
|
||||||
|
{
|
||||||
|
word[i] ^= word[len-i-1];
|
||||||
|
word[len-i-1] ^= word[i];
|
||||||
|
word[i] ^= word[len-i-1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::ReadString( FILE* file )
|
||||||
|
{
|
||||||
|
std::string ret;
|
||||||
|
int i = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
char b;
|
||||||
|
fread(&b, sizeof(char), 1, file);
|
||||||
|
if (b == 0)
|
||||||
|
break;
|
||||||
|
ret[i++] = b;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 Utils::Size( FILE* file )
|
||||||
|
{
|
||||||
|
// store the old position
|
||||||
|
uint32 offset = ftell(file);
|
||||||
|
// Get file size
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
uint32 size = ftell(file);
|
||||||
|
// reset back to the old position
|
||||||
|
fseek(file, offset, SEEK_SET);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Utils::ToRecast( Vector3 val )
|
||||||
|
{
|
||||||
|
return Vector3(-val.y, val.z, -val.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::GetAdtPath( std::string world, int x, int y )
|
||||||
|
{
|
||||||
|
return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::FixModelPath( std::string path )
|
||||||
|
{
|
||||||
|
std::string::size_type idx = path.rfind(".");
|
||||||
|
// Bizarre way of changing extension but...
|
||||||
|
if (idx != std::string::npos)
|
||||||
|
{
|
||||||
|
path[idx + 1] = "M";
|
||||||
|
path[idx + 2] = "2";
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
G3D::Matrix4 Utils::RotationX(float angle)
|
||||||
|
{
|
||||||
|
float _cos = cos(angle);
|
||||||
|
float _sin = sin(angle);
|
||||||
|
Matrix4 ret = G3D::Matrix4::identity();
|
||||||
|
ret[2][2] = _cos;
|
||||||
|
ret[2][3] = _sin;
|
||||||
|
ret[3][2] = -_sin;
|
||||||
|
ret[3][3] = _cos;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
G3D::Matrix4 Utils::GetTransformation( IDefinition def )
|
||||||
|
{
|
||||||
|
G3D::Matrix4 translation;
|
||||||
|
if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f)
|
||||||
|
translation = G3D::Matrix4::identity();
|
||||||
|
else
|
||||||
|
translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY),
|
||||||
|
-(def.Position.x - Constants::MaxXY), def.Position.y);
|
||||||
|
|
||||||
|
G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180));
|
||||||
|
if (def.Scale() < 1.0f || def.Scale() > 1.0f)
|
||||||
|
return G3D::Matrix4::scale(def.Scale()) * rotation * translation;
|
||||||
|
return rotation * translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
G3D::Matrix4 Utils::RotationY( float angle )
|
||||||
|
{
|
||||||
|
float _cos = cos(angle);
|
||||||
|
float _sin = sin(angle);
|
||||||
|
Matrix4 ret = G3D::Matrix4::identity();
|
||||||
|
ret[1][1] = _cos;
|
||||||
|
ret[1][3] = -_sin;
|
||||||
|
ret[3][1] = _sin;
|
||||||
|
ret[3][3] = _cos;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
G3D::Matrix4 Utils::RotationZ( float angle )
|
||||||
|
{
|
||||||
|
float _cos = cos(angle);
|
||||||
|
float _sin = sin(angle);
|
||||||
|
Matrix4 ret = G3D::Matrix4::identity();
|
||||||
|
ret[1][1] = _cos;
|
||||||
|
ret[1][2] = _sin;
|
||||||
|
ret[2][1] = -_sin;
|
||||||
|
ret[2][2] = _cos;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Utils::ToRadians( float degrees )
|
||||||
|
{
|
||||||
|
return Constants::PI * degrees / 180.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix matrix )
|
||||||
|
{
|
||||||
|
Vector3 ret;
|
||||||
|
ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1];
|
||||||
|
ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2];
|
||||||
|
ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Utils::GetPathBase( std::string path )
|
||||||
|
{
|
||||||
|
int lastIndex = path.find_last_of(".");
|
||||||
|
if (lastIndex != std::string::npos)
|
||||||
|
return path.substr(0, lastindex);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Vector3::Read( FILE* file )
|
||||||
|
{
|
||||||
|
Vector3 ret;
|
||||||
|
fread(&ret, sizeof(Vector3), 1, file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y)
|
||||||
|
{
|
||||||
|
if (Utils::Distance(height, 0.0f) > 0.5f)
|
||||||
|
basePosition.z = 0.0f;
|
||||||
|
return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Utils::Distance( float x, float y )
|
||||||
|
{
|
||||||
|
return sqrt(x*x + y*y);
|
||||||
|
}
|
||||||
@@ -0,0 +1,497 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "g3d/Matrix4.h"
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Constants.h"
|
||||||
|
|
||||||
|
struct Vector3
|
||||||
|
{
|
||||||
|
Vector3() {}
|
||||||
|
Vector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {}
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
Vector3 operator+(Vector3 right, Vector3 left)
|
||||||
|
{
|
||||||
|
return Vector3(right.x+left.x, right.y+left.y, right.z+left.z);
|
||||||
|
}
|
||||||
|
static Vector3 Read(FILE* file);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TilePos
|
||||||
|
{
|
||||||
|
TilePos(int x, int y) : X(x), Y(y) {}
|
||||||
|
int X;
|
||||||
|
int Y;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Triangle
|
||||||
|
{
|
||||||
|
Triangle(Constants::TriangleType type, T v0, T v1, T v2) : Type(type), V0(v0), V1(v1), V2(v2) {}
|
||||||
|
T V0;
|
||||||
|
T V1;
|
||||||
|
T V2;
|
||||||
|
Constants::TriangleType Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MapChunkHeader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapChunkHeader() {}
|
||||||
|
uint32 Flags;
|
||||||
|
uint32 IndexX;
|
||||||
|
uint32 IndexY;
|
||||||
|
uint32 Layers;
|
||||||
|
uint32 DoodadRefs;
|
||||||
|
uint32 OffsetMCVT;
|
||||||
|
uint32 OffsetMCNR;
|
||||||
|
uint32 OffsetMCLY;
|
||||||
|
uint32 OffsetMCRF;
|
||||||
|
uint32 OffsetMCAL;
|
||||||
|
uint32 SizeMCAL;
|
||||||
|
uint32 OffsetMCSH;
|
||||||
|
uint32 SizeMCSH;
|
||||||
|
uint32 AreaId;
|
||||||
|
uint32 MapObjectRefs;
|
||||||
|
uint32 Holes;
|
||||||
|
uint32* LowQualityTextureMap;
|
||||||
|
uint32 PredTex;
|
||||||
|
uint32 NumberEffectDoodad;
|
||||||
|
uint32 OffsetMCSE;
|
||||||
|
uint32 SoundEmitters;
|
||||||
|
uint32 OffsetMCLQ;
|
||||||
|
uint32 SizeMCLQ;
|
||||||
|
Vector3 Position;
|
||||||
|
uint32 OffsetMCCV;
|
||||||
|
|
||||||
|
void Read(FILE* stream)
|
||||||
|
{
|
||||||
|
fread(&Flags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&IndexX, sizeof(uint32), 1, stream);
|
||||||
|
fread(&IndexY, sizeof(uint32), 1, stream);
|
||||||
|
fread(&Layers, sizeof(uint32), 1, stream);
|
||||||
|
fread(&DoodadRefs, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCVT, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCNR, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCLY, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCRF, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCAL, sizeof(uint32), 1, stream);
|
||||||
|
fread(&SizeMCAL, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCSH, sizeof(uint32), 1, stream);
|
||||||
|
fread(&SizeMCSH, sizeof(uint32), 1, stream);
|
||||||
|
fread(&AreaId, sizeof(uint32), 1, stream);
|
||||||
|
fread(&MapObjectRefs, sizeof(uint32), 1, stream);
|
||||||
|
fread(&Holes, sizeof(uint32), 1, stream);
|
||||||
|
LowQualityTextureMap = new uint32[4];
|
||||||
|
fread(LowQualityTextureMap, sizeof(uint32), 4, stream);
|
||||||
|
fread(&PredTex, sizeof(uint32), 1, stream);
|
||||||
|
fread(&NumberEffectDoodad, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCSE, sizeof(uint32), 1, stream);
|
||||||
|
fread(&SoundEmitters, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCLQ, sizeof(uint32), 1, stream);
|
||||||
|
fread(&SizeMCLQ, sizeof(uint32), 1, stream);
|
||||||
|
Position = Vector3::Read(stream);
|
||||||
|
fread(&OffsetMCCV, sizeof(uint32), 1, stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MHDR
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MHDR() {}
|
||||||
|
uint32 Flags;
|
||||||
|
uint32 OffsetMCIN;
|
||||||
|
uint32 OffsetMTEX;
|
||||||
|
uint32 OffsetMMDX;
|
||||||
|
uint32 OffsetMMID;
|
||||||
|
uint32 OffsetMWMO;
|
||||||
|
uint32 OffsetMWID;
|
||||||
|
uint32 OffsetMDDF;
|
||||||
|
uint32 OffsetMODF;
|
||||||
|
uint32 OffsetMFBO;
|
||||||
|
uint32 OffsetMH2O;
|
||||||
|
uint32 OffsetMTFX;
|
||||||
|
|
||||||
|
void Read(FILE* stream)
|
||||||
|
{
|
||||||
|
fread(&Flags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMCIN, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMTEX, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMMDX, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMMID, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMWMO, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMWID, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMDDF, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMODF, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMFBO, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMH2O, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetMTFX, sizeof(uint32), 1, stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModelHeader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char Magic[5];
|
||||||
|
uint32 Version;
|
||||||
|
uint32 LengthModelName;
|
||||||
|
uint32 OffsetName;
|
||||||
|
uint32 ModelFlags;
|
||||||
|
uint32 CountGlobalSequences;
|
||||||
|
uint32 OffsetGlobalSequences;
|
||||||
|
uint32 CountAnimations;
|
||||||
|
uint32 OffsetAnimations;
|
||||||
|
uint32 CountAnimationLookup;
|
||||||
|
uint32 OffsetAnimationLookup;
|
||||||
|
uint32 CountBones;
|
||||||
|
uint32 OffsetBones;
|
||||||
|
uint32 CountKeyBoneLookup;
|
||||||
|
uint32 OffsetKeyBoneLookup;
|
||||||
|
uint32 CountVertices;
|
||||||
|
uint32 OffsetVertices;
|
||||||
|
uint32 CountViews;
|
||||||
|
uint32 CountColors;
|
||||||
|
uint32 OffsetColors;
|
||||||
|
uint32 CountTextures;
|
||||||
|
uint32 OffsetTextures;
|
||||||
|
uint32 CountTransparency;
|
||||||
|
uint32 OffsetTransparency;
|
||||||
|
uint32 CountUvAnimation;
|
||||||
|
uint32 OffsetUvAnimation;
|
||||||
|
uint32 CountTexReplace;
|
||||||
|
uint32 OffsetTexReplace;
|
||||||
|
uint32 CountRenderFlags;
|
||||||
|
uint32 OffsetRenderFlags;
|
||||||
|
uint32 CountBoneLookup;
|
||||||
|
uint32 OffsetBoneLookup;
|
||||||
|
uint32 CountTexLookup;
|
||||||
|
uint32 OffsetTexLookup;
|
||||||
|
uint32 CountTexUnits;
|
||||||
|
uint32 OffsetTexUnits;
|
||||||
|
uint32 CountTransLookup;
|
||||||
|
uint32 OffsetTransLookup;
|
||||||
|
uint32 CountUvAnimLookup;
|
||||||
|
uint32 OffsetUvAnimLookup;
|
||||||
|
Vector3 VertexBox[2];
|
||||||
|
float VertexRadius;
|
||||||
|
Vector3 BoundingBox[2];
|
||||||
|
float BoundingRadius;
|
||||||
|
uint32 CountBoundingTriangles;
|
||||||
|
uint32 OffsetBoundingTriangles;
|
||||||
|
uint32 CountBoundingVertices;
|
||||||
|
uint32 OffsetBoundingVertices;
|
||||||
|
uint32 CountBoundingNormals;
|
||||||
|
uint32 OffsetBoundingNormals;
|
||||||
|
|
||||||
|
void Read(FILE* stream)
|
||||||
|
{
|
||||||
|
fread(&Magic, sizeof(char), 4, stream);
|
||||||
|
Magic[4] = '\0'; // null-terminate it.
|
||||||
|
fread(&Version, sizeof(uint32), 1, stream);
|
||||||
|
fread(&LengthModelName, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetName, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ModelFlags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountGlobalSequences, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountAnimations, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetAnimations, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountAnimationLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountBones, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetBones, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountViews, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountColors, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetColors, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTextures, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTextures, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTransparency, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTransparency, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountUvAnimation, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetUvAnimation, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTexReplace, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTexReplace, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountRenderFlags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetRenderFlags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountBoneLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetBoneLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTexLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTexLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTexUnits, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTexUnits, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTransLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTransLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountUvAnimLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountColors, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetColors, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTextures, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTextures, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTransparency, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTransparency, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountUvAnimation, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetUvAnimation, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountTexReplace, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetTexReplace, sizeof(uint32), 1, stream);
|
||||||
|
VertexBox[0] = Vector3::Read(stream);
|
||||||
|
VertexBox[1] = Vector3::Read(stream);
|
||||||
|
fread(&VertexRadius, sizeof(float), 1, stream);
|
||||||
|
BoundingBox[0] = Vector3::Read(stream);
|
||||||
|
BoundingBox[1] = Vector3::Read(stream);
|
||||||
|
fread(&BoundingRadius, sizeof(float), 1, stream);
|
||||||
|
fread(&CountBoundingTriangles, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountBoundingVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&CountBoundingNormals, sizeof(uint32), 1, stream);
|
||||||
|
fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class WorldModelHeader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorldModelHeader() {}
|
||||||
|
uint32 CountMaterials;
|
||||||
|
uint32 CountGroups;
|
||||||
|
uint32 CountPortals;
|
||||||
|
uint32 CountLights;
|
||||||
|
uint32 CountModels;
|
||||||
|
uint32 CountDoodads;
|
||||||
|
uint32 CountSets;
|
||||||
|
uint32 AmbientColorUnk;
|
||||||
|
uint32 WmoId;
|
||||||
|
Vector3 BoundingBox[2];
|
||||||
|
uint32 LiquidTypeRelated;
|
||||||
|
|
||||||
|
static WorldModelHeader Read(FILE* stream)
|
||||||
|
{
|
||||||
|
WorldModelHeader ret;
|
||||||
|
fread(&ret.CountMaterials, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountGroups, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountPortals, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountLights, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountModels, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountDoodads, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountSets, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.WmoId, sizeof(uint32), 1, stream);
|
||||||
|
ret.BoundingBox[0] = Vector3::Read(stream);
|
||||||
|
ret.BoundingBox[1] = Vector3::Read(stream);
|
||||||
|
fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DoodadInstance
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DoodadInstance() {}
|
||||||
|
uint32 FileOffset;
|
||||||
|
std::string File;
|
||||||
|
Vector3 Position;
|
||||||
|
float QuatW;
|
||||||
|
float QuatX;
|
||||||
|
float QuatY;
|
||||||
|
float QuatZ;
|
||||||
|
float Scale;
|
||||||
|
uint32 LightColor;
|
||||||
|
|
||||||
|
static DoodadInstance Read(FILE* stream)
|
||||||
|
{
|
||||||
|
DoodadInstance ret;
|
||||||
|
fread(&ret.FileOffset, sizeof(uint32), 1, stream);
|
||||||
|
ret.Position = Vector3::Read(stream);
|
||||||
|
fread(&ret.QuatW, sizeof(float), 1, stream);
|
||||||
|
fread(&ret.QuatX, sizeof(float), 1, stream);
|
||||||
|
fread(&ret.QuatY, sizeof(float), 1, stream);
|
||||||
|
fread(&ret.QuatZ, sizeof(float), 1, stream);
|
||||||
|
fread(&ret.Scale, sizeof(float), 1, stream);
|
||||||
|
fread(&ret.LightColor, sizeof(uint32), 1, stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DoodadSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DoodadSet() {}
|
||||||
|
std::string Name;
|
||||||
|
uint32 FirstInstanceIndex;
|
||||||
|
uint32 CountInstances;
|
||||||
|
uint32 UnknownZero;
|
||||||
|
|
||||||
|
static DoodadSet Read(FILE* stream)
|
||||||
|
{
|
||||||
|
DoodadSet ret;
|
||||||
|
char name[21];
|
||||||
|
fread(&name, sizeof(char), 20, stream);
|
||||||
|
name[20] = '\0';
|
||||||
|
ret.Name = name;
|
||||||
|
fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountInstances, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.UnknownZero, sizeof(uint32), 1, stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LiquidHeader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LiquidHeader() {}
|
||||||
|
uint32 CountXVertices;
|
||||||
|
uint32 CountYVertices;
|
||||||
|
uint32 Width;
|
||||||
|
uint32 Height;
|
||||||
|
Vector3 BaseLocation;
|
||||||
|
uint16 MaterialId;
|
||||||
|
|
||||||
|
static LiquidHeader Read(FILE* stream)
|
||||||
|
{
|
||||||
|
LiquidHeader ret;
|
||||||
|
fread(&ret.CountXVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.CountYVertices, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.Width, sizeof(uint32), 1, stream);
|
||||||
|
fread(&ret.Height, sizeof(uint32), 1, stream);
|
||||||
|
ret.BaseLocation = Vector3::Read(stream);
|
||||||
|
fread(&ret.MaterialId, sizeof(uint16), 1, stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class LiquidData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LiquidData() {}
|
||||||
|
float** HeightMap;
|
||||||
|
uint8** RenderFlags;
|
||||||
|
|
||||||
|
bool ShouldRender(int x, int y)
|
||||||
|
{
|
||||||
|
return RenderFlags[x][y] != 0x0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LiquidData Read(FILE* stream, LiquidHeader header)
|
||||||
|
{
|
||||||
|
LiquidData ret;
|
||||||
|
ret.HeightMap = new float*[header.CountXVertices];
|
||||||
|
for (int i = 0; i < header.CountXVertices; ++i)
|
||||||
|
ret.HeightMap[i] = new float[header.CountYVertices];
|
||||||
|
|
||||||
|
ret.RenderFlags = new uint8*[header.Width];
|
||||||
|
for (int i = 0; i < header.Width; ++i)
|
||||||
|
ret.RenderFlags[i] = new uint8[header.Height];
|
||||||
|
|
||||||
|
for (int y = 0; y < header.CountYVertices; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < header.CountXVertices; x++)
|
||||||
|
{
|
||||||
|
uint32 discard;
|
||||||
|
fread(&discard, sizeof(uint32), 1, stream);
|
||||||
|
float tmp;
|
||||||
|
fread(&tmp, sizeof(float), 1, stream);
|
||||||
|
ret.HeightMap[x][y] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < header.Height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < header.Width; x++)
|
||||||
|
{
|
||||||
|
uint8 tmp;
|
||||||
|
fread(&tmp, sizeof(uint8), 1, stream);
|
||||||
|
ret.RenderFlags[x][y] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class H2ORenderMask
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
H2ORenderMask() {}
|
||||||
|
uint8 Mask[8];
|
||||||
|
|
||||||
|
bool ShouldRender(int x, int y)
|
||||||
|
{
|
||||||
|
return (Mask[y] >> x & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static H2ORenderMask Read(FILE* stream)
|
||||||
|
{
|
||||||
|
H2ORenderMask ret;
|
||||||
|
fread(&ret.Mask, sizeof(uint8), 8, stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MCNKLiquidData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MCNKLiquidData() {}
|
||||||
|
const float MaxStandableHeight = 1.5f;
|
||||||
|
|
||||||
|
float** Heights;
|
||||||
|
H2ORenderMask* Mask;
|
||||||
|
|
||||||
|
bool IsWater(int x, int y, float height)
|
||||||
|
{
|
||||||
|
if (!Heights || !Mask)
|
||||||
|
return false;
|
||||||
|
if (!Mask->ShouldRender(x, y))
|
||||||
|
return false;
|
||||||
|
float diff = Heights[x][y] - height;
|
||||||
|
if (diff > MaxStandableHeight)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dummy class to act as an interface.
|
||||||
|
class IDefinition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Vector3 Position;
|
||||||
|
Vector3 Rotation;
|
||||||
|
virtual float Scale() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Reverse(char word[]);
|
||||||
|
static std::string ReadString(FILE* file);
|
||||||
|
static uint32 Size(FILE* file);
|
||||||
|
static Vector3 ToRecast( Vector3 val );
|
||||||
|
static std::string GetAdtPath(std::string world, int x, int y);
|
||||||
|
static std::string FixModelPath(std::string path);
|
||||||
|
static G3D::Matrix4 GetTransformation(IDefinition def);
|
||||||
|
/// They say its better to declare template functions in the header files.
|
||||||
|
template <typename T>
|
||||||
|
static std::string ToString(T val)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << val;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
static G3D::Matrix4 RotationX(float angle);
|
||||||
|
static G3D::Matrix4 RotationY(float angle);
|
||||||
|
static G3D::Matrix4 RotationZ(float angle);
|
||||||
|
static float ToRadians(float degrees);
|
||||||
|
static Vector3 VectorTransform(Vector3 vec, G3D::Matrix matrix);
|
||||||
|
static std::string GetPathBase(std::string path);
|
||||||
|
static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y);
|
||||||
|
static float Distance(float x, float y);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include "WDT.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "WorldModelHandler.h"
|
||||||
|
|
||||||
|
WDT::WDT(std::string file) : IsValid(false), IsGlobalModel(false)
|
||||||
|
{
|
||||||
|
Data = new ChunkedData(file, 2);
|
||||||
|
ReadTileTable();
|
||||||
|
ReadGlobalModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WDT::ReadGlobalModel()
|
||||||
|
{
|
||||||
|
Chunk* fileChunk = Data->GetChunkByName("MWMO");
|
||||||
|
Chunk* defChunk = Data->GetChunkByName("MODF");
|
||||||
|
if (!fileChunk || !defChunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsGlobalModel = true;
|
||||||
|
ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream());
|
||||||
|
ModelFile = Utils::ReadString(fileChunk->GetStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WDT::ReadTileTable()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Data->GetChunkByName("MAIN");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
IsValid = true;
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
for (int y = 0; y < 64; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 64; ++x)
|
||||||
|
{
|
||||||
|
const uint32 hasTileFlag = 0x1;
|
||||||
|
uint32 flags;
|
||||||
|
uint32 discard;
|
||||||
|
fread(&flags, sizeof(uint32), 1, stream);
|
||||||
|
fread(&discard, sizeof(uint32), 1, stream);
|
||||||
|
if (flags & hasTileFlag)
|
||||||
|
TileTable.push_back(TilePos(x, y));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WDT::HasTile( int x, int y )
|
||||||
|
{
|
||||||
|
for (std::vector<TilePos>::iterator itr = TileTable.begin(); itr != TileTable.end(); ++itr)
|
||||||
|
if (itr->X == x && itr->Y == y)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef WDT_H
|
||||||
|
#define WDT_H
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "WorldModelHandler.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
class WDT
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WDT(std::string file);
|
||||||
|
|
||||||
|
ChunkedData* Data;
|
||||||
|
std::vector<TilePos> TileTable;
|
||||||
|
bool IsGlobalModel;
|
||||||
|
bool IsValid;
|
||||||
|
std::string ModelFile;
|
||||||
|
WorldModelDefinition ModelDefinition;
|
||||||
|
bool HasTile(int x, int y);
|
||||||
|
private:
|
||||||
|
void ReadGlobalModel();
|
||||||
|
void ReadTileTable();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
#include "WorldModelGroup.h"
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
|
||||||
|
WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), IsBad(false)
|
||||||
|
{
|
||||||
|
Data = new ChunkedData(path);
|
||||||
|
if (!Data->Stream)
|
||||||
|
{
|
||||||
|
IsBad = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Chunk* mainChunk = Data->GetChunkByName("MOGP");
|
||||||
|
int32 firstSub = mainChunk->FindSubChunkOffset("MOPY");
|
||||||
|
if (firstSub == -1)
|
||||||
|
return;
|
||||||
|
FILE* stream = mainChunk->GetStream();
|
||||||
|
fseek(stream, firstSub, SEEK_SET);
|
||||||
|
SubData = new ChunkedData(stream, mainChunk->Length - firstSub);
|
||||||
|
|
||||||
|
ReadBoundingBox();
|
||||||
|
ReadMaterials();
|
||||||
|
ReadTriangles();
|
||||||
|
ReadVertices();
|
||||||
|
ReadNormals();
|
||||||
|
ReadLiquid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadNormals()
|
||||||
|
{
|
||||||
|
Chunk* chunk = SubData->GetChunkByName("MONR");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 normalCount = chunk->Length / 12;
|
||||||
|
Assert(normalCount == Vertices.size(), "normalCount is different than the Vertices count");
|
||||||
|
Normals.reserve(normalCount);
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
for (int i = 0; i < normalCount; i++)
|
||||||
|
Normals[i] = Vector3::Read(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadLiquid()
|
||||||
|
{
|
||||||
|
Chunk* chunk = SubData->GetChunkByName("MLIQ");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HasLiquidData = true;
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
LiquidDataHeader = LiquidHeader.Read(stream);
|
||||||
|
LiquidDataGeometry = LiquidData.Read(stream, LiquidDataHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadVertices()
|
||||||
|
{
|
||||||
|
Chunk* chunk = SubData->GetChunkByName("MOVT");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 verticeCount = chunk.Length / 12;
|
||||||
|
Vertices.reserve(verticeCount);
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
for (int i = 0; i < verticeCount; i++)
|
||||||
|
Vertices.push_back(Vector3::Read(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadTriangles()
|
||||||
|
{
|
||||||
|
Chunk* chunk = SubData->GetChunkByName("MOVI");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 triangleCount = chunk.Length / 6;
|
||||||
|
Assert(triangleCount == TriangleFlags.size(), "triangleCount != TriangleFlags.size()");
|
||||||
|
FILE* stream = chunk.GetStream();
|
||||||
|
Triangles.reserve(triangleCount);
|
||||||
|
for (int i = 0; i < triangleCount; i++)
|
||||||
|
{
|
||||||
|
uint16 v0;
|
||||||
|
uint16 v1;
|
||||||
|
uint16 v2;
|
||||||
|
fread(&v0, sizeof(uint16), 1, stream);
|
||||||
|
fread(&v1, sizeof(uint16), 1, stream);
|
||||||
|
fread(&v2, sizeof(uint16), 1, stream);
|
||||||
|
Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadMaterials()
|
||||||
|
{
|
||||||
|
Chunk* chunk = SubData->GetChunkByName("MOPY");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
uint32 triangleCount = chunk.Length / 2;
|
||||||
|
TriangleFlags.reserve(triangleCount);
|
||||||
|
TriangleMaterials.reserve(triangleCount);
|
||||||
|
for (int i = 0; i < triangleCount; i++)
|
||||||
|
{
|
||||||
|
uint8 tmp;
|
||||||
|
fread(&tmp, sizeof(uint8), 1, stream);
|
||||||
|
TriangleFlags.push_back(tmp);
|
||||||
|
// Read again for material.
|
||||||
|
fread(&tmp, sizeof(uint8), 1, stream);
|
||||||
|
TriangleMaterials.push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelGroup::ReadBoundingBox()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Data->GetChunkByName("MOGP");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
fseek(stream, 8, SEEK_CUR):
|
||||||
|
fread(&Flags, sizeof(uint32), 1, stream);
|
||||||
|
BoundingBox[0] = Vector3::Read(stream);
|
||||||
|
BoundingBox[1] = Vector3::Read(stream);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef WMOGROUP_H
|
||||||
|
#define WMOGROUP_H
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
class WorldModelGroup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorldModelGroup(std::string path, int groupIndex);
|
||||||
|
ChunkedData* Data;
|
||||||
|
ChunkedData* SubData;
|
||||||
|
int GroupIndex;
|
||||||
|
Vector3 BoundingBox[2];
|
||||||
|
uint32 Flags;
|
||||||
|
std::vector<uint8> TriangleFlags;
|
||||||
|
std::vector<uint8> TriangleMaterials;
|
||||||
|
std::vector<Triangle<uint16> > Triangles;
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Vector3> Normals;
|
||||||
|
|
||||||
|
bool HasLiquidData;
|
||||||
|
bool IsBad;
|
||||||
|
LiquidHeader LiquidDataHeader;
|
||||||
|
LiquidData LiquidDataGeometry;
|
||||||
|
private:
|
||||||
|
void ReadNormals();
|
||||||
|
void ReadLiquid();
|
||||||
|
void ReadVertices();
|
||||||
|
void ReadTriangles();
|
||||||
|
void ReadMaterials();
|
||||||
|
void ReadBoundingBox();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
#include "WorldModelHandler.h"
|
||||||
|
#include "Chunk.h"
|
||||||
|
#include "Cache.h"
|
||||||
|
#include "Common.h"
|
||||||
|
#include "g3d/Matrix4.h"
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
WorldModelDefinition WorldModelDefinition::Read( FILE* file )
|
||||||
|
{
|
||||||
|
WorldModelDefinition ret;
|
||||||
|
fread(&ret.MwidIndex, sizeof(uint32), 1, file);
|
||||||
|
fread(&ret.UniqueId, sizeof(uint32), 1, file);
|
||||||
|
ret.Position = Vector3::Read(file);
|
||||||
|
ret.Rotation = Vector3::Read(file);
|
||||||
|
ret.UpperExtents = Vector3::Read(file);
|
||||||
|
ret.LowerExtents = Vector3::Read(file);
|
||||||
|
fread(&ret.Flags, sizeof(uint16), 1, file);
|
||||||
|
fread(&ret.DooadSet, sizeof(uint16), 1, file);
|
||||||
|
uint32 discard;
|
||||||
|
fread(&discard, sizeof(uint32), 1, file);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt)
|
||||||
|
{
|
||||||
|
if (!adt->HasObjectData)
|
||||||
|
return;
|
||||||
|
ReadModelPaths();
|
||||||
|
ReadDefinitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelHandler::ProcessInternal( ChunkedData* subChunks )
|
||||||
|
{
|
||||||
|
if (!IsSane())
|
||||||
|
return;
|
||||||
|
Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW");
|
||||||
|
if (!wmoReferencesChunk)
|
||||||
|
return;
|
||||||
|
FILE* stream = wmoReferencesChunk->GetStream();
|
||||||
|
uint32 refCount = wmoReferencesChunk->Length / 4;
|
||||||
|
for (int i = 0; i < refCount; i++)
|
||||||
|
{
|
||||||
|
int32 index;
|
||||||
|
fread(&index, sizeof(int32), 1, stream);
|
||||||
|
|
||||||
|
if (index < 0 || index >= _definitions->size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
WorldModelDefinition wmo = (*_definitions)[index];
|
||||||
|
|
||||||
|
if (_drawn.find(wmo.UniqueId) != _drawn.end())
|
||||||
|
continue;
|
||||||
|
_drawn.insert(wmo.UniqueId);
|
||||||
|
|
||||||
|
if (wmo.MwidIndex >= _paths->size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string path = _paths[(int) wmo.MwidIndex];
|
||||||
|
WorldModelRoot* model = Cache->WorldModelCache.Get(path);
|
||||||
|
if (!model)
|
||||||
|
{
|
||||||
|
model = new WorldModelRoot(path);
|
||||||
|
Cache->WorldModelCache.Insert(path, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vertices.reserve(1000);
|
||||||
|
Triangles.reserve(1000);
|
||||||
|
|
||||||
|
InsertModelGeometry(Vertices, Triangles, wmo, model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelHandler::InsertModelGeometry( std::vector<Vector3> verts, std::vector<Triangle<uint32> > tris, WorldModelDefinition& def, WorldModelRoot* root )
|
||||||
|
{
|
||||||
|
G3D::Matrix4 transformation = Utils::GetTransformation();
|
||||||
|
for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
|
||||||
|
{
|
||||||
|
uint32 vertOffset = verts.size();
|
||||||
|
for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2)
|
||||||
|
verts.push_back(Utils::VectorTransform(*itr2, transformation));
|
||||||
|
|
||||||
|
for (int i = 0; i < group->Triangles.size(); ++i)
|
||||||
|
{
|
||||||
|
// only include collidable tris
|
||||||
|
if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF)
|
||||||
|
continue;
|
||||||
|
Triangle<uint16> tri = group->Triangles[i];
|
||||||
|
tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, tri.V0 + vertOffset, tri.V1 + vertOffset, tri.V2 + vertOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def.DoodadSet >= 0 && def.DoodadSet < root->DoodadSets.size())
|
||||||
|
{
|
||||||
|
DoodadSet set = root->DoodadSets[def.DoodadSet];
|
||||||
|
std::vector<DoodadInstance> instances = new std::vector<DoodadInstance>;
|
||||||
|
instances.reserve(set.CountInstances);
|
||||||
|
for (uint32 i = set.FirstInstanceIndex; i < (set.CountInstances + set.FirstInstanceIndex); i++)
|
||||||
|
{
|
||||||
|
if (i >= root->DoodadInstances.size())
|
||||||
|
break;
|
||||||
|
instances.push_back(root->DoodadInstances[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance)
|
||||||
|
{
|
||||||
|
Model* model = Cache.ModelCache.Get(instance->File);
|
||||||
|
if (!model)
|
||||||
|
{
|
||||||
|
model = new Model(instance->File);
|
||||||
|
Cache.ModelCache.Insert(instance->File, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!model.IsCollidable)
|
||||||
|
continue;
|
||||||
|
G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(instance, def);
|
||||||
|
int vertOffset = verts.size();
|
||||||
|
for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2)
|
||||||
|
verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation));
|
||||||
|
for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2)
|
||||||
|
tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group)
|
||||||
|
{
|
||||||
|
if (!group->HasLiquidData)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (int y = 0; y < group->LiquidDataHeader.Height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < group->LiquidDataHeader.Width; x++)
|
||||||
|
{
|
||||||
|
if (!group->LiquidDataGeometry.ShouldRender(x, y))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32 vertOffset = verts.size();
|
||||||
|
verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
|
||||||
|
group->LiquidDataGeometry.HeightMap[x][y], x, y));
|
||||||
|
verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
|
||||||
|
group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y));
|
||||||
|
verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
|
||||||
|
group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1));
|
||||||
|
verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation,
|
||||||
|
group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1));
|
||||||
|
|
||||||
|
tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1));
|
||||||
|
tris.push_back(new Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelHandler::ReadDefinitions()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Source->ObjectData->GetChunkByName("MODF");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int32 definitionSize = 64;
|
||||||
|
uint32 definitionCount = chunk.Length / definitionSize;
|
||||||
|
_definitions = new new std::vector<WorldModelDefinition>;
|
||||||
|
_definitions->reserve(definitionCount);
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
for (int i = 0; i < definitionCount; i++)
|
||||||
|
_definitions->push_back(WorldModelDefinition::Read(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelHandler::ReadModelPaths()
|
||||||
|
{
|
||||||
|
Chunk* mwid = Source->ObjectData->GetChunkByName("MWID");
|
||||||
|
Chunk* mwmo = Source->ObjectData->GetChunkByName("MWMO");
|
||||||
|
if (!mwid || !mwmo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 paths = mwid.Length / 4;
|
||||||
|
_paths = new std::vector<std::string>;
|
||||||
|
_paths->reserve(paths);
|
||||||
|
for (int i = 0; i < paths; i++)
|
||||||
|
{
|
||||||
|
FILE* stream = mwid->GetStream();
|
||||||
|
fseek(stream, i * 4, SEEK_CUR);
|
||||||
|
uint32 offset;
|
||||||
|
fread(&offset, sizeof(uint32), 1, stream);
|
||||||
|
FILE* dataStream = mwmo->GetStream();
|
||||||
|
fseek(dataStream, offset + mwmo->Offset, SEEK_SET);
|
||||||
|
_paths->push_back(Utils::ReadString(dataStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef WMODEL_HNDL_H
|
||||||
|
#define WMODEL_HNDL_H
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "ObjectDataHandler.h"
|
||||||
|
#include "ADT.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct WorldModelDefinition : IDefinition
|
||||||
|
{
|
||||||
|
WorldModelDefinition() {}
|
||||||
|
uint32 MwidIndex;
|
||||||
|
uint32 UniqueId;
|
||||||
|
Vector3 UpperExtents;
|
||||||
|
Vector3 LowerExtents;
|
||||||
|
uint16 Flags;
|
||||||
|
uint16 DoodadSet;
|
||||||
|
float Scale() { return 1.0f; }
|
||||||
|
static WorldModelDefinition Read(FILE* file);
|
||||||
|
};
|
||||||
|
|
||||||
|
class WorldModelHandler : public ObjectDataHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorldModelHandler(ADT* adt);
|
||||||
|
|
||||||
|
std::vector<Vector3> Vertices;
|
||||||
|
std::vector<Triangle<uint32> > Triangles;
|
||||||
|
bool IsSane() { return _definitions && _paths; }
|
||||||
|
void InsertModelGeometry(std::vector<Vector3> verts, std::vector<Triangle<uint32> > tris, WorldModelDefinition& def, WorldModelRoot* root);
|
||||||
|
protected:
|
||||||
|
void ProcessInternal(ChunkedData* data);
|
||||||
|
private:
|
||||||
|
void ReadDefinitions();
|
||||||
|
void ReadModelPaths();
|
||||||
|
std::set<uint32> _drawn;
|
||||||
|
std::vector<WorldModelDefinition>* _definitions;
|
||||||
|
std::vector<std::string>* _paths;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#include "WorldModelRoot.h"
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
WorldModelRoot::WorldModelRoot( std::string path )
|
||||||
|
{
|
||||||
|
Data = new ChunkedData(path);
|
||||||
|
Path = path;
|
||||||
|
ReadHeader();
|
||||||
|
ReadGroups();
|
||||||
|
ReadDoodadInstances();
|
||||||
|
ReadDoodadSets();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelRoot::ReadGroups()
|
||||||
|
{
|
||||||
|
std::string pathBase = Utils::GetPathBase(Path);
|
||||||
|
Groups.reserve(Header.CountGroups);
|
||||||
|
for (int i = 0; i < Header.CountGroups; i++)
|
||||||
|
{
|
||||||
|
char name[200];
|
||||||
|
sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i);
|
||||||
|
WorldModelGroup group(name, i);
|
||||||
|
if (!group.IsBad)
|
||||||
|
Groups.push_back(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelRoot::ReadDoodadSets()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Data->GetChunkByName("MODS");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
Assert(chunk.Length / 32 == Header.CountSets, "chunk.Length / 32 == Header.CountSets");
|
||||||
|
DoodadSets.reserve(Header.CountSets);
|
||||||
|
for (int i = 0; i < Header.CountSets; i++)
|
||||||
|
DoodadSets.push_back(DoodadSet::Read(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelRoot::ReadDoodadInstances()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Data->GetChunkByName("MODD");
|
||||||
|
Chunk* nameChunk = Data->GetChunkByName("MODN");
|
||||||
|
if (!chunk || !nameChunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint32 instanceSize = 40;
|
||||||
|
uint32 countInstances = chunk->Length / instanceSize;
|
||||||
|
DoodadInstances.reserve(countInstances);
|
||||||
|
for (int i = 0; i < countInstances; i++)
|
||||||
|
{
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
fseek(stream, instanceSize * i, SEEK_CUR);
|
||||||
|
DoodadInstance instance = DoodadInstance::Read(stream);
|
||||||
|
FILE* nameStream = nameChunk->GetStream();
|
||||||
|
if (instance.FileOffset >= nameChunk->Length)
|
||||||
|
continue;
|
||||||
|
fseek(nameStream, instance.FileOffset, SEEK_CUR);
|
||||||
|
instance.File = Utils::ReadString(nameStream);
|
||||||
|
DoodadInstances.push_back(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldModelRoot::ReadHeader()
|
||||||
|
{
|
||||||
|
Chunk* chunk = Data->GetChunkByName("MOHD");
|
||||||
|
if (!chunk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE* stream = chunk->GetStream();
|
||||||
|
Header = WorldModelHeader::Read(stream);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef WMOROOT_H
|
||||||
|
#define WMOROOT_H
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ChunkedData.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include "WorldModelGroup.h"
|
||||||
|
|
||||||
|
class WorldModelRoot
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WorldModelRoot(std::string path);
|
||||||
|
std::string Path;
|
||||||
|
ChunkedData* Data;
|
||||||
|
WorldModelHeader Header;
|
||||||
|
std::vector<DoodadInstance> DoodadInstances;
|
||||||
|
std::vector<DoodadSet> DoodadSets;
|
||||||
|
std::vector<WorldModelGroup> Groups;
|
||||||
|
private:
|
||||||
|
void ReadGroups();
|
||||||
|
void ReadDoodadSets();
|
||||||
|
void ReadDoodadInstances();
|
||||||
|
void ReadHeader();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
Experimental mesh extractor.
|
||||||
|
Original work in C# by stschake
|
||||||
|
Thanks to:
|
||||||
|
Subv
|
||||||
|
~
|
||||||
|
For helping in the porting to C++
|
||||||
Reference in new issue
Block a user