diff --git a/bin/data/misc/square.jpg b/bin/data/misc/square.jpg new file mode 100644 index 0000000..83f250d Binary files /dev/null and b/bin/data/misc/square.jpg differ diff --git a/bin/scripts/__core_func.def b/bin/scripts/__core_func.def index 11156ec..485ec39 100644 --- a/bin/scripts/__core_func.def +++ b/bin/scripts/__core_func.def @@ -53,6 +53,9 @@ LOADSCP,race data/scp/race.scp EXLOADSCP,sound data/scp/sound.scp EXLOADSCP,emote data/scp/emote.scp EXLOADSCP,zone data/scp/zone.scp +EXLOADSCP,creaturedisplayinfo data/scp/creaturedisplayinfo.scp +EXLOADSCP,creaturemodeldata data/scp/creaturemodeldata.scp +EXLOADSCP,npcsound data/scp/npcsound.scp LOG * SCP loaded. diff --git a/src/Client/GUI/CM2MeshFileLoader.cpp b/src/Client/GUI/CM2MeshFileLoader.cpp new file mode 100644 index 0000000..077aadd --- /dev/null +++ b/src/Client/GUI/CM2MeshFileLoader.cpp @@ -0,0 +1,128 @@ +#include +#include "CM2MeshFileLoader.h" + +namespace irr +{ + namespace scene + { + + CM2MeshFileLoader::CM2MeshFileLoader(IrrlichtDevice* device):Device(device) + { + Mesh = NULL; + aniMesh = NULL; + } + + CM2MeshFileLoader::~CM2MeshFileLoader() + { + + } + + + bool CM2MeshFileLoader::isALoadableFileExtension(const c8* filename) + { + return strstr(filename, ".m2")!=0; + } + + + //! creates/loads an animated mesh from the file. + //! \return Pointer to the created mesh. Returns 0 if loading failed. + //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). + //! See IUnknown::drop() for more information. + IAnimatedMesh* CM2MeshFileLoader::createMesh(io::IReadFile* file) + { + ILogger* logger =Device->getLogger(); + + logger->log("Trying to open file",file->getFileName(),ELL_INFORMATION); + + + file->read(&header,sizeof(ModelHeader)); + if (header.version[0] != 4 && header.version[1] != 1 && header.version[2] != 0 && header.version[3] != 0) { + logger->log("Something wrong!",ELL_ERROR); + return 0; + } + else logger->log(L"header okay",ELL_INFORMATION); + //Name -> not very important I think, but save it nontheless; + file->seek(header.nameOfs); + file->read(&M2MeshName[0],header.nameLength); + logger->log("Mesh Name",M2MeshName.c_str(),ELL_INFORMATION); + + ModelVertex tempM2MVert; + file->seek(header.ofsVertices); + for(u32 i =0;iread(&tempM2MVert,sizeof(ModelVertex)); + M2MVertices.push_back(tempM2MVert); + } + std::cout << "Read "<seek(header.ofsViews); + for(u32 i =0;iread(&tempM2MView,sizeof(ModelView)); + M2MViews.push_back(tempM2MView); + } + std::cout << "Read "<log("Using View 0 for all further operations",ELL_INFORMATION); + + + u16 tempM2Index; + file->seek(M2MViews[0].ofsIndex); + for(u32 i =0;iread(&tempM2Index,sizeof(u16)); + M2MIndices.push_back(tempM2Index); + } + std::cout << "Read "<seek(M2MViews[0].ofsTris); + for(u32 i =0;iread(&tempM2Triangle,sizeof(u16)); + M2MTriangles.push_back(tempM2Triangle); + } + std::cout << "Read "<Vertices.size(); i++) + IMB->Vertices.push_back(M2Vertices[i]); + for(u32 i = 0; i < IMB->Indices.size(); i++) + IMB->Indices.push_back(M2Indices[i]); + // irr 1.4 + //IMB->append(M2Vertices.const_pointer(),M2Vertices.size(),M2Indices.const_pointer(),M2Indices.size()); + + //Device->getSceneManager()->getMeshManipulator()->recalculateNormals(IMB,false); + IMB->recalculateBoundingBox(); + Mesh->addMeshBuffer(IMB); + //IMesh* tangentMesh = Device->getSceneManager()->getMeshManipulator()->createMeshWithTangents(Mesh); + aniMesh= new SAnimatedMesh(); + aniMesh->addMesh(Mesh); + aniMesh->recalculateBoundingBox(); + + IMB->drop(); + Mesh->drop(); + + return aniMesh; + } + + } +} diff --git a/src/Client/GUI/CM2MeshFileLoader.h b/src/Client/GUI/CM2MeshFileLoader.h new file mode 100644 index 0000000..f3aedf4 --- /dev/null +++ b/src/Client/GUI/CM2MeshFileLoader.h @@ -0,0 +1,151 @@ +#include "irrlicht/irrlicht.h" +#include "irrlicht/IMeshLoader.h" + + +namespace irr +{ + namespace scene + { + + class CM2MeshFileLoader : public IMeshLoader + { + public: + + //! Constructor + CM2MeshFileLoader(IrrlichtDevice* device); + + //! destructor + ~CM2MeshFileLoader(); + + //! returns true if the file maybe is able to be loaded by this class + //! based on the file extension (e.g. ".cob") + virtual bool isALoadableFileExtension(const c8* fileName); + + //! creates/loads an animated mesh from the file. + //! \return Pointer to the created mesh. Returns 0 if loading failed. + //! If you no longer need the mesh, you should call IAnimatedMesh::drop(). + //! See IUnknown::drop() for more information. + virtual scene::IAnimatedMesh* createMesh(irr::io::IReadFile* file); + private: + + + struct ModelHeader { + c8 id[4]; + u8 version[4]; + u32 nameLength; + u32 nameOfs; + u32 type; + + u32 nGlobalSequences; + u32 ofsGlobalSequences; + u32 nAnimations; + u32 ofsAnimations; + u32 nC; + u32 ofsC; + u32 nD; + u32 ofsD; + u32 nBones; + u32 ofsBones; + u32 nF; + u32 ofsF; + + u32 nVertices; + u32 ofsVertices; + u32 nViews; + u32 ofsViews; + + u32 nColors; + u32 ofsColors; + + u32 nTextures; + u32 ofsTextures; + + u32 nTransparency; // H + u32 ofsTransparency; + u32 nI; // always unused ? + u32 ofsI; + u32 nTexAnims; // J + u32 ofsTexAnims; + u32 nTexReplace; + u32 ofsTexReplace; + + u32 nTexFlags; + u32 ofsTexFlags; + u32 nY; + u32 ofsY; + + u32 nTexLookup; + u32 ofsTexLookup; + + u32 nTexUnitLookup; // L + u32 ofsTexUnitLookup; + u32 nTransparencyLookup; // M + u32 ofsTransparencyLookup; + u32 nTexAnimLookup; + u32 ofsTexAnimLookup; + + f32 floats[14]; + + u32 nBoundingTriangles; + u32 ofsBoundingTriangles; + u32 nBoundingVertices; + u32 ofsBoundingVertices; + u32 nBoundingNormals; + u32 ofsBoundingNormals; + + u32 nAttachments; // O + u32 ofsAttachments; + u32 nAttachLookup; // P + u32 ofsAttachLookup; + u32 nQ; // Q + u32 ofsQ; + u32 nLights; // R + u32 ofsLights; + u32 nCameras; // S + u32 ofsCameras; + u32 nT; + u32 ofsT; + u32 nRibbonEmitters; // U + u32 ofsRibbonEmitters; + u32 nParticleEmitters; // V + u32 ofsParticleEmitters; + + } header; + + + struct ModelVertex { + core::vector3df pos;//Use Irrlicht Vector here! + u8 weights[4]; + u8 bones[4]; + core::vector3df normal;//Use Irrlicht Vector here! + core::vector2df texcoords;//Use Irrlicht Vector here! + u32 unk1, unk2; // always 0,0 so this is probably unused + }; + + struct ModelView { + u32 nIndex, ofsIndex; // Vertices in this model (index into vertices[]) + u32 nTris, ofsTris; // indices + u32 nProps, ofsProps; // additional vtx properties + u32 nSub, ofsSub; // materials/renderops/submeshes + u32 nTex, ofsTex; // material properties/textures + s32 lod; // LOD bias? + }; + + // io::IFileSystem* FileSystem; + IrrlichtDevice* Device; + // scene::IMeshManipulator* Manipulator; + core::stringc M2MeshName; + SAnimatedMesh* aniMesh; + SMesh* Mesh; + //Taken from the Model file, thus m2M* + core::array M2MVertices; + core::array M2MViews; + core::array M2MIndices; + core::array M2MTriangles; + //Used for the Mesh, thus m2_noM_* + core::array M2Vertices; + core::array M2Indices; + + }; + }//namespace scene +}//namespace irr diff --git a/src/Client/GUI/DrawObject.cpp b/src/Client/GUI/DrawObject.cpp index d6dcc47..c140e2e 100644 --- a/src/Client/GUI/DrawObject.cpp +++ b/src/Client/GUI/DrawObject.cpp @@ -4,13 +4,17 @@ #include "PseuWoW.h" #include "Object.h" -DrawObject::DrawObject(irr::IrrlichtDevice *device, Object *obj) +using namespace irr; + +DrawObject::DrawObject(irr::IrrlichtDevice *device, Object *obj, PseuInstance *ins) { _initialized = false; Unlink(); + _device = device; _smgr = device->getSceneManager(); _guienv = device->getGUIEnvironment(); _obj = obj; + _instance = ins; DEBUG( logdebug("create DrawObject() this=%X obj=%X name='%s' smgr=%X",this,_obj,_obj->GetName().c_str(),_smgr) ); } @@ -34,10 +38,26 @@ void DrawObject::_Init(void) { if(!cube && _obj->IsWorldObject()) // only world objects have coords and can be drawn { - cube = _smgr->addCubeSceneNode(2); - cube->setName("CUBE"); + uint32 displayid = _obj->IsUnit() ? _obj->GetUInt32Value(UNIT_FIELD_DISPLAYID) : 0; // TODO: in case its GO get it from proto data + uint32 modelid = _instance->dbmgr.GetDB("creaturedisplayinfo").GetField(displayid).GetInteger("model"); + std::string modelfile = std::string("data/model/") + _instance->dbmgr.GetDB("creaturemodeldata").GetField(modelid).GetString("file"); + //scene::IAnimatedMesh *mesh = _smgr->getMesh(modelfile.c_str()); + // ok we use a model that works for sure until crashs with some M2 files, especially character models, are fixed + /*scene::IAnimatedMesh *mesh = _smgr->getMesh("data/model/gyrocopter.m2"); + if(mesh) + { + cube = _smgr->addAnimatedMeshSceneNode(mesh); + //video::ITexture *tex = _device->getVideoDriver()->getTexture("data/misc/square.jpg"); + //cube->setMaterialTexture(0, tex); + //tex->drop(); + } + else + {*/ + cube = _smgr->addCubeSceneNode(2); + //} + cube->setName("OBJECT"); //cube->setPosition(irr::core::vector3di(100,100,100)); - cube->setRotation(irr::core::vector3df(0,0,0)); + cube->setRotation(core::vector3df(0,0,0)); if(_obj->IsPlayer()) { diff --git a/src/Client/GUI/DrawObject.h b/src/Client/GUI/DrawObject.h index 4960976..827a10d 100644 --- a/src/Client/GUI/DrawObject.h +++ b/src/Client/GUI/DrawObject.h @@ -5,11 +5,12 @@ #include "irrlicht/irrlicht.h" class Object; +class PseuInstance; class DrawObject { public: - DrawObject(irr::IrrlichtDevice *device, Object*); + DrawObject(irr::IrrlichtDevice *device, Object*, PseuInstance *ins); ~DrawObject(); void Draw(void); // call only in threadsafe environment!! (ensure the obj ptr is still valid!) void Unlink(void); @@ -19,10 +20,12 @@ private: void _Init(void); Object *_obj; bool _initialized : 1; + irr::IrrlichtDevice *_device; irr::scene::ISceneManager *_smgr; irr::gui::IGUIEnvironment* _guienv; irr::scene::ISceneNode* cube; irr::scene::ITextSceneNode *text; + PseuInstance *_instance; }; diff --git a/src/Client/GUI/PseuGUI.cpp b/src/Client/GUI/PseuGUI.cpp index e54e910..d3bda15 100644 --- a/src/Client/GUI/PseuGUI.cpp +++ b/src/Client/GUI/PseuGUI.cpp @@ -1,5 +1,6 @@ #include "common.h" #include "irrlicht/irrlicht.h" +#include "CM2MeshFileLoader.h" #include "Object.h" #include "DrawObject.h" #include "PseuWoW.h" @@ -119,6 +120,11 @@ void PseuGUI::_Init(void) _guienv = _device->getGUIEnvironment(); _timer = _device->getTimer(); //... + + // register external loaders for not supported filetypes + scene::CM2MeshFileLoader* m2loader = new scene::CM2MeshFileLoader(_device); + _smgr->addExternalMeshLoader(m2loader); + _initialized = true; } @@ -229,7 +235,7 @@ void PseuGUI::NotifyObjectDeletion(uint64 guid) // called from ObjMgr::Add(Object*) void PseuGUI::NotifyObjectCreation(Object *o) { - DrawObject *d = new DrawObject(_device,o); + DrawObject *d = new DrawObject(_device,o,_instance); domgr.Add(o->GetGUID(),d); } diff --git a/src/PseuWoW.vcproj b/src/PseuWoW.vcproj index f1fa1e1..b41526c 100644 --- a/src/PseuWoW.vcproj +++ b/src/PseuWoW.vcproj @@ -417,6 +417,12 @@ + + + + diff --git a/src/PseuWoW_VC80.vcproj b/src/PseuWoW_VC80.vcproj index 2f1d6a3..871b814 100644 --- a/src/PseuWoW_VC80.vcproj +++ b/src/PseuWoW_VC80.vcproj @@ -557,6 +557,14 @@ + + + + diff --git a/src/tools/stuffextract/DBCFieldData.h b/src/tools/stuffextract/DBCFieldData.h index ab74597..cc1356e 100644 --- a/src/tools/stuffextract/DBCFieldData.h +++ b/src/tools/stuffextract/DBCFieldData.h @@ -304,4 +304,75 @@ static const char *ItemDisplayInfoFormat = { "ssi" }; +enum CreatureModelDataEnum +{ + CREATUREMODELDATA_ID = 0, + CREATUREMODELDATA_FILE = 2, + // other fields unknown + CREATUREMODELDATA_END = 24 +}; + +static const char *CreatureModelDataFieldNames[] = { + "","","file","","","","","","", // 0-9 + "","","","","","","","","", // 10-19 + "","","","","", + "" +}; + +static const char *CreatureModelDataFormat = { + "ixsxxxxxx" // 0-9 + "xxxxxxxxx" // 10-19 + "xxxxx" +}; + +enum CreatureDisplayInfoEnum +{ + CREATUREDISPLAYINFO_ID = 0, // <<-- this is the so-called DisplayID!! + CREATUREDISPLAYINFO_MODEL = 1, // id from CreatureModelData + CREATUREDISPLAYINFO_UNK = 2, + CREATUREDISPLAYINFO_EXTRA = 3, // id from CreatureDisplayInfoExtra + CREATUREDISPLAYINFO_SCALE = 4, // default scale, float + CREATUREDISPLAYINFO_OPACITY = 5, // 0 = 100% tansparent, 255 = 100% solid + CREATUREDISPLAYINFO_NAME1 = 6, // next 3 fields are some names, not always present + CREATUREDISPLAYINFO_NAME2 = 7, + CREATUREDISPLAYINFO_NAME3 = 8, + CREATUREDISPLAYINFO_NPCSOUNDS = 11, // id from NPCSounds + CREATUREDISPLAYINFO_END = 21 +}; + +static const char *CreatureDisplayInfoFieldNames[] = { + "","model","unk","extra","scale","opacity","name1","name2","name3","", // 0-9 + "","sound","","","","","","","","", // 10-19 + "", + "" +}; + +static const char *CreatureDisplayInfoFormat = { + "iiiifisssx" // 0-9 + "xixxxxxxxx" // 10-19 + "x" +}; + +enum NPCSoundsEnum +{ + NPCSOUNDS_ID = 0, + NPCSOUNDS_GREETINGS = 1, + NPCSOUNDS_FAREWELLS = 2, + NPCSOUNDS_PISSED = 3, + NPCSOUNDS_UNK = 4, // always 0 + NPCSOUNDS_END = 5 +}; + +static const char *NPCSoundsFieldNames[] = { + "","greet","bye","pissed","", + "" +}; + +static const char *NPCSoundsFormat = { + "iiiix" +}; + + + + #endif diff --git a/src/tools/stuffextract/StuffExtract.cpp b/src/tools/stuffextract/StuffExtract.cpp index c4c4451..7916986 100644 --- a/src/tools/stuffextract/StuffExtract.cpp +++ b/src/tools/stuffextract/StuffExtract.cpp @@ -119,7 +119,6 @@ void ProcessCmdArgs(int argc, char *argv[]) { doTextures = false; doWmos = false; - doModels = false; } if(help) { @@ -149,7 +148,7 @@ void PrintHelp(void) printf("maps - map extraction\n"); printf("textures - extract textures (requires maps extraction, for now)\n"); printf("wmos - extract map WMOs (requires maps extraction)\n"); - printf("models - extract models (required maps extraction, for now)\n"); + printf("models - extract models\n"); printf("sounds - extract sound files (wav/mp3)\n"); printf("md5 - write MD5 checksum lists of extracted files\n"); printf("autoclose - close program when done\n"); @@ -250,8 +249,10 @@ void OutMD5(char *path, MD5FileMap& fm) bool ConvertDBC(void) { std::map racemap; // needed to extract other dbc files correctly - SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage; // will store the converted data from dbc files - DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo; + SCPStorageMap EmoteDataStorage,RaceDataStorage,SoundDataStorage,MapDataStorage,ZoneDataStorage,ItemDisplayInfoStorage, + CreatureModelStorage,CreatureDisplayInfoStorage,NPCSoundStorage; // will store the converted data from dbc files + DBCFile EmotesText,EmotesTextData,EmotesTextSound,ChrRaces,SoundEntries,Map,AreaTable,ItemDisplayInfo, + CreatureModelData,CreatureDisplayInfo,NPCSounds; printf("Opening DBC archive...\n"); MPQHelper mpq("dbc"); @@ -264,6 +265,9 @@ bool ConvertDBC(void) Map.openmem(mpq.ExtractFile("DBFilesClient\\Map.dbc")); AreaTable.openmem(mpq.ExtractFile("DBFilesClient\\AreaTable.dbc")); ItemDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\ItemDisplayInfo.dbc")); + CreatureModelData.openmem(mpq.ExtractFile("DBFilesClient\\CreatureModelData.dbc")); + CreatureDisplayInfo.openmem(mpq.ExtractFile("DBFilesClient\\CreatureDisplayInfo.dbc")); + NPCSounds.openmem(mpq.ExtractFile("DBFilesClient\\NPCSounds.dbc")); //... printf("DBC files opened.\n"); //... @@ -409,6 +413,57 @@ bool ConvertDBC(void) } } + printf("creaturemodeldata.."); + for(DBCFile::Iterator it = CreatureModelData.begin(); it != CreatureModelData.end(); ++it) + { + uint32 id = it->getUInt(CREATUREMODELDATA_ID); + for(uint32 field=CREATUREMODELDATA_ID; field < CREATUREMODELDATA_END; field++) + { + if(strlen(CreatureModelDataFieldNames[field])) + { + std::string value = AutoGetDataString(it,CreatureModelDataFormat,field); + if(value.size()) // only store if not null + { + modelNames.insert(value); // we need to extract model later, store it + std::string fn = _PathToFileName(value); + if(stricmp(fn.c_str()+fn.length()-4, "mdx")) + fn = fn.substr(0,fn.length()-3) + "m2"; + CreatureModelStorage[id].push_back(std::string(CreatureModelDataFieldNames[field]) + "=" + fn); + } + } + } + } + + printf("creaturedisplayinfo.."); + for(DBCFile::Iterator it = CreatureDisplayInfo.begin(); it != CreatureDisplayInfo.end(); ++it) + { + uint32 id = it->getUInt(CREATUREDISPLAYINFO_ID); + for(uint32 field=CREATUREDISPLAYINFO_ID; field < CREATUREDISPLAYINFO_END; field++) + { + if(strlen(CreatureDisplayInfoFieldNames[field])) + { + std::string value = AutoGetDataString(it,CreatureDisplayInfoFormat,field); + if(value.size()) // only store if not null + CreatureDisplayInfoStorage[id].push_back(std::string(CreatureDisplayInfoFieldNames[field]) + "=" + value); + } + } + } + + printf("npcsounds.."); + for(DBCFile::Iterator it = NPCSounds.begin(); it != NPCSounds.end(); ++it) + { + uint32 id = it->getUInt(NPCSOUNDS_ID); + for(uint32 field=NPCSOUNDS_ID; field < NPCSOUNDS_END; field++) + { + if(strlen(NPCSoundsFieldNames[field])) + { + std::string value = AutoGetDataString(it,NPCSoundsFormat,field); + if(value.size()) // only store if not null + NPCSoundStorage[id].push_back(std::string(NPCSoundsFieldNames[field]) + "=" + value); + } + } + } + //... @@ -423,7 +478,10 @@ bool ConvertDBC(void) printf("sound.."); OutSCP(SCPDIR "/sound.scp",SoundDataStorage, "sound"); printf("map.."); OutSCP(SCPDIR "/map.scp",MapDataStorage, "map"); printf("area.."); OutSCP(SCPDIR "/zone.scp",ZoneDataStorage, "zone"); - printf("itemdisplayinfo."); OutSCP(SCPDIR "/itemdisplayinfo.scp",ItemDisplayInfoStorage, "itemdisplayinfo"); + printf("itemdisplayinfo.."); OutSCP(SCPDIR "/itemdisplayinfo.scp",ItemDisplayInfoStorage, "itemdisplayinfo"); + printf("creaturemodeldata.."); OutSCP(SCPDIR "/creaturemodeldata.scp",CreatureModelStorage,"creaturemodeldata"); + printf("creaturedisplayinfo.."); OutSCP(SCPDIR "/creaturedisplayinfo.scp",CreatureDisplayInfoStorage,"creaturedisplayinfo"); + printf("npcsound.."); OutSCP(SCPDIR "/npcsound.scp",NPCSoundStorage,"npcsound"); //... printf("DONE!\n");