iw4x-client/src/Components/Modules/AssetInterfaces/IXModel.cpp

386 lines
12 KiB
C++
Raw Normal View History

2016-07-11 11:14:58 -04:00
#include <STDInclude.hpp>
2016-12-20 12:15:28 -05:00
#define IW4X_MODEL_VERSION 2
2016-07-11 11:14:58 -04:00
namespace Assets
{
void IXModel::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder)
2016-07-11 11:14:58 -04:00
{
Components::FileSystem::File modelFile(fmt::sprintf("xmodel/%s.iw4xModel", name.data()));
if (modelFile.exists())
2016-07-11 11:14:58 -04:00
{
Game::XModel* baseModel = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_XMODEL, "viewmodel_mp5k").model;
// Allocate new model and copy the base data to it
Game::XModel* model = builder->getAllocator()->allocate<Game::XModel>();
2016-07-11 11:14:58 -04:00
std::memcpy(model, baseModel, sizeof(Game::XModel));
Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer());
2016-07-11 11:14:58 -04:00
if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xModl"))
{
Components::Logger::Error(0, "Reading model '%s' failed, header is invalid!", name.data());
}
int version = reader.read<int>();
if (version != IW4X_MODEL_VERSION)
{
Components::Logger::Error(0, "Reading model '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_MODEL_VERSION, version);
}
model->name = reader.readCString();
model->numBones = reader.readByte();
model->numRootBones = reader.readByte();
model->numSurfaces = reader.read<unsigned char>();
model->numColSurfs = reader.read<int>();
2016-07-11 11:14:58 -04:00
// Read bone names
model->boneNames = builder->getAllocator()->allocateArray<short>(model->numBones);
2016-07-11 11:14:58 -04:00
for (int i = 0; i < model->numBones; ++i)
{
model->boneNames[i] = Game::SL_GetString(reader.readCString(), 0);
2016-07-11 11:14:58 -04:00
}
// Bone count
int boneCount = (model->numBones - model->numRootBones);
// Read bone data
model->parentList = reader.readArray<char>(boneCount);
model->tagAngles = reader.readArray<Game::XModelAngle>(boneCount);
model->tagPositions = reader.readArray<Game::XModelTagPos>(boneCount);
model->partClassification = reader.readArray<char>(boneCount);
model->animMatrix = reader.readArray<Game::DObjAnimMat>(boneCount);
2016-07-11 11:14:58 -04:00
// Prepare surfaces
Game::XSurface* baseSurface = &baseModel->lods[0].surfaces[0].surfaces[0];
Game::XModelSurfs* surf = builder->getAllocator()->allocate<Game::XModelSurfs>();
2016-07-11 11:14:58 -04:00
std::memcpy(surf, baseModel->lods[0].surfaces, sizeof(Game::XModelSurfs));
surf->name = builder->getAllocator()->duplicateString(fmt::sprintf("%s_lod1", model->name));
surf->surfaces = builder->getAllocator()->allocateArray<Game::XSurface>(model->numSurfaces);
2016-07-11 11:14:58 -04:00
surf->numSurfaces = model->numSurfaces;
// Reset surfaces in remaining lods
for (unsigned int i = 1; i < 4; ++i)
{
2016-11-05 21:24:30 -04:00
ZeroMemory(&model->lods[i], sizeof(Game::XModelLodInfo));
2016-07-11 11:14:58 -04:00
}
model->lods[0].dist = reader.read<float>();
model->lods[0].numSurfs = reader.read<short>();
model->lods[0].maxSurfs = reader.read<short>();
2016-11-06 08:03:51 -05:00
model->lods[0].partBits[0] = reader.read<int>();
model->lods[0].partBits[1] = reader.read<int>();
model->lods[0].partBits[2] = reader.read<int>();
model->lods[0].partBits[3] = reader.read<int>();
2016-11-06 08:03:51 -05:00
model->lods[0].numSurfs = model->numSurfaces; // This is needed in case we have more than 1 LOD
model->lods[0].surfaces = surf;
model->lods[0].surfs = surf->surfaces;
model->numLods = 1;
2016-07-11 11:14:58 -04:00
// Read surfaces
for (int i = 0; i < surf->numSurfaces; ++i)
{
Game::XSurface* surface = &surf->surfaces[i];
std::memcpy(surface, baseSurface, sizeof(Game::XSurface));
surface->tileMode = reader.read<char>();
surface->deformed = reader.read<char>();
2016-11-05 21:24:30 -04:00
surface->streamHandle = reader.read<unsigned char>();
2016-12-20 12:15:28 -05:00
surface->partBits[0] = reader.read<int>();
surface->partBits[1] = reader.read<int>();
surface->partBits[2] = reader.read<int>();
surface->partBits[3] = reader.read<int>();
2016-07-11 11:14:58 -04:00
2016-12-20 12:15:28 -05:00
surface->baseTriIndex = reader.read<unsigned __int16>();
surface->baseVertIndex = reader.read<unsigned __int16>();
2016-11-05 21:24:30 -04:00
surface->numVertices = reader.read<unsigned short>();
surface->numPrimitives = reader.read<unsigned short>();
surface->numCT = reader.read<int>();
2016-07-11 11:14:58 -04:00
surface->blendNum1 = reader.read<short>();
surface->blendNum2 = reader.read<short>();
surface->blendNum3 = reader.read<short>();
surface->blendNum4 = reader.read<short>();
2016-07-11 11:14:58 -04:00
surface->blendInfo = reinterpret_cast<char*>(reader.read(2, surface->blendNum1 + (3 * surface->blendNum2) + (5 * surface->blendNum3) + (7 * surface->blendNum4)));
2016-07-11 11:14:58 -04:00
surface->vertexBuffer = reader.readArray<Game::GfxPackedVertex>(surface->numVertices);
surface->indexBuffer = reader.readArray<Game::Face>(surface->numPrimitives);
2016-07-11 11:14:58 -04:00
// Read vert list
if (reader.readByte())
2016-07-11 11:14:58 -04:00
{
surface->ct = reader.readArray<Game::XRigidVertList>(surface->numCT);
2016-07-11 11:14:58 -04:00
for (int j = 0; j < surface->numCT; ++j)
{
Game::XRigidVertList* vertList = &surface->ct[j];
vertList->entry = reader.readArray<Game::XSurfaceCollisionTree>();
vertList->entry->node = reinterpret_cast<char*>(reader.read(16, vertList->entry->numNode));
vertList->entry->leaf = reader.readArray<short>(vertList->entry->numLeaf);
2016-07-11 11:14:58 -04:00
}
}
else
{
surface->ct = nullptr;
}
}
// Read materials
model->materials = builder->getAllocator()->allocateArray<Game::Material*>(model->numSurfaces);
for (unsigned char i = 0; i < model->numSurfaces; ++i)
2016-07-11 11:14:58 -04:00
{
model->materials[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
2016-07-11 11:14:58 -04:00
}
// Read collision surfaces
if (reader.readByte())
2016-07-11 11:14:58 -04:00
{
model->colSurf = reader.readArray<Game::XModelCollSurf>(model->numColSurfs);
2016-07-11 11:14:58 -04:00
for (int i = 0; i < model->numColSurfs; ++i)
{
if (model->colSurf[i].tris)
{
model->colSurf[i].tris = reader.read(48, model->colSurf[i].count);
2016-07-11 11:14:58 -04:00
}
}
}
else
{
model->colSurf = nullptr;
}
// Read bone info
if (reader.readByte())
2016-07-11 11:14:58 -04:00
{
model->boneInfo = reader.readArray<Game::XBoneInfo>(model->numBones);
2016-07-11 11:14:58 -04:00
}
else
{
model->boneInfo = nullptr;
}
if (!reader.end())
2016-07-11 11:14:58 -04:00
{
Components::Logger::Error(0, "Reading model '%s' failed, remaining raw data found!", name.data());
}
header->model = model;
}
}
void IXModel::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
2016-07-11 11:14:58 -04:00
{
Game::XModel* asset = header.model;
if (asset->boneNames)
{
for (char i = 0; i < asset->numBones; ++i)
{
builder->addScriptString(asset->boneNames[i]);
2016-07-11 11:14:58 -04:00
}
}
if (asset->materials)
{
for (unsigned char i = 0; i < asset->numSurfaces; ++i)
2016-07-11 11:14:58 -04:00
{
if (asset->materials[i])
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materials[i]->name);
2016-07-11 11:14:58 -04:00
}
}
}
for (int i = 0; i < 4; ++i)
{
if (asset->lods[i].surfaces)
{
// We're not supposed to include xmodelsurfs as standalone asset
//builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lods[i].surfaces->name);
2016-07-11 11:14:58 -04:00
IXModelSurfs().mark({ asset->lods[i].surfaces }, builder);
2016-07-11 11:14:58 -04:00
}
}
if (asset->physPreset)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset->name);
2016-07-11 11:14:58 -04:00
}
if (asset->physCollmap)
{
builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap->name);
2016-07-11 11:14:58 -04:00
}
}
void IXModel::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
2016-07-11 11:14:58 -04:00
{
AssertSize(Game::XModel, 304);
2016-07-11 11:14:58 -04:00
Utils::Stream* buffer = builder->getBuffer();
2016-07-11 11:14:58 -04:00
Game::XModel* asset = header.model;
Game::XModel* dest = buffer->dest<Game::XModel>();
buffer->save(asset);
2016-07-11 11:14:58 -04:00
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
2016-07-11 11:14:58 -04:00
if (asset->name)
{
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->name);
}
if (asset->boneNames)
{
buffer->align(Utils::Stream::ALIGN_2);
2016-07-11 11:14:58 -04:00
unsigned short* destBoneNames = buffer->dest<unsigned short>();
buffer->saveArray(asset->boneNames, asset->numBones);
2016-07-11 11:14:58 -04:00
for (char i = 0; i < asset->numBones; ++i)
{
builder->mapScriptString(&destBoneNames[i]);
2016-07-11 11:14:58 -04:00
}
Utils::Stream::ClearPointer(&dest->boneNames);
}
if (asset->parentList)
{
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->parentList);
}
if (asset->tagAngles)
{
AssertSize(Game::XModelAngle, 8);
2016-07-11 11:14:58 -04:00
buffer->align(Utils::Stream::ALIGN_2);
buffer->saveArray(asset->tagAngles, asset->numBones - asset->numRootBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->tagAngles);
}
if (asset->tagPositions)
{
AssertSize(Game::XModelTagPos, 12);
2016-07-11 11:14:58 -04:00
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(asset->tagPositions, asset->numBones - asset->numRootBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->tagPositions);
}
if (asset->partClassification)
{
buffer->save(asset->partClassification, asset->numBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->partClassification);
}
if (asset->animMatrix)
{
AssertSize(Game::DObjAnimMat, 32);
2016-07-11 11:14:58 -04:00
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(asset->animMatrix, asset->numBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->animMatrix);
}
if (asset->materials)
{
buffer->align(Utils::Stream::ALIGN_4);
2016-07-11 11:14:58 -04:00
Game::Material** destMaterials = buffer->dest<Game::Material*>();
buffer->saveArray(asset->materials, asset->numSurfaces);
2016-07-11 11:14:58 -04:00
for (unsigned char i = 0; i < asset->numSurfaces; ++i)
2016-07-11 11:14:58 -04:00
{
if (asset->materials[i])
{
destMaterials[i] = builder->requireAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materials[i]->name).material;
2016-07-11 11:14:58 -04:00
}
}
Utils::Stream::ClearPointer(&dest->materials);
}
// Save_XModelLodInfoArray
{
AssertSize(Game::XModelLodInfo, 44);
2016-07-11 11:14:58 -04:00
for (int i = 0; i < 4; ++i)
{
if (asset->lods[i].surfaces)
{
// Requiring this asset is not possible, it has to be loaded as part of the model
//dest->lods[i].surfaces = builder->requireAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lods[i].surfaces->name).surfaces;
2016-07-11 11:14:58 -04:00
buffer->pushBlock(Game::XFILE_BLOCK_TEMP);
buffer->align(Utils::Stream::ALIGN_4);
2016-07-11 11:14:58 -04:00
IXModelSurfs().save({ asset->lods[i].surfaces }, builder);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->lods[i].surfaces);
buffer->popBlock();
2016-07-11 11:14:58 -04:00
}
}
}
// Save_XModelCollSurfArray
if (asset->colSurf)
{
AssertSize(Game::XModelCollSurf, 44);
2016-07-11 11:14:58 -04:00
buffer->align(Utils::Stream::ALIGN_4);
2016-07-11 11:14:58 -04:00
Game::XModelCollSurf* destColSurfs = buffer->dest<Game::XModelCollSurf>();
buffer->saveArray(asset->colSurf, asset->numColSurfs);
2016-07-11 11:14:58 -04:00
for (int i = 0; i < asset->numColSurfs; ++i)
{
Game::XModelCollSurf* destColSurf = &destColSurfs[i];
Game::XModelCollSurf* colSurf = &asset->colSurf[i];
if (colSurf->tris)
{
buffer->align(Utils::Stream::ALIGN_4);
2016-07-11 11:14:58 -04:00
buffer->save(colSurf->tris, 48, colSurf->count);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&destColSurf->tris);
}
}
Utils::Stream::ClearPointer(&dest->colSurf);
}
if (asset->boneInfo)
{
AssertSize(Game::XBoneInfo, 28);
2016-07-11 11:14:58 -04:00
buffer->align(Utils::Stream::ALIGN_4);
2016-07-11 11:14:58 -04:00
buffer->saveArray(asset->boneInfo, asset->numBones);
2016-07-11 11:14:58 -04:00
Utils::Stream::ClearPointer(&dest->boneInfo);
}
if (asset->physPreset)
{
dest->physPreset = builder->requireAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset->name).physPreset;
2016-07-11 11:14:58 -04:00
}
if (asset->physCollmap)
{
dest->physCollmap = builder->requireAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap->name).physCollmap;
2016-07-11 11:14:58 -04:00
}
buffer->popBlock();
2016-07-11 11:14:58 -04:00
}
}