Zonebuilder improvements
This commit is contained in:
parent
f1ec16983a
commit
8fdde66bdc
@ -242,54 +242,14 @@ namespace Components
|
||||
{
|
||||
auto str = Game::SL_FindString("j_spinelower");
|
||||
int interestingPart = 0;
|
||||
for (size_t i = 0; i < anim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; i++)
|
||||
{
|
||||
if (anim->names[i] == str)
|
||||
{
|
||||
interestingPart = i;
|
||||
//anim->names[i] = Game::SL_FindString("torso_stabilizer");
|
||||
}
|
||||
}
|
||||
|
||||
// Something interesting to do there
|
||||
//for (size_t frameIndex = 0; frameIndex < anim->numframes; frameIndex++)
|
||||
//{
|
||||
// const auto delta = &anim->deltaPart[frameIndex];
|
||||
|
||||
// const auto frameCount = anim->numframes;
|
||||
|
||||
// if (delta->trans->size)
|
||||
// {
|
||||
// const auto test = delta->trans->u.frames;
|
||||
|
||||
// if (frameCount > 0xFF)
|
||||
// {
|
||||
// delta->trans->u.frames.indices._1
|
||||
// buffer->saveArray(delta->trans->u.frames.indices._2, delta->trans->size + 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// buffer->saveArray(delta->trans->u.frames.indices._1, delta->trans->size + 1);
|
||||
// }
|
||||
|
||||
// if (delta->trans->u.frames.frames._1)
|
||||
// {
|
||||
// if (delta->trans->smallTrans)
|
||||
// {
|
||||
// buffer->save(delta->trans->u.frames.frames._1, 3, delta->trans->size + 1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// buffer->align(Utils::Stream::ALIGN_4);
|
||||
// buffer->save(delta->trans->u.frames.frames._1, 6, delta->trans->size + 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// buffer->save(delta->trans->u.frame0, 12);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,65 +283,14 @@ namespace Components
|
||||
// Unlikely candidate
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
static std::vector<std::string> names{};
|
||||
names.clear();
|
||||
|
||||
static Utils::Memory::Allocator allocator{};
|
||||
for (auto i = 0; i < model->numBones; i++)
|
||||
{
|
||||
const auto bone = model->boneNames[i];
|
||||
const auto boneName = Game::SL_ConvertToString(bone);
|
||||
const auto str = std::format("{} => q({} {} {} {}) t({} {} {})",
|
||||
boneName,
|
||||
model->baseMat[i - 1].quat[0],
|
||||
model->baseMat[i - 1].quat[1],
|
||||
model->baseMat[i - 1].quat[2],
|
||||
model->baseMat[i - 1].quat[3],
|
||||
model->baseMat[i - 1].trans[0],
|
||||
model->baseMat[i - 1].trans[1],
|
||||
model->baseMat[i - 1].trans[2]
|
||||
);
|
||||
|
||||
const auto duplicated = allocator.duplicateString(str);
|
||||
names.push_back(
|
||||
duplicated
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Assets::IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(model, allocator);
|
||||
printf("");
|
||||
|
||||
|
||||
//
|
||||
names.clear();
|
||||
for (auto i = 1; i < model->numBones; i++)
|
||||
//if (name == "body_urban_civ_female_a"s)
|
||||
{
|
||||
const auto bone = model->boneNames[i];
|
||||
const auto boneName = Game::SL_ConvertToString(bone);
|
||||
const auto m1 = i - 1;
|
||||
const auto fmt = std::format("{} => q({} {} {} {}) t({} {} {})",
|
||||
boneName,
|
||||
model->quats[m1 * 4 + 0],
|
||||
model->quats[m1 * 4 + 1],
|
||||
model->quats[m1 * 4 + 2],
|
||||
model->quats[m1 * 4 + 3],
|
||||
model->trans[m1 * 3 + 0],
|
||||
model->trans[m1 * 3 + 1],
|
||||
model->trans[m1 * 3 + 2]
|
||||
);
|
||||
names.push_back(
|
||||
fmt
|
||||
);
|
||||
|
||||
Assets::IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(model, allocator);
|
||||
printf("");
|
||||
}
|
||||
|
||||
printf("");
|
||||
//
|
||||
}
|
||||
|
||||
if (type == Game::ASSET_TYPE_MATERIAL && (name == "gfx_distortion_knife_trail" || name == "gfx_distortion_heat_far" || name == "gfx_distortion_ring_light" || name == "gfx_distortion_heat") && asset.material->info.sortKey >= 43)
|
||||
|
@ -296,6 +296,7 @@ namespace Assets
|
||||
|
||||
std::string IXModel::GetParentOfBone(Game::XModel* model, uint8_t index)
|
||||
{
|
||||
assert(index > 0);
|
||||
const auto parentIndex = GetParentIndexOfBone(model, index);
|
||||
const auto boneName = Game::SL_ConvertToString(model->boneNames[parentIndex]);
|
||||
return boneName;
|
||||
@ -313,17 +314,17 @@ namespace Assets
|
||||
|
||||
auto vertsBlendOffset = 0;
|
||||
|
||||
int rebuiltPartBits[6]{};
|
||||
int rebuiltPartBits[LENGTH]{};
|
||||
std::unordered_set<uint8_t> affectingBones{};
|
||||
|
||||
const auto registerBoneAffectingSurface = [&](unsigned int offset) {
|
||||
uint8_t index = static_cast<uint8_t>(surface->vertInfo.vertsBlend[offset] / sizeof(Game::DObjSkelMat));
|
||||
highestBoneIndex = std::max(highestBoneIndex, index);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 1 bone weight
|
||||
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[0]; vertIndex++)
|
||||
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[0]; vertIndex++)
|
||||
{
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 0);
|
||||
|
||||
@ -331,7 +332,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
// 2 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[1]; vertIndex++)
|
||||
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[1]; vertIndex++)
|
||||
{
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 0);
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 1);
|
||||
@ -340,7 +341,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
// 3 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[2]; vertIndex++)
|
||||
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[2]; vertIndex++)
|
||||
{
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 0);
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 1);
|
||||
@ -350,7 +351,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
// 4 bone weights
|
||||
for (auto vertIndex = 0; vertIndex < surface->vertInfo.vertCount[3]; vertIndex++)
|
||||
for (unsigned int vertIndex = 0; vertIndex < surface->vertInfo.vertCount[3]; vertIndex++)
|
||||
{
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 0);
|
||||
registerBoneAffectingSurface(vertsBlendOffset + 1);
|
||||
@ -360,7 +361,7 @@ namespace Assets
|
||||
vertsBlendOffset += 7;
|
||||
}
|
||||
|
||||
for (auto vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
|
||||
for (unsigned int vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
|
||||
{
|
||||
highestBoneIndex = std::max(highestBoneIndex, static_cast<uint8_t>(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat)));
|
||||
}
|
||||
@ -395,7 +396,7 @@ namespace Assets
|
||||
assert(index < model->numBones);
|
||||
|
||||
affectingBones.emplace(index);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// 1 bone weight
|
||||
@ -438,7 +439,7 @@ namespace Assets
|
||||
|
||||
for (auto vertListIndex = 0; vertListIndex < surface->vertListCount; vertListIndex++)
|
||||
{
|
||||
affectingBones.emplace(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat));
|
||||
affectingBones.emplace(static_cast<uint8_t>(surface->vertList[vertListIndex].boneOffset / sizeof(Game::DObjSkelMat)));
|
||||
}
|
||||
|
||||
// Actually rebuilding
|
||||
@ -497,7 +498,7 @@ namespace Assets
|
||||
const uint8_t newBoneIndex = atPosition;
|
||||
const uint8_t newBoneIndexMinusRoot = atPosition - model->numRootBones;
|
||||
|
||||
Components::Logger::Print("Inserting bone {} at position {} (between {} and {})\n", boneName, atPosition, Game::SL_ConvertToString(model->boneNames[atPosition-1]), Game::SL_ConvertToString(model->boneNames[atPosition+1]));
|
||||
Components::Logger::Print("Inserting bone {} at position {} (between {} and {})\n", boneName, atPosition, Game::SL_ConvertToString(model->boneNames[atPosition - 1]), Game::SL_ConvertToString(model->boneNames[atPosition + 1]));
|
||||
|
||||
// Reallocate
|
||||
const auto newBoneNames = allocator.allocateArray<uint16_t>(newBoneCount);
|
||||
@ -549,7 +550,7 @@ namespace Assets
|
||||
|
||||
// It's RELATIVE !
|
||||
uint16_t quat[4]{};
|
||||
quat[3] = 32767; // 0 0 0 32767
|
||||
quat[3] = SHRT_MAX; // 0 0 0 1
|
||||
|
||||
float trans[3]{};
|
||||
|
||||
@ -570,7 +571,7 @@ namespace Assets
|
||||
{
|
||||
std::memcpy(&newBoneNames[atPosition + 1], &model->boneNames[atPosition], sizeof(uint16_t) * lengthOfSecondPart);
|
||||
std::memcpy(&newMats[atPosition + 1], &model->baseMat[atPosition], sizeof(Game::DObjAnimMat) * lengthOfSecondPart);
|
||||
std::memcpy(&newPartsClassification[atPosition+1], &model->partClassification[atPosition], lengthOfSecondPart);
|
||||
std::memcpy(&newPartsClassification[atPosition + 1], &model->partClassification[atPosition], lengthOfSecondPart);
|
||||
std::memcpy(&newBoneInfo[atPosition + 1], &model->boneInfo[atPosition], sizeof(Game::XBoneInfo) * lengthOfSecondPart);
|
||||
std::memcpy(&newQuats[(atPositionM1 + 1) * 4], &model->quats[atPositionM1 * 4], sizeof(uint16_t) * 4 * lengthOfSecondPartM1);
|
||||
std::memcpy(&newTrans[(atPositionM1 + 1) * 3], &model->trans[atPositionM1 * 3], sizeof(float) * 3 * lengthOfSecondPartM1);
|
||||
@ -635,7 +636,7 @@ namespace Assets
|
||||
|
||||
surface->vertInfo.vertsBlend[offset] = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Fix bone offsets
|
||||
if (surface->vertList)
|
||||
@ -739,8 +740,9 @@ namespace Assets
|
||||
|
||||
void IXModel::TransferWeights(Game::XModel* model, const uint8_t origin, const uint8_t destination)
|
||||
{
|
||||
// Does not work
|
||||
return;
|
||||
const auto from = Game::SL_ConvertToString(model->boneNames[origin]);
|
||||
const auto to = Game::SL_ConvertToString(model->boneNames[destination]);
|
||||
Components::Logger::Print("Transferring bone weights from {} to {}\n", from, to);
|
||||
|
||||
const auto originalWeights = model->baseMat[origin].transWeight;
|
||||
model->baseMat[origin].transWeight = model->baseMat[destination].transWeight;
|
||||
@ -750,6 +752,12 @@ namespace Assets
|
||||
{
|
||||
const auto lod = &model->lodInfo[i];
|
||||
|
||||
if ((lod->partBits[5] & 0x1) == 0x1)
|
||||
{
|
||||
// surface lod already converted (more efficient)
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int surfIndex = 0; surfIndex < lod->modelSurfs->numsurfs; surfIndex++)
|
||||
{
|
||||
auto vertsBlendOffset = 0u;
|
||||
@ -760,12 +768,13 @@ namespace Assets
|
||||
int index = static_cast<int>(surface->vertInfo.vertsBlend[offset] / sizeof(Game::DObjSkelMat));
|
||||
if (index == origin)
|
||||
{
|
||||
if (index < 0 || index >= model->numBones - 1)
|
||||
index = destination;
|
||||
|
||||
if (index < 0 || index >= model->numBones)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
index = destination;
|
||||
|
||||
surface->vertInfo.vertsBlend[offset] = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
|
||||
}
|
||||
@ -778,13 +787,14 @@ namespace Assets
|
||||
{
|
||||
const auto vertList = &surface->vertList[vertListIndex];
|
||||
auto index = vertList->boneOffset / sizeof(Game::DObjSkelMat);
|
||||
if (index < 0 || index >= model->numBones - 1)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (index == origin)
|
||||
{
|
||||
if (index < 0 || index >= model->numBones)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
index = destination;
|
||||
vertList->boneOffset = static_cast<unsigned short>(index * sizeof(Game::DObjSkelMat));
|
||||
}
|
||||
@ -834,6 +844,46 @@ namespace Assets
|
||||
}
|
||||
};
|
||||
|
||||
void IXModel::SetBoneTrans(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z)
|
||||
{
|
||||
if (baseMat)
|
||||
{
|
||||
model->baseMat[boneIndex].trans[0] = x;
|
||||
model->baseMat[boneIndex].trans[1] = y;
|
||||
model->baseMat[boneIndex].trans[2] = z;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto index = boneIndex - model->numRootBones;
|
||||
assert(index >= 0);
|
||||
|
||||
model->trans[index * 3 + 0] = x;
|
||||
model->trans[index * 3 + 1] = y;
|
||||
model->trans[index * 3 + 2] = z;
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::SetBoneQuaternion(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z, float w)
|
||||
{
|
||||
if (baseMat)
|
||||
{
|
||||
model->baseMat[boneIndex].quat[0] = x;
|
||||
model->baseMat[boneIndex].quat[1] = y;
|
||||
model->baseMat[boneIndex].quat[2] = z;
|
||||
model->baseMat[boneIndex].quat[3] = w;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto index = boneIndex - model->numRootBones;
|
||||
assert(index >= 0);
|
||||
|
||||
model->quats[index * 4 + 0] = static_cast<uint16_t>(x * SHRT_MAX);
|
||||
model->quats[index * 4 + 1] = static_cast<uint16_t>(y * SHRT_MAX);
|
||||
model->quats[index * 4 + 2] = static_cast<uint16_t>(z * SHRT_MAX);
|
||||
model->quats[index * 4 + 3] = static_cast<uint16_t>(w * SHRT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IXModel::ConvertPlayerModelFromSingleplayerToMultiplayer(Game::XModel* model, Utils::Memory::Allocator& allocator)
|
||||
{
|
||||
@ -866,24 +916,14 @@ namespace Assets
|
||||
const auto root = GetIndexOfBone(model, "j_mainroot");
|
||||
if (root < UCHAR_MAX) {
|
||||
|
||||
#if true
|
||||
//// Works
|
||||
//InsertBone(model, "offsetron_the_great_offsetter_of_bones", "j_mainroot", allocator);
|
||||
|
||||
//// Breaks the model
|
||||
//InsertBone(model, "offsetron2_the_greater_offsetter_of_bones", "j_mainroot", allocator);
|
||||
|
||||
//for (auto lodIndex = 0; lodIndex < model->numLods; lodIndex++)
|
||||
//{
|
||||
// convertedSurfs.emplace(model->lodInfo[lodIndex].modelSurfs);
|
||||
//}
|
||||
|
||||
//RebuildPartBits(model);
|
||||
|
||||
//return;
|
||||
|
||||
// Add pelvis
|
||||
#if false
|
||||
const uint8_t backLow = InsertBone(model, "back_low", "j_spinelower", allocator);
|
||||
TransferWeights(model, GetIndexOfBone(model, "j_spinelower"), backLow);
|
||||
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spineupper"), backLow);
|
||||
#else
|
||||
const uint8_t indexOfPelvis = InsertBone(model, "pelvis", "j_mainroot", allocator);
|
||||
SetBoneQuaternion(model, indexOfPelvis, true, -0.494f, -0.506f, -0.506f, 0.494);
|
||||
|
||||
TransferWeights(model, root, indexOfPelvis);
|
||||
|
||||
@ -891,11 +931,23 @@ namespace Assets
|
||||
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_hip_ri"), indexOfPelvis);
|
||||
SetParentIndexOfBone(model, GetIndexOfBone(model, "tag_stowed_hip_rear"), indexOfPelvis);
|
||||
|
||||
// These two are optional
|
||||
if (GetIndexOfBone(model, "j_coatfront_le") == UCHAR_MAX)
|
||||
{
|
||||
InsertBone(model, "j_coatfront_le", "pelvis", allocator);
|
||||
}
|
||||
|
||||
if (GetIndexOfBone(model, "j_coatfront_ri") == UCHAR_MAX)
|
||||
{
|
||||
InsertBone(model, "j_coatfront_ri", "pelvis", allocator);
|
||||
}
|
||||
|
||||
const uint8_t torsoStabilizer = InsertBone(model, "torso_stabilizer", "pelvis", allocator);
|
||||
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spinelower"), torsoStabilizer);
|
||||
const uint8_t lowerSpine = GetIndexOfBone(model, "j_spinelower");
|
||||
SetParentIndexOfBone(model, lowerSpine, torsoStabilizer);
|
||||
|
||||
const uint8_t backLow = InsertBone(model, "back_low", "j_spinelower", allocator);
|
||||
TransferWeights(model, GetIndexOfBone(model, "j_spinelower"), backLow);
|
||||
TransferWeights(model, lowerSpine, backLow);
|
||||
SetParentIndexOfBone(model, GetIndexOfBone(model, "j_spineupper"), backLow);
|
||||
|
||||
const uint8_t backMid = InsertBone(model, "back_mid", "j_spineupper", allocator);
|
||||
@ -908,29 +960,34 @@ namespace Assets
|
||||
assert(backLow == GetIndexOfBone(model, "back_low"));
|
||||
assert(backMid == GetIndexOfBone(model, "back_mid"));
|
||||
|
||||
// Fix up torso stabilizer
|
||||
model->baseMat[torsoStabilizer].quat[0] = 0.F;
|
||||
model->baseMat[torsoStabilizer].quat[1] = 0.F;
|
||||
model->baseMat[torsoStabilizer].quat[2] = 0.F;
|
||||
model->baseMat[torsoStabilizer].quat[3] = 1.F;
|
||||
// Twister bone
|
||||
SetBoneQuaternion(model, lowerSpine, false, -0.492f, -0.507f, -0.507f, 0.492f);
|
||||
SetBoneQuaternion(model, torsoStabilizer, false, 0.494f, 0.506f, 0.506f, 0.494f);
|
||||
|
||||
const auto spineLowerM1 = GetIndexOfBone(model, "j_spinelower") - model->numRootBones;
|
||||
|
||||
// This doesn't feel like it should be necessary
|
||||
model->trans[spineLowerM1 * 3 + 0] = 0.069828756;
|
||||
model->trans[spineLowerM1 * 3 + 1] = -0.0f;
|
||||
model->trans[spineLowerM1 * 3 + 2] = 5.2035017F;
|
||||
// It is, on singleplayer models unfortunately. Could we add an extra bone to compensate this?
|
||||
// Or compensate it another way?
|
||||
SetBoneTrans(model, GetIndexOfBone(model, "j_spinelower"), false, 0.07, 0.0f, 5.2f);
|
||||
|
||||
//// Euler -180.000 88.572 -90.000
|
||||
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 0] = 16179; // 0.4952
|
||||
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 1] = 16586; // 0.5077
|
||||
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 2] = 16586; // 0.5077
|
||||
model->quats[(torsoStabilizer - model->numRootBones) * 4 + 3] = 16178; // 0.4952
|
||||
// These are often messed up on civilian models, but there is no obvious way to tell from code
|
||||
const auto stowedBack = GetIndexOfBone(model, "tag_stowed_back");
|
||||
if (stowedBack != UCHAR_MAX)
|
||||
{
|
||||
SetBoneTrans(model, stowedBack, false, -0.32f, -6.27f, -2.65F);
|
||||
SetBoneQuaternion(model, stowedBack, false, -0.044, 0.088, -0.995, 0.025);
|
||||
SetBoneTrans(model, stowedBack, true, -9.571f, -2.654f, 51.738f);
|
||||
SetBoneQuaternion(model, stowedBack, true, -0.071f, 0.0f, -0.997f, 0.0f);
|
||||
}
|
||||
|
||||
#else
|
||||
const uint8_t torsoStabilizer = insertBone("torso_stabilizer", "j_mainroot");
|
||||
transferWeights(getIndexOfBone("j_mainroot"), getIndexOfBone("torso_stabilizer"));
|
||||
setParentIndexOfBone(getIndexOfBone("j_spinelower"), torsoStabilizer);
|
||||
if (stowedBack != UCHAR_MAX)
|
||||
{
|
||||
const auto stowedRear = GetIndexOfBone(model, "tag_stowed_hip_rear");
|
||||
SetBoneTrans(model, stowedRear, false, -0.75f, -6.45f, -4.99f);
|
||||
SetBoneQuaternion(model, stowedRear, false, -0.553f, -0.062f, -0.049f, 0.830f);
|
||||
SetBoneTrans(model, stowedBack, true, -9.866f, -4.989f, 36.315f);
|
||||
SetBoneQuaternion(model, stowedRear, true, -0.054, -0.025f, -0.975f, 0.214f);
|
||||
}
|
||||
#endif
|
||||
|
||||
RebuildPartBits(model);
|
||||
|
@ -22,5 +22,8 @@ namespace Assets
|
||||
static void RebuildPartBits(Game::XModel* model);
|
||||
static uint8_t InsertBone(Game::XModel* model, const std::string& boneName, const std::string& parentName, Utils::Memory::Allocator& allocator);
|
||||
static void TransferWeights(Game::XModel* model, const uint8_t origin, const uint8_t destination);
|
||||
|
||||
static void SetBoneTrans(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z);
|
||||
static void SetBoneQuaternion(Game::XModel* model, uint8_t boneIndex, bool baseMat, float x, float y, float z, float w);
|
||||
};
|
||||
}
|
||||
|
@ -314,9 +314,6 @@ namespace Components
|
||||
|
||||
QuickPatch::QuickPatch()
|
||||
{
|
||||
// Do not call DObjCalcAnim
|
||||
Utils::Hook(0x6508C4, DObjCalcAnim, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x06508F6, DObjCalcAnim, HOOK_CALL).install()->quick();
|
||||
|
||||
|
||||
// Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <version.hpp>
|
||||
|
||||
#include "AssetInterfaces/ILocalizeEntry.hpp"
|
||||
#include "Branding.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -21,7 +22,8 @@ namespace Components
|
||||
iw4of::api ZoneBuilder::ExporterAPI(GetExporterAPIParams());
|
||||
std::string ZoneBuilder::DumpingZone{};
|
||||
|
||||
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
|
||||
ZoneBuilder::Zone::Zone(const std::string& name, const std::string& sourceName, const std::string& destination) :
|
||||
indexStart(0), externalSize(0),
|
||||
// Reserve 100MB by default.
|
||||
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
|
||||
// That way we can be sure it won't need to reallocate memory.
|
||||
@ -29,11 +31,17 @@ namespace Components
|
||||
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
|
||||
buffer(0xC800000),
|
||||
zoneName(name),
|
||||
dataMap("zone_source/" + name + ".csv"),
|
||||
destination(destination),
|
||||
dataMap("zone_source/" + sourceName + ".csv"),
|
||||
branding{ nullptr },
|
||||
assetDepth(0),
|
||||
iw4ofApi(getIW4OfApiParams())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ZoneBuilder::Zone::Zone(const std::string& name) : ZoneBuilder::Zone::Zone(name, name, std::format("zone/english/{}.ff", name))
|
||||
{
|
||||
}
|
||||
|
||||
ZoneBuilder::Zone::~Zone()
|
||||
@ -148,7 +156,7 @@ namespace Components
|
||||
{
|
||||
Game::XZoneInfo info;
|
||||
info.name = fastfile.data();
|
||||
info.allocFlags = 0x20;
|
||||
info.allocFlags = Game::DB_ZONE_LOAD;
|
||||
info.freeFlags = 0;
|
||||
|
||||
Game::DB_LoadXAssets(&info, 1, true);
|
||||
@ -449,11 +457,11 @@ namespace Components
|
||||
zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer);
|
||||
outBuffer.append(zoneBuffer);
|
||||
|
||||
std::string outFile = "zone/" + this->zoneName + ".ff";
|
||||
Utils::IO::WriteFile(outFile, outBuffer);
|
||||
|
||||
Logger::Print("done.\n");
|
||||
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", outFile, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
|
||||
Utils::IO::WriteFile(destination, outBuffer);
|
||||
|
||||
Logger::Print("done writing {}\n", destination);
|
||||
Logger::Print("Zone '{}' written with {} assets and {} script strings\n", destination, (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size());
|
||||
}
|
||||
|
||||
void ZoneBuilder::Zone::saveData()
|
||||
@ -759,18 +767,23 @@ namespace Components
|
||||
return header;
|
||||
}
|
||||
|
||||
void ZoneBuilder::RefreshExporterWorkDirectory()
|
||||
std::string ZoneBuilder::GetDumpingZonePath()
|
||||
{
|
||||
if (ZoneBuilder::DumpingZone.empty())
|
||||
{
|
||||
ExporterAPI.set_work_path(std::format("userraw/dump/stray"));
|
||||
return std::format("userraw/dump/stray");
|
||||
}
|
||||
else
|
||||
{
|
||||
ExporterAPI.set_work_path(std::format("userraw/dump/{}", ZoneBuilder::DumpingZone));
|
||||
return std::format("userraw/dump/{}", ZoneBuilder::DumpingZone);
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneBuilder::RefreshExporterWorkDirectory()
|
||||
{
|
||||
ExporterAPI.set_work_path(GetDumpingZonePath());
|
||||
}
|
||||
|
||||
iw4of::api* ZoneBuilder::GetExporter()
|
||||
{
|
||||
return &ExporterAPI;
|
||||
@ -988,10 +1001,10 @@ namespace Components
|
||||
Logger::Print(" --------------------------------------------------------------------------------\n");
|
||||
Logger::Print(" IW4x ZoneBuilder - {}\n", REVISION_STR);
|
||||
Logger::Print(" Commands:\n");
|
||||
Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n");
|
||||
Logger::Print("\t-buildall: builds all zones in zone_source\n");
|
||||
Logger::Print("\t-buildmod [mod name]: Build a mod.ff from the source located in zone_source/mod_name.csv\n");
|
||||
Logger::Print("\t-buildzone [zone]: Builds a zone from a csv located in zone_source\n");
|
||||
Logger::Print("\t-dumpzone [zone]: Loads and dump the specified zone in userraw/dump\n");
|
||||
Logger::Print("\t-verifyzone [zone]: loads and verifies the specified zone\n");
|
||||
Logger::Print("\t-dumpzone [zone]: loads and dump the specified zone\n");
|
||||
Logger::Print("\t-listassets [assettype]: lists all loaded assets of the specified type\n");
|
||||
Logger::Print("\t-quit: quits the program\n");
|
||||
Logger::Print(" --------------------------------------------------------------------------------\n");
|
||||
@ -1176,7 +1189,7 @@ namespace Components
|
||||
|
||||
Game::DB_LoadXAssets(&info, 1, true);
|
||||
AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
ZoneBuilder::ZoneBuilder()
|
||||
@ -1288,43 +1301,139 @@ namespace Components
|
||||
Utils::Hook::Nop(0x5BC791, 5);
|
||||
|
||||
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /* asset*/, const std::string& name, bool* /*restrict*/)
|
||||
{
|
||||
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
|
||||
{
|
||||
ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name));
|
||||
}
|
||||
});
|
||||
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
|
||||
{
|
||||
ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name));
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("dumpzone", [](const Command::Params* params)
|
||||
{
|
||||
if (params->size() < 2) return;
|
||||
|
||||
|
||||
std::string zone = params->get(1);
|
||||
|
||||
ZoneBuilder::DumpingZone = zone;
|
||||
ZoneBuilder::RefreshExporterWorkDirectory();
|
||||
|
||||
std::vector<std::pair<Game::XAssetType, std::string>> assets{};
|
||||
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
|
||||
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
|
||||
|
||||
Logger::Print("Dumping zone '{}'...\n", zone);
|
||||
|
||||
for (const auto& asset : assets)
|
||||
{
|
||||
const auto type = asset.first;
|
||||
const auto name = asset.second;
|
||||
if (ExporterAPI.is_type_supported(type) && name[0] != ',')
|
||||
Utils::IO::CreateDir(GetDumpingZonePath());
|
||||
std::ofstream csv(std::filesystem::path(GetDumpingZonePath()) / std::format("{}.csv", zone));
|
||||
csv
|
||||
<< std::format("### Zone '{}' dumped with Zonebuilder {}", zone, Components::Branding::GetVersionString())
|
||||
<< "\n\n";
|
||||
|
||||
constexpr Game::XAssetType typeOrder[] = {
|
||||
Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP,
|
||||
Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP,
|
||||
Game::XAssetType::ASSET_TYPE_GFXWORLD,
|
||||
Game::XAssetType::ASSET_TYPE_COMWORLD,
|
||||
Game::XAssetType::ASSET_TYPE_FXWORLD,
|
||||
Game::XAssetType::ASSET_TYPE_CLIPMAP_MP,
|
||||
Game::XAssetType::ASSET_TYPE_CLIPMAP_SP,
|
||||
Game::XAssetType::ASSET_TYPE_RAWFILE,
|
||||
Game::XAssetType::ASSET_TYPE_VEHICLE,
|
||||
Game::XAssetType::ASSET_TYPE_WEAPON,
|
||||
Game::XAssetType::ASSET_TYPE_FX,
|
||||
Game::XAssetType::ASSET_TYPE_TRACER,
|
||||
Game::XAssetType::ASSET_TYPE_XMODEL,
|
||||
Game::XAssetType::ASSET_TYPE_MATERIAL,
|
||||
Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET,
|
||||
Game::XAssetType::ASSET_TYPE_PIXELSHADER,
|
||||
Game::XAssetType::ASSET_TYPE_VERTEXSHADER,
|
||||
Game::XAssetType::ASSET_TYPE_VERTEXDECL,
|
||||
Game::XAssetType::ASSET_TYPE_IMAGE,
|
||||
Game::XAssetType::ASSET_TYPE_SOUND,
|
||||
Game::XAssetType::ASSET_TYPE_LOADED_SOUND,
|
||||
Game::XAssetType::ASSET_TYPE_SOUND_CURVE,
|
||||
Game::XAssetType::ASSET_TYPE_PHYSPRESET,
|
||||
};
|
||||
|
||||
std::unordered_map<Game::XAssetType, int> typePriority{};
|
||||
for (auto i = 0; i < ARRAYSIZE(typeOrder); i++)
|
||||
{
|
||||
const auto assetHeader = Game::DB_FindXAssetHeader(type, name.data());
|
||||
if (assetHeader.data)
|
||||
const auto type = typeOrder[i];
|
||||
typePriority.emplace(type, 1 + ARRAYSIZE(typeOrder) - i);
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> invalidAssets{};
|
||||
|
||||
std::sort(assets.begin(), assets.end(), [&](
|
||||
const std::pair<Game::XAssetType, std::string>& a,
|
||||
const std::pair<Game::XAssetType, std::string>& b
|
||||
) {
|
||||
if (a.first == b.first)
|
||||
{
|
||||
|
||||
return a.second.compare(b.second) < 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto priorityA = typePriority[a.first];
|
||||
const auto priorityB = typePriority[b.first];
|
||||
|
||||
if (priorityA == priorityB)
|
||||
{
|
||||
return a.second.compare(b.second) < 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return priorityB < priorityA;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Used to format the CSV
|
||||
Game::XAssetType lastTypeEncountered{};
|
||||
|
||||
for (const auto& asset : assets)
|
||||
{
|
||||
const auto type = asset.first;
|
||||
const auto name = asset.second;
|
||||
if (ExporterAPI.is_type_supported(type) && name[0] != ',')
|
||||
{
|
||||
ExporterAPI.write(type, assetHeader.data);
|
||||
const auto assetHeader = Game::DB_FindXAssetHeader(type, name.data());
|
||||
if (assetHeader.data)
|
||||
{
|
||||
ExporterAPI.write(type, assetHeader.data);
|
||||
const auto typeName = Game::DB_GetXAssetTypeName(type) ;
|
||||
|
||||
if (type != lastTypeEncountered)
|
||||
{
|
||||
csv << "\n### " << typeName << "\n";
|
||||
lastTypeEncountered = type;
|
||||
}
|
||||
|
||||
csv << typeName << "," << name << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Warning(Game::conChannel_t::CON_CHANNEL_ERROR, "Asset {} has disappeared while dumping!\n", name);
|
||||
invalidAssets["The following assets disappeared while dumping"].push_back(std::format("{},{}", Game::DB_GetXAssetTypeName(type), name));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Warning(Game::conChannel_t::CON_CHANNEL_ERROR, "Asset {} has disappeared while dumping!\n", name);
|
||||
invalidAssets["The following assets are unsupported or not dumped as individual assets, but still present in the zone"].push_back(std::format("{},{}", Game::DB_GetXAssetTypeName(type), name));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& kv : invalidAssets)
|
||||
{
|
||||
csv << "\n### " << kv.first << "\n";
|
||||
for (const auto& line : kv.second)
|
||||
{
|
||||
csv << "#" << line << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
csv << std::format("\n### {} assets", assets.size()) << "\n";
|
||||
}
|
||||
|
||||
unload();
|
||||
@ -1339,7 +1448,7 @@ namespace Components
|
||||
|
||||
std::string zone = params->get(1);
|
||||
std::vector<std::pair<Game::XAssetType, std::string>> assets{};
|
||||
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
|
||||
const auto unload = ZoneBuilder::LoadZoneWithTrace(zone, assets);
|
||||
|
||||
int count = 0;
|
||||
for (auto i = assets.begin(); i != assets.end(); ++i, ++count)
|
||||
@ -1361,6 +1470,24 @@ namespace Components
|
||||
Zone(zoneName).build();
|
||||
});
|
||||
|
||||
Command::Add("buildmod", [](const Command::Params* params)
|
||||
{
|
||||
if (params->size() < 2) return;
|
||||
|
||||
std::string modName = params->get(1);
|
||||
Logger::Print("Building zone for mod '{}'...\n", modName);
|
||||
|
||||
const std::string previousFsGame = Dvar::Var("fs_game").get<std::string>();
|
||||
const std::string dir = "mods/" + modName;
|
||||
Utils::IO::CreateDir(dir);
|
||||
|
||||
Dvar::Var("fs_game").set(dir);
|
||||
|
||||
Zone("mod", modName, dir + "/mod.ff").build();
|
||||
|
||||
Dvar::Var("fs_game").set(previousFsGame);
|
||||
});
|
||||
|
||||
Command::Add("buildall", []()
|
||||
{
|
||||
auto path = std::format("{}\\zone_source", (*Game::fs_basepath)->current.string);
|
||||
@ -1414,8 +1541,8 @@ namespace Components
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("listassets", [](const Command::Params* params)
|
||||
{
|
||||
@ -1431,7 +1558,7 @@ namespace Components
|
||||
}, &type, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZoneBuilder::~ZoneBuilder()
|
||||
|
@ -32,7 +32,8 @@ namespace Components
|
||||
private:
|
||||
Zone* builder;
|
||||
};
|
||||
|
||||
|
||||
Zone(const std::string& zoneName, const std::string& sourceName, const std::string& destination);
|
||||
Zone(const std::string& zoneName);
|
||||
~Zone();
|
||||
|
||||
@ -100,6 +101,7 @@ namespace Components
|
||||
iw4of::api iw4ofApi;
|
||||
|
||||
std::string zoneName;
|
||||
std::string destination;
|
||||
Utils::CSV dataMap;
|
||||
|
||||
Utils::Memory::Allocator memAllocator;
|
||||
@ -134,6 +136,7 @@ namespace Components
|
||||
static std::vector<std::pair<Game::XAssetType, std::string>> EndAssetTrace();
|
||||
|
||||
static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder);
|
||||
static std::string GetDumpingZonePath();
|
||||
static void RefreshExporterWorkDirectory();
|
||||
|
||||
static iw4of::api* GetExporter();
|
||||
|
@ -960,7 +960,7 @@ namespace Game
|
||||
|
||||
union XAnimDynamicFrames
|
||||
{
|
||||
char(*_1)[3];
|
||||
uint8_t(*_1)[3];
|
||||
unsigned __int16(*_2)[3];
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user