2022-02-27 07:53:44 -05:00
|
|
|
#include <STDInclude.hpp>
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-03-18 10:19:31 -04:00
|
|
|
#define IW4X_MODEL_VERSION 5
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
namespace Assets
|
|
|
|
{
|
2017-01-03 16:04:10 -05:00
|
|
|
void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader)
|
|
|
|
{
|
|
|
|
if (entry->nodes)
|
|
|
|
{
|
|
|
|
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->leafs)
|
|
|
|
{
|
|
|
|
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2019-02-01 16:16:42 -05:00
|
|
|
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-01-03 16:04:10 -05:00
|
|
|
if (surf->vertInfo.vertsBlend)
|
|
|
|
{
|
|
|
|
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Access vertex block
|
|
|
|
if (surf->verts0)
|
|
|
|
{
|
|
|
|
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save_XRigidVertListArray
|
|
|
|
if (surf->vertList)
|
|
|
|
{
|
|
|
|
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
|
|
|
{
|
|
|
|
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
|
|
|
|
|
|
|
|
if (rigidVertList->collisionTree)
|
|
|
|
{
|
|
|
|
rigidVertList->collisionTree = reader->readObject<Game::XSurfaceCollisionTree>();
|
|
|
|
this->loadXSurfaceCollisionTree(rigidVertList->collisionTree, reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Access index block
|
2019-02-01 16:16:42 -05:00
|
|
|
if (surf->triIndices)
|
|
|
|
{
|
|
|
|
void* oldPtr = surf->triIndices;
|
|
|
|
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
|
|
|
|
|
|
|
|
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
|
|
|
{
|
|
|
|
surf->triIndices = builder->getAllocator()->getPointer<unsigned short>(oldPtr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
builder->getAllocator()->mapPointer(oldPtr, surf->triIndices);
|
|
|
|
}
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2019-02-01 16:16:42 -05:00
|
|
|
void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-01-03 16:04:10 -05:00
|
|
|
if (asset->name)
|
|
|
|
{
|
|
|
|
asset->name = reader->readCString();
|
|
|
|
}
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
if (asset->surfs)
|
2017-01-03 16:04:10 -05:00
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
asset->surfs = reader->readArray<Game::XSurface>(asset->numsurfs);
|
2017-01-03 16:04:10 -05:00
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
for (int i = 0; i < asset->numsurfs; ++i)
|
2017-01-03 16:04:10 -05:00
|
|
|
{
|
2019-02-01 16:16:42 -05:00
|
|
|
this->loadXSurface(&asset->surfs[i], reader, builder);
|
2017-01-03 16:04:10 -05:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2021-09-05 18:20:16 -04:00
|
|
|
Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data()));
|
|
|
|
|
2021-09-05 19:07:53 -04:00
|
|
|
if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::PreferDiskAssetsDvar.get<bool>() || !modelFile.exists()))
|
2017-06-05 15:03:04 -04:00
|
|
|
{
|
|
|
|
header->model = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).model;
|
|
|
|
if (header->model) return;
|
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (modelFile.exists())
|
|
|
|
{
|
|
|
|
Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer());
|
|
|
|
|
2017-01-20 16:41:03 -05:00
|
|
|
__int64 magic = reader.read<__int64>();
|
|
|
|
if (std::memcmp(&magic, "IW4xModl", 8))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
Components::Logger::Error(0, "Reading model '%s' failed, header is invalid!", name.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
int version = reader.read<int>();
|
2017-04-24 15:14:32 -04:00
|
|
|
if (version != IW4X_MODEL_VERSION)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
Components::Logger::Error(0, "Reading model '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_MODEL_VERSION, version);
|
|
|
|
}
|
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
if (version == 4)
|
2017-03-18 10:19:31 -04:00
|
|
|
{
|
|
|
|
Components::Logger::Print("WARNING: Model '%s' is in legacy format, please update it!\n", name.data());
|
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
Game::XModel* asset = reader.readObject<Game::XModel>();
|
|
|
|
|
|
|
|
if (asset->name)
|
|
|
|
{
|
|
|
|
asset->name = reader.readCString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->boneNames)
|
|
|
|
{
|
|
|
|
asset->boneNames = builder->getAllocator()->allocateArray<unsigned short>(asset->numBones);
|
|
|
|
|
|
|
|
for (char i = 0; i < asset->numBones; ++i)
|
|
|
|
{
|
|
|
|
asset->boneNames[i] = Game::SL_GetString(reader.readCString(), 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->parentList)
|
|
|
|
{
|
|
|
|
asset->parentList = reader.readArray<char>(asset->numBones - asset->numRootBones);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->quats)
|
|
|
|
{
|
|
|
|
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->trans)
|
|
|
|
{
|
|
|
|
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->partClassification)
|
|
|
|
{
|
|
|
|
asset->partClassification = reader.readArray<char>(asset->numBones);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->baseMat)
|
|
|
|
{
|
|
|
|
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->materialHandles)
|
|
|
|
{
|
|
|
|
asset->materialHandles = reader.readArray<Game::Material*>(asset->numsurfs);
|
|
|
|
|
2017-03-25 08:42:21 -04:00
|
|
|
for (unsigned char i = 0; i < asset->numsurfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (asset->materialHandles[i])
|
|
|
|
{
|
|
|
|
asset->materialHandles[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save_XModelLodInfoArray
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
if (asset->lodInfo[i].modelSurfs)
|
|
|
|
{
|
|
|
|
asset->lodInfo[i].modelSurfs = reader.readObject<Game::XModelSurfs>();
|
2019-02-01 16:16:42 -05:00
|
|
|
this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader, builder);
|
2018-05-09 06:04:20 -04:00
|
|
|
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { asset->lodInfo[i].modelSurfs });
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfs;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Zero that for now, it breaks the models.
|
|
|
|
// TODO: Figure out how that can be converted
|
|
|
|
asset->lodInfo[i].smcBaseIndexPlusOne = 0;
|
|
|
|
asset->lodInfo[i].smcSubIndexMask = 0;
|
|
|
|
asset->lodInfo[i].smcBucket = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save_XModelCollSurfArray
|
|
|
|
if (asset->collSurfs)
|
|
|
|
{
|
|
|
|
asset->collSurfs = reader.readArray<Game::XModelCollSurf_s>(asset->numCollSurfs);
|
|
|
|
|
|
|
|
for (int i = 0; i < asset->numCollSurfs; ++i)
|
|
|
|
{
|
|
|
|
Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i];
|
|
|
|
|
|
|
|
if (collSurf->collTris)
|
|
|
|
{
|
|
|
|
collSurf->collTris = reader.readArray<Game::XModelCollTri_s>(collSurf->numCollTris);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->boneInfo)
|
|
|
|
{
|
|
|
|
asset->boneInfo = reader.readArray<Game::XBoneInfo>(asset->numBones);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physPreset)
|
|
|
|
{
|
|
|
|
asset->physPreset = reader.readObject<Game::PhysPreset>();
|
|
|
|
|
2017-01-06 08:36:03 -05:00
|
|
|
if (asset->physPreset->name)
|
|
|
|
{
|
|
|
|
asset->physPreset->name = reader.readCString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physPreset->sndAliasPrefix)
|
|
|
|
{
|
|
|
|
asset->physPreset->sndAliasPrefix = reader.readCString();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2017-01-18 09:18:25 -05:00
|
|
|
// This is an experiment, ak74 fails though
|
|
|
|
if (asset->name == "weapon_ak74u"s)
|
|
|
|
{
|
|
|
|
asset->physPreset = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-03-18 10:19:31 -04:00
|
|
|
Game::PhysPreset* preset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset->name, builder).physPreset;
|
2017-06-14 06:06:04 -04:00
|
|
|
if (preset)
|
2017-03-18 10:19:31 -04:00
|
|
|
{
|
|
|
|
asset->physPreset = preset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset });
|
|
|
|
}
|
2017-01-18 09:18:25 -05:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physCollmap)
|
|
|
|
{
|
2017-03-18 10:19:31 -04:00
|
|
|
if (version == 4)
|
|
|
|
{
|
|
|
|
asset->physCollmap = nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
|
|
|
asset->physCollmap = collmap;
|
|
|
|
|
|
|
|
if (collmap->name)
|
|
|
|
{
|
|
|
|
collmap->name = reader.readCString();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (collmap->geoms)
|
|
|
|
{
|
|
|
|
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < collmap->count; ++i)
|
|
|
|
{
|
|
|
|
Game::PhysGeomInfo* geom = &collmap->geoms[i];
|
|
|
|
|
2018-05-09 08:33:52 -04:00
|
|
|
if (geom->brushWrapper)
|
2017-03-18 10:19:31 -04:00
|
|
|
{
|
|
|
|
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
|
2018-05-09 08:33:52 -04:00
|
|
|
geom->brushWrapper = brush;
|
2017-03-18 10:19:31 -04:00
|
|
|
{
|
|
|
|
if (brush->brush.sides)
|
|
|
|
{
|
|
|
|
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides);
|
|
|
|
for (unsigned short j = 0; j < brush->brush.numsides; ++j)
|
|
|
|
{
|
|
|
|
Game::cbrushside_t* side = &brush->brush.sides[j];
|
|
|
|
|
|
|
|
// TODO: Add pointer support
|
|
|
|
if (side->plane)
|
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
side->plane = reader.readObject<Game::cplane_s>();
|
2017-03-18 10:19:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (brush->brush.baseAdjacentSide)
|
|
|
|
{
|
|
|
|
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Add pointer support
|
|
|
|
if (brush->planes)
|
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
|
2017-03-18 10:19:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-09 06:04:20 -04:00
|
|
|
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap });
|
2019-02-01 16:16:42 -05:00
|
|
|
// asset->physCollmap = nullptr;
|
2017-03-18 10:19:31 -04:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!reader.end())
|
|
|
|
{
|
|
|
|
Components::Logger::Error(0, "Reading model '%s' failed, remaining raw data found!", name.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
header->model = asset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void IXModel::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
|
|
|
{
|
|
|
|
Game::XModel* asset = header.model;
|
|
|
|
|
|
|
|
if (asset->boneNames)
|
|
|
|
{
|
|
|
|
for (char i = 0; i < asset->numBones; ++i)
|
|
|
|
{
|
|
|
|
builder->addScriptString(asset->boneNames[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->materialHandles)
|
|
|
|
{
|
2017-03-25 08:42:21 -04:00
|
|
|
for (unsigned char i = 0; i < asset->numsurfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (asset->materialHandles[i])
|
|
|
|
{
|
|
|
|
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
if (asset->lodInfo[i].modelSurfs)
|
|
|
|
{
|
2018-05-09 06:04:20 -04:00
|
|
|
builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, asset->lodInfo[i].modelSurfs);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physPreset)
|
|
|
|
{
|
|
|
|
builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physCollmap)
|
|
|
|
{
|
2018-05-09 06:04:20 -04:00
|
|
|
builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, asset->physCollmap);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void IXModel::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
|
|
|
{
|
|
|
|
AssertSize(Game::XModel, 304);
|
|
|
|
|
|
|
|
Utils::Stream* buffer = builder->getBuffer();
|
|
|
|
Game::XModel* asset = header.model;
|
|
|
|
Game::XModel* dest = buffer->dest<Game::XModel>();
|
|
|
|
buffer->save(asset);
|
|
|
|
|
|
|
|
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
|
|
|
|
|
|
|
if (asset->name)
|
|
|
|
{
|
|
|
|
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
|
|
|
|
Utils::Stream::ClearPointer(&dest->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->boneNames)
|
|
|
|
{
|
|
|
|
buffer->align(Utils::Stream::ALIGN_2);
|
|
|
|
|
|
|
|
unsigned short* destBoneNames = buffer->dest<unsigned short>();
|
|
|
|
buffer->saveArray(asset->boneNames, asset->numBones);
|
|
|
|
|
|
|
|
for (char i = 0; i < asset->numBones; ++i)
|
|
|
|
{
|
|
|
|
builder->mapScriptString(&destBoneNames[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils::Stream::ClearPointer(&dest->boneNames);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->parentList)
|
|
|
|
{
|
|
|
|
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
|
|
|
|
Utils::Stream::ClearPointer(&dest->parentList);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->quats)
|
|
|
|
{
|
|
|
|
buffer->align(Utils::Stream::ALIGN_2);
|
|
|
|
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
|
|
|
|
Utils::Stream::ClearPointer(&dest->quats);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->trans)
|
|
|
|
{
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
|
|
|
|
Utils::Stream::ClearPointer(&dest->trans);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->partClassification)
|
|
|
|
{
|
|
|
|
buffer->save(asset->partClassification, asset->numBones);
|
|
|
|
Utils::Stream::ClearPointer(&dest->partClassification);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->baseMat)
|
|
|
|
{
|
|
|
|
AssertSize(Game::DObjAnimMat, 32);
|
|
|
|
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
buffer->saveArray(asset->baseMat, asset->numBones);
|
|
|
|
Utils::Stream::ClearPointer(&dest->baseMat);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->materialHandles)
|
|
|
|
{
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
|
|
|
|
Game::Material** destMaterials = buffer->dest<Game::Material*>();
|
|
|
|
buffer->saveArray(asset->materialHandles, asset->numsurfs);
|
|
|
|
|
2017-03-25 08:42:21 -04:00
|
|
|
for (unsigned char i = 0; i < asset->numsurfs; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (asset->materialHandles[i])
|
|
|
|
{
|
|
|
|
destMaterials[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]).material;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils::Stream::ClearPointer(&dest->materialHandles);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save_XModelLodInfoArray
|
|
|
|
{
|
|
|
|
AssertSize(Game::XModelLodInfo, 44);
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
{
|
|
|
|
if (asset->lodInfo[i].modelSurfs)
|
|
|
|
{
|
2018-05-09 08:33:52 -04:00
|
|
|
dest->lodInfo[i].modelSurfs = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, asset->lodInfo[i].modelSurfs).modelSurfs;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save_XModelCollSurfArray
|
|
|
|
if (asset->collSurfs)
|
|
|
|
{
|
|
|
|
AssertSize(Game::XModelCollSurf_s, 44);
|
|
|
|
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
|
|
|
|
Game::XModelCollSurf_s* destColSurfs = buffer->dest<Game::XModelCollSurf_s>();
|
|
|
|
buffer->saveArray(asset->collSurfs, asset->numCollSurfs);
|
|
|
|
|
|
|
|
for (int i = 0; i < asset->numCollSurfs; ++i)
|
|
|
|
{
|
|
|
|
Game::XModelCollSurf_s* destCollSurf = &destColSurfs[i];
|
|
|
|
Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i];
|
|
|
|
|
|
|
|
if (collSurf->collTris)
|
|
|
|
{
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
|
|
|
|
buffer->save(collSurf->collTris, 48, collSurf->numCollTris);
|
|
|
|
Utils::Stream::ClearPointer(&destCollSurf->collTris);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Utils::Stream::ClearPointer(&dest->collSurfs);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->boneInfo)
|
|
|
|
{
|
|
|
|
AssertSize(Game::XBoneInfo, 28);
|
|
|
|
|
|
|
|
buffer->align(Utils::Stream::ALIGN_4);
|
|
|
|
|
|
|
|
buffer->saveArray(asset->boneInfo, asset->numBones);
|
|
|
|
Utils::Stream::ClearPointer(&dest->boneInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physPreset)
|
|
|
|
{
|
|
|
|
dest->physPreset = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset).physPreset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (asset->physCollmap)
|
|
|
|
{
|
2018-05-09 06:04:20 -04:00
|
|
|
dest->physCollmap = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, asset->physCollmap).physCollmap;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
buffer->popBlock();
|
|
|
|
}
|
|
|
|
}
|