Subresource cooking and various bug fixes

This commit is contained in:
Jack Andersen 2017-02-12 20:51:47 -10:00
parent 242f9e3a2d
commit 7085b837b7
10 changed files with 501 additions and 323 deletions

View File

@ -1077,10 +1077,7 @@ bool ANCS::Extract(const SpecBase& dataSpec,
bool ANCS::Cook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
hecl::BlenderConnection::DataStream& ds,
bool pc,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc)
const DNAANCS::Actor& actor)
{
/* Search for yaml */
hecl::ProjectPath yamlPath = inPath.getWithExtension(_S(".yaml"), true);
@ -1151,122 +1148,12 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath,
return true;
});
std::unordered_map<std::string, atInt32> boneIdMap;
std::experimental::optional<CINF> rigCinf;
std::experimental::optional<DNAANIM::RigInverter<CINF>> rigInv;
/* Write out CINF resources */
for (const DNAANCS::Actor::Armature& arm : actor.armatures)
{
hecl::SystemStringView sysStr(arm.name);
hecl::ProjectPath pathOut = inPath.ensureAuxInfo(sysStr.sys_str() + _S(".CINF")).getCookedPath(SpecEntMP1);
pathOut.makeDirChain(false);
athena::io::FileWriter w(pathOut.getAbsolutePath(), true, false);
if (w.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"),
pathOut.getRelativePath().c_str());
CINF cinf(arm, boneIdMap);
cinf.write(w);
if (!rigInv)
{
rigCinf.emplace(cinf);
auto matrices = ds.getBoneMatrices(arm.name);
rigInv.emplace(*rigCinf, matrices);
}
}
ds.close();
/* Write out CSKR resources */
for (ANCS::CharacterSet::CharacterInfo& ch : ancs.characterSet.characters)
{
const DNAANCS::Actor::Subtype* subtype = nullptr;
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes)
{
if (!sub.name.compare(ch.name))
{
subtype = &sub;
break;
}
}
if (!subtype)
Log.report(logvisor::Fatal, "unable to find subtype '%s'", ch.name.c_str());
const hecl::ProjectPath& modelPath = subtype->mesh;
if (!modelPath.isFile())
Log.report(logvisor::Fatal, _S("unable to resolve '%s'"), modelPath.getRelativePath().c_str());
hecl::ProjectPath skinIntPath = modelPath.getCookedPath(SpecEntMP1PC).getWithExtension(_S(".skinint"));
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath.getModtime())
if (!modelCookFunc(modelPath))
Log.report(logvisor::Fatal, _S("unable to cook '%s'"), modelPath.getRelativePath().c_str());
athena::io::FileReader skinIO(skinIntPath.getAbsolutePath(), 1024*32, false);
if (skinIO.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s'"), skinIntPath.getRelativePath().c_str());
std::vector<std::vector<uint32_t>> skinBanks;
uint32_t bankCount = skinIO.readUint32Big();
skinBanks.reserve(bankCount);
for (uint32_t i=0 ; i<bankCount ; ++i)
{
skinBanks.emplace_back();
std::vector<uint32_t>& bonesOut = skinBanks.back();
uint32_t boneCount = skinIO.readUint32Big();
bonesOut.reserve(boneCount);
for (uint32_t j=0 ; j<boneCount ; ++j)
{
uint32_t idx = skinIO.readUint32Big();
bonesOut.push_back(idx);
}
}
std::vector<std::string> boneNames;
uint32_t boneNameCount = skinIO.readUint32Big();
boneNames.reserve(boneNameCount);
for (uint32_t i=0 ; i<boneNameCount ; ++i)
boneNames.push_back(skinIO.readString());
skinIO.close();
hecl::SystemStringView sysStr(ch.name);
hecl::ProjectPath skinPath = inPath.ensureAuxInfo(sysStr.sys_str() + _S(".CSKR")).getCookedPath(SpecEntMP1PC);
skinPath.makeDirChain(false);
athena::io::FileWriter skinOut(skinPath.getAbsolutePath(), true, false);
if (skinOut.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"),
skinPath.getRelativePath().c_str());
skinOut.writeUint32Big(bankCount);
for (const std::vector<uint32_t>& bank : skinBanks)
{
skinOut.writeUint32Big(bank.size());
for (uint32_t bIdx : bank)
{
const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name);
if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, "unable to find bone '%s' in %s",
name.c_str(), inPath.getRelativePathUTF8().c_str());
skinOut.writeUint32Big(search->second);
}
}
}
/* Write out ANIM resources */
/* Gather ANIM resources */
ancs.animationSet.animResources.reserve(actor.actions.size());
for (const DNAANCS::Actor::Action& act : actor.actions)
{
hecl::SystemStringView sysStr(act.name);
hecl::ProjectPath pathOut = inPath.ensureAuxInfo(sysStr.sys_str() + _S(".ANIM"));
hecl::ProjectPath cookedOut = pathOut.getCookedPath(SpecEntMP1PC);
cookedOut.makeDirChain(false);
athena::io::FileWriter w(cookedOut.getAbsolutePath(), true, false);
if (w.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"),
cookedOut.getRelativePath().c_str());
ANIM anim(act, boneIdMap, *rigInv, pc);
ancs.animationSet.animResources.emplace_back();
ancs.animationSet.animResources.back().animId = pathOut;
@ -1275,41 +1162,172 @@ bool ANCS::Cook(const hecl::ProjectPath& outPath,
hecl::ProjectPath evntYamlPath = inPath.getWithExtension((hecl::SystemString(_S(".")) +
sysStr.sys_str() +
_S(".evnt.yaml")).c_str(), true);
evntYamlPath = evntYamlPath.ensureAuxInfo(_S(""));
if (evntYamlPath.isFile())
{
athena::io::FileReader reader(evntYamlPath.getAbsolutePath());
if (reader.isOpen())
{
EVNT evnt;
evnt.fromYAMLStream(reader);
hecl::ProjectPath evntCookedOut = evntYamlPath.getCookedPath(SpecEntMP1);
evntCookedOut.makeDirChain(false);
athena::io::FileWriter w(evntCookedOut.getAbsolutePath(), true, false);
if (w.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"),
evntCookedOut.getRelativePath().c_str());
evnt.write(w);
ancs.animationSet.animResources.back().evntId = evntYamlPath;
anim.m_anim->evnt = evntYamlPath;
}
}
anim.write(w);
ancs.animationSet.animResources.back().evntId = evntYamlPath;
}
/* Write out ANCS */
hecl::ProjectPath pathOut = inPath.ensureAuxInfo("").getCookedPath(SpecEntMP1);
athena::io::FileWriter w(pathOut.getAbsolutePath(), true, false);
if (w.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s' for writing"),
pathOut.getRelativePath().c_str());
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
ancs.write(w);
return true;
}
bool ANCS::CookCINF(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor)
{
hecl::SystemString armName(inPath.getAuxInfo().begin(),
inPath.getAuxInfo().end() - 5);
for (const DNAANCS::Actor::Armature& arm : actor.armatures)
{
hecl::SystemStringView sysStr(arm.name);
if (sysStr.sys_str() == armName)
{
std::unordered_map<std::string, atInt32> boneIdMap;
CINF cinf(arm, boneIdMap);
/* Write out CINF resource */
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
cinf.write(w);
return true;
}
}
return false;
}
bool ANCS::CookCSKR(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc)
{
hecl::SystemString subName(inPath.getAuxInfo().begin(),
inPath.getAuxInfo().end() - 5);
hecl::SystemUTF8View subNameView(subName);
/* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap;
for (const DNAANCS::Actor::Armature& arm : actor.armatures)
{
CINF cinf(arm, boneIdMap);
}
const DNAANCS::Actor::Subtype* subtype = nullptr;
for (const DNAANCS::Actor::Subtype& sub : actor.subtypes)
{
if (!sub.name.compare(subNameView.str()))
{
subtype = &sub;
break;
}
}
if (!subtype)
Log.report(logvisor::Fatal, _S("unable to find subtype '%s'"), subName.c_str());
const hecl::ProjectPath& modelPath = subtype->mesh;
if (!modelPath.isFile())
Log.report(logvisor::Fatal, _S("unable to resolve '%s'"), modelPath.getRelativePath().c_str());
hecl::ProjectPath skinIntPath = modelPath.getCookedPath(SpecEntMP1PC).getWithExtension(_S(".skinint"));
if (!skinIntPath.isFileOrGlob() || skinIntPath.getModtime() < modelPath.getModtime())
if (!modelCookFunc(modelPath))
Log.report(logvisor::Fatal, _S("unable to cook '%s'"), modelPath.getRelativePath().c_str());
athena::io::FileReader skinIO(skinIntPath.getAbsolutePath(), 1024*32, false);
if (skinIO.hasError())
Log.report(logvisor::Fatal, _S("unable to open '%s'"), skinIntPath.getRelativePath().c_str());
std::vector<std::vector<uint32_t>> skinBanks;
uint32_t bankCount = skinIO.readUint32Big();
skinBanks.reserve(bankCount);
for (uint32_t i=0 ; i<bankCount ; ++i)
{
skinBanks.emplace_back();
std::vector<uint32_t>& bonesOut = skinBanks.back();
uint32_t boneCount = skinIO.readUint32Big();
bonesOut.reserve(boneCount);
for (uint32_t j=0 ; j<boneCount ; ++j)
{
uint32_t idx = skinIO.readUint32Big();
bonesOut.push_back(idx);
}
}
std::vector<std::string> boneNames;
uint32_t boneNameCount = skinIO.readUint32Big();
boneNames.reserve(boneNameCount);
for (uint32_t i=0 ; i<boneNameCount ; ++i)
boneNames.push_back(skinIO.readString());
skinIO.close();
athena::io::TransactionalFileWriter skinOut(outPath.getAbsolutePath());
skinOut.writeUint32Big(bankCount);
for (const std::vector<uint32_t>& bank : skinBanks)
{
skinOut.writeUint32Big(bank.size());
for (uint32_t bIdx : bank)
{
const std::string& name = boneNames[bIdx];
auto search = boneIdMap.find(name);
if (search == boneIdMap.cend())
Log.report(logvisor::Fatal, "unable to find bone '%s' in %s",
name.c_str(), inPath.getRelativePathUTF8().c_str());
skinOut.writeUint32Big(search->second);
}
}
return true;
}
bool ANCS::CookANIM(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
hecl::BlenderConnection::DataStream& ds,
bool pc)
{
hecl::SystemString actName(inPath.getAuxInfo().begin(),
inPath.getAuxInfo().end() - 5);
hecl::SystemUTF8View actNameView(actName);
DNAANCS::Actor::Action action = ds.compileActionChannelsOnly(actNameView.str());
if (!actor.armatures.size())
Log.report(logvisor::Fatal, _S("0 armatures in %s"),
inPath.getRelativePath().c_str());
/* Build bone ID map */
std::unordered_map<std::string, atInt32> boneIdMap;
std::experimental::optional<DNAANIM::RigInverter<CINF>> rigInv;
for (const DNAANCS::Actor::Armature& arm : actor.armatures)
{
CINF cinf(arm, boneIdMap);
if (!rigInv)
{
auto matrices = ds.getBoneMatrices(arm.name);
rigInv.emplace(cinf, matrices);
}
}
ANIM anim(action, boneIdMap, *rigInv, pc);
/* Check for associated EVNT YAML */
hecl::ProjectPath evntYamlPath = inPath.getWithExtension((hecl::SystemString(_S(".")) + actName +
_S(".evnt.yaml")).c_str(), true);
evntYamlPath = evntYamlPath.ensureAuxInfo(_S(""));
if (evntYamlPath.isFile())
anim.m_anim->evnt = evntYamlPath;
/* Write out ANIM resource */
athena::io::TransactionalFileWriter w(outPath.getAbsolutePath());
anim.write(w);
return true;
}
}
}

View File

@ -542,10 +542,22 @@ struct ANCS : BigYAML
static bool Cook(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
hecl::BlenderConnection::DataStream& ds,
bool pc,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc);
const DNAANCS::Actor& actor);
static bool CookCINF(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor);
static bool CookCSKR(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
const std::function<bool(const hecl::ProjectPath& modelPath)>& modelCookFunc);
static bool CookANIM(const hecl::ProjectPath& outPath,
const hecl::ProjectPath& inPath,
const DNAANCS::Actor& actor,
hecl::BlenderConnection::DataStream& ds,
bool pc);
};
}

View File

@ -395,7 +395,7 @@ void ANIM::ANIM2::write(athena::io::IStreamWriter& writer) const
frameCount += 1;
}
}
head.keyBitmapBitCount = frameCount;
head.keyBitmapBitCount = keyBmp.getBitCount();
head.duration = frameCount * mainInterval;
head.boneChannelCount = bones.size();

View File

@ -89,6 +89,22 @@ struct EVNT : BigYAML
evnt.toYAMLStream(writer);
return true;
}
static bool Cook(const hecl::ProjectPath& inPath, const hecl::ProjectPath& outPath)
{
EVNT evnt;
athena::io::FileReader reader(inPath.getAbsolutePath());
evnt.fromYAMLStream(reader);
athena::io::FileWriter ws(outPath.getAbsolutePath());
evnt.write(ws);
return true;
}
void gatherDependencies(std::vector<hecl::ProjectPath>& pathsOut) const
{
for (const ParticlePOINode& node : particlePOINodes)
g_curSpec->flattenDependencies(node.id, pathsOut);
}
};
}

View File

@ -78,14 +78,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
hecl::ProjectPath mapwPath = inPath.ensureAuxInfo(_S("MAPW"));
mlvl.worldMap = mapwPath;
std::vector<urde::SObjectTag> mapaTags;
mapaTags.reserve(wld.areas.size());
SAVW savw = {};
savw.header.magic = 0xC001D00D;
savw.header.version = 0x3;
std::unordered_set<UniqueID32> addedScans;
size_t areaIdx = 0;
size_t nameOffset = 0;
for (const World::Area& area : wld.areas)
@ -109,8 +101,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
r.enumerate<atUint32>("memrelays", memRelays);
}
savw.relays.insert(savw.relays.end(), memRelays.begin(), memRelays.end());
/* Bare minimum we'll need exactly the same number of links as relays */
std::vector<MemRelayLink> memRelayLinks(memRelays.size());
@ -156,11 +146,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
/* Finish last area */
mlvl.finishLastArea();
/* Area map */
hecl::ProjectPath mapPath(area.path, _S("/!map.blend"));
if (mapPath.isFile())
mapaTags.push_back(g_curSpec->BuildTagFromPath(mapPath, btok));
/* Populate area record */
mlvl.areas.emplace_back();
MLVL::Area& areaOut = mlvl.areas.back();
@ -242,7 +227,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
{
g_ThreadBlenderToken.reset(&btok);
std::vector<hecl::ProjectPath> depPaths;
std::vector<Scan> scans;
for (std::unique_ptr<IScriptObject>& obj : layer.objects)
{
if (obj->type == int(urde::EScriptObjectType::MemoryRelay))
@ -260,7 +244,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
{
/* We must have a new relay, let's track it */
memRelayLinks.push_back(linkOut);
savw.relays.push_back(memRelay.id);
memRelays.push_back(memRelay.id);
}
else /* Lets insert this in it's appropriate location, target order doesn't matter */
@ -273,27 +256,8 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
}
}
}
else if (obj->type == int(urde::EScriptObjectType::SpecialFunction))
{
SpecialFunction& specialFunc = static_cast<SpecialFunction&>(*obj);
if (specialFunc.function == ESpecialFunctionType::CinematicSkip)
savw.skippableCutscenes.push_back(specialFunc.id);
else if (specialFunc.function == ESpecialFunctionType::ScriptLayerController)
{
savw.layers.emplace_back();
SAVWCommon::Layer& layer = savw.layers.back();
layer.areaId = specialFunc.layerSwitch.area;
layer.layer = specialFunc.layerSwitch.layerIdx;
}
}
else if (obj->type == int(urde::EScriptObjectType::Door))
{
DoorArea& doorArea = static_cast<DoorArea&>(*obj);
savw.doors.push_back(doorArea.id);
}
obj->gatherDependencies(depPaths);
obj->gatherScans(scans);
}
/* Cull duplicate paths and add typed hash to list */
@ -307,22 +271,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
areaOut.deps.emplace_back(tag.id, tag.type);
}
}
/* Cull duplicate scans and add to list */
for (const Scan& scan : scans)
{
if (!scan.scanId)
continue;
if (addedScans.find(scan.scanId) == addedScans.cend())
{
addedScans.insert(scan.scanId);
hecl::ProjectPath scanPath = UniqueIDBridge::TranslatePakIdToPath(scan.scanId);
savw.scans.emplace_back();
Scan& scanOut = savw.scans.back();
scanOut.scanId = scan.scanId;
scanOut.category = SAVWCommon::EScanCategory(SCAN::GetCategory(scanPath));
}
}
}
hecl::SystemUTF8View layerU8(layerName);
@ -382,32 +330,176 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
{
athena::io::FileWriter fo(outPath.getAbsolutePath());
mlvl.write(fo);
int64_t rem = fo.position() % 32;
if (rem)
for (int64_t i=0 ; i<32-rem ; ++i)
fo.writeBytes((atInt8*)"\xff", 1);
}
return true;
}
bool MLVL::CookMAPW(const hecl::ProjectPath& outPath,
const World& wld,
hecl::BlenderToken& btok)
{
std::vector<urde::SObjectTag> mapaTags;
mapaTags.reserve(wld.areas.size());
for (const World::Area& area : wld.areas)
{
if (area.path.getPathType() != hecl::ProjectPath::Type::Directory)
continue;
/* Area map */
hecl::ProjectPath mapPath(area.path, _S("/!map.blend"));
if (mapPath.isFile())
mapaTags.push_back(g_curSpec->BuildTagFromPath(mapPath, btok));
}
/* Write out MAPW */
{
hecl::ProjectPath mapwCooked = mapwPath.getCookedPath(*g_curSpec->overrideDataSpec(mapwPath, nullptr, btok));
mapwCooked.makeDirChain(false);
athena::io::FileWriter fo(mapwCooked.getAbsolutePath());
athena::io::FileWriter fo(outPath.getAbsolutePath());
fo.writeUint32Big(0xDEADF00D);
fo.writeUint32Big(1);
fo.writeUint32Big(mapaTags.size());
for (const urde::SObjectTag& mapa : mapaTags)
fo.writeUint32Big(mapa.id);
int64_t rem = fo.position() % 32;
if (rem)
for (int64_t i=0 ; i<32-rem ; ++i)
fo.writeBytes((atInt8*)"\xff", 1);
}
return true;
}
bool MLVL::CookSAVW(const hecl::ProjectPath& outPath,
const World& wld)
{
SAVW savw = {};
savw.header.magic = 0xC001D00D;
savw.header.version = 0x3;
std::unordered_set<UniqueID32> addedScans;
for (const World::Area& area : wld.areas)
{
if (area.path.getPathType() != hecl::ProjectPath::Type::Directory)
continue;
hecl::ProjectPath areaPath(area.path, _S("/!area.blend"));
if (!areaPath.isFile())
continue;
hecl::ProjectPath memRelayPath(area.path, _S("/!memoryrelays.yaml"));
std::vector<atUint32> memRelays;
if (memRelayPath.isFile())
{
athena::io::FileReader fr(memRelayPath.getAbsolutePath());
athena::io::YAMLDocReader r;
if (r.parse(&fr))
r.enumerate<atUint32>("memrelays", memRelays);
}
savw.relays.insert(savw.relays.end(), memRelays.begin(), memRelays.end());
hecl::DirectoryEnumerator dEnum(area.path.getAbsolutePath(), hecl::DirectoryEnumerator::Mode::DirsSorted);
for (const hecl::DirectoryEnumerator::Entry& e : dEnum)
{
hecl::SystemString layerName;
hecl::SystemChar* endCh = nullptr;
hecl::StrToUl(e.m_name.c_str(), &endCh, 0);
if (!endCh)
layerName = hecl::StringUtils::TrimWhitespace(e.m_name);
else
layerName = hecl::StringUtils::TrimWhitespace(hecl::SystemString(endCh));
hecl::ProjectPath objectsPath(area.path, e.m_name + _S("/!objects.yaml"));
if (objectsPath.isNone())
continue;
SCLY::ScriptLayer layer;
{
athena::io::FileReader freader(objectsPath.getAbsolutePath());
if (!freader.isOpen())
continue;
if (!BigYAML::ValidateFromYAMLStream<DNAMP1::SCLY::ScriptLayer>(freader))
continue;
athena::io::YAMLDocReader reader;
if (!reader.parse(&freader))
continue;
layer.read(reader);
}
/* Gather memory relays, scans, and dependencies */
{
std::vector<Scan> scans;
for (std::unique_ptr<IScriptObject>& obj : layer.objects)
{
if (obj->type == int(urde::EScriptObjectType::MemoryRelay))
{
MemoryRelay& memRelay = static_cast<MemoryRelay&>(*obj);
auto iter = std::find(memRelays.begin(), memRelays.end(), memRelay.id);
if (iter == memRelays.end())
{
/* We must have a new relay, let's track it */
savw.relays.push_back(memRelay.id);
memRelays.push_back(memRelay.id);
}
}
else if (obj->type == int(urde::EScriptObjectType::SpecialFunction))
{
SpecialFunction& specialFunc = static_cast<SpecialFunction&>(*obj);
if (specialFunc.function == ESpecialFunctionType::CinematicSkip)
savw.skippableCutscenes.push_back(specialFunc.id);
else if (specialFunc.function == ESpecialFunctionType::ScriptLayerController)
{
savw.layers.emplace_back();
SAVWCommon::Layer& layer = savw.layers.back();
layer.areaId = specialFunc.layerSwitch.area;
layer.layer = specialFunc.layerSwitch.layerIdx;
}
}
else if (obj->type == int(urde::EScriptObjectType::Door))
{
DoorArea& doorArea = static_cast<DoorArea&>(*obj);
savw.doors.push_back(doorArea.id);
}
obj->gatherScans(scans);
}
/* Cull duplicate scans and add to list */
for (const Scan& scan : scans)
{
if (!scan.scanId)
continue;
if (addedScans.find(scan.scanId) == addedScans.cend())
{
addedScans.insert(scan.scanId);
hecl::ProjectPath scanPath = UniqueIDBridge::TranslatePakIdToPath(scan.scanId);
savw.scans.emplace_back();
Scan& scanOut = savw.scans.back();
scanOut.scanId = scan.scanId;
scanOut.category = SAVWCommon::EScanCategory(SCAN::GetCategory(scanPath));
}
}
}
}
}
/* Write out SAVW */
{
savw.header.areaCount = mlvl.areaCount;
savw.header.areaCount = wld.areas.size();
savw.skippableCutsceneCount = savw.skippableCutscenes.size();
savw.relayCount = savw.relays.size();
savw.layerCount = savw.layers.size();
savw.doorCount = savw.doors.size();
savw.scanCount = savw.scans.size();
hecl::ProjectPath savwCooked = savwPath.getCookedPath(*g_curSpec->overrideDataSpec(savwPath, nullptr, btok));
savwCooked.makeDirChain(false);
athena::io::FileWriter fo(savwCooked.getAbsolutePath());
athena::io::FileWriter fo(outPath.getAbsolutePath());
savw.write(fo);
int64_t rem = fo.position() % 32;
if (rem)
@ -417,5 +509,6 @@ bool MLVL::Cook(const hecl::ProjectPath& outPath, const hecl::ProjectPath& inPat
return true;
}
}
}

View File

@ -155,6 +155,13 @@ struct MLVL : BigYAML
const hecl::ProjectPath& inPath,
const World& wld,
hecl::BlenderToken& btok);
static bool CookMAPW(const hecl::ProjectPath& outPath,
const World& wld,
hecl::BlenderToken& btok);
static bool CookSAVW(const hecl::ProjectPath& outPath,
const World& wld);
};
}

View File

@ -493,7 +493,7 @@ void ANIM::ANIM2::write(athena::io::IStreamWriter& writer) const
keyBmp.setBit(frame);
frameCount = frame + 1;
}
head.keyBitmapBitCount = frameCount;
head.keyBitmapBitCount = keyBmp.getBitCount();
head.duration = frameCount * mainInterval;
head.boneChannelCount = bones.size();

View File

@ -463,6 +463,8 @@ struct SpecMP1 : SpecBase
return true;
else if (!strcmp(classType, DNAMP1::HINT::DNAType()))
return true;
else if (!strcmp(classType, DNAMP1::EVNT::DNAType()))
return true;
else if (!strcmp(classType, "ATBL"))
return true;
else if (!strcmp(classType, "MP1OriginalIDs"))
@ -559,97 +561,97 @@ struct SpecMP1 : SpecBase
urde::SObjectTag resTag;
if (reader.ClassTypeOperation([&](const char* className) -> bool {
if (!strcmp(className, "GPSM"))
{
resTag.type = SBIG('PART');
return true;
}
if (!strcmp(className, "SWSH"))
{
resTag.type = SBIG('SWHC');
return true;
}
if (!strcmp(className, "ELSM"))
{
resTag.type = SBIG('ELSC');
return true;
}
if (!strcmp(className, "WPSM"))
{
resTag.type = SBIG('WPSC');
return true;
}
if (!strcmp(className, "CRSM"))
{
resTag.type = SBIG('CRSC');
return true;
}
if (!strcmp(className, "DPSM"))
{
resTag.type = SBIG('DPSC');
return true;
}
else if (!strcmp(className, "FONT"))
{
resTag.type = SBIG('FONT');
return true;
}
else if (!strcmp(className, "urde::DNAMP1::EVNT"))
{
resTag.type = SBIG('EVNT');
return true;
}
else if (!strcmp(className, "urde::DGRP"))
{
resTag.type = SBIG('DGRP');
return true;
}
else if (!strcmp(className, "urde::DNAMP1::STRG"))
{
resTag.type = SBIG('STRG');
return true;
}
else if (!strcmp(className, "urde::DNAMP1::SCAN"))
{
resTag.type = SBIG('SCAN');
return true;
}
else if (!strcmp(className, "DataSpec::DNAMP1::CTweakPlayerRes") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakGunRes") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakSlideShow") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakPlayer") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakCameraBob") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakGame") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakTargeting") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakAutoMapper") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakGui") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakPlayerControl") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakBall") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakParticle") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakGuiColors") ||
!strcmp(className, "DataSpec::DNAMP1::CTweakPlayerGun"))
{
resTag.type = SBIG('CTWK');
return true;
}
else if (!strcmp(className, "DataSpec::DNAMP1::HINT"))
{
resTag.type = SBIG('HINT');
return true;
}
else if (!strcmp(className, "ATBL"))
{
resTag.type = SBIG('ATBL');
return true;
}
else if (!strcmp(className, "MP1OriginalIDs"))
{
resTag.type = SBIG('OIDS');
return true;
}
if (!strcmp(className, DNAParticle::GPSM<UniqueID32>::DNAType()))
{
resTag.type = SBIG('PART');
return true;
}
if (!strcmp(className, DNAParticle::SWSH<UniqueID32>::DNAType()))
{
resTag.type = SBIG('SWHC');
return true;
}
if (!strcmp(className, DNAParticle::ELSM<UniqueID32>::DNAType()))
{
resTag.type = SBIG('ELSC');
return true;
}
if (!strcmp(className, DNAParticle::WPSM<UniqueID32>::DNAType()))
{
resTag.type = SBIG('WPSC');
return true;
}
if (!strcmp(className, DNAParticle::CRSM<UniqueID32>::DNAType()))
{
resTag.type = SBIG('CRSC');
return true;
}
if (!strcmp(className, DNAParticle::DPSM<UniqueID32>::DNAType()))
{
resTag.type = SBIG('DPSC');
return true;
}
else if (!strcmp(className, DNAFont::FONT<UniqueID32>::DNAType()))
{
resTag.type = SBIG('FONT');
return true;
}
else if (!strcmp(className, DNAMP1::EVNT::DNAType()))
{
resTag.type = SBIG('EVNT');
return true;
}
else if (!strcmp(className, DNADGRP::DGRP<UniqueID32>::DNAType()))
{
resTag.type = SBIG('DGRP');
return true;
}
else if (!strcmp(className, DataSpec::DNAMP1::STRG::DNAType()))
{
resTag.type = SBIG('STRG');
return true;
}
else if (!strcmp(className, DataSpec::DNAMP1::SCAN::DNAType()))
{
resTag.type = SBIG('SCAN');
return true;
}
else if (!strcmp(className, DataSpec::DNAMP1::CTweakPlayerRes::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakGunRes::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakSlideShow::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakPlayer::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakCameraBob::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakGame::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakTargeting::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakAutoMapper::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakGui::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakPlayerControl::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakBall::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakParticle::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakGuiColors::DNAType()) ||
!strcmp(className, DataSpec::DNAMP1::CTweakPlayerGun::DNAType()))
{
resTag.type = SBIG('CTWK');
return true;
}
else if (!strcmp(className, DataSpec::DNAMP1::HINT::DNAType()))
{
resTag.type = SBIG('HINT');
return true;
}
else if (!strcmp(className, "ATBL"))
{
resTag.type = SBIG('ATBL');
return true;
}
else if (!strcmp(className, "MP1OriginalIDs"))
{
resTag.type = SBIG('OIDS');
return true;
}
return false;
}))
return false;
}))
{
resTag.id = path.hash().val32();
fclose(fp);
@ -676,25 +678,35 @@ struct SpecMP1 : SpecBase
void cookActor(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast,
hecl::BlenderToken& btok, FCookProgress progress)
{
/*
if (hecl::StringUtils::EndsWith(in.getAuxInfo(), _S(".CINF")))
return {SBIG('CINF'), path.hash().val32()};
{
Actor actor = ds.compileActorCharacterOnly();
DNAMP1::ANCS::CookCINF(out, in, actor);
}
else if (hecl::StringUtils::EndsWith(in.getAuxInfo(), _S(".CSKR")))
return {SBIG('CSKR'), path.hash().val32()};
{
Actor actor = ds.compileActorCharacterOnly();
ds.close();
DNAMP1::ANCS::CookCSKR(out, in, actor, [&](const hecl::ProjectPath& modelPath) -> bool {
hecl::ProjectPath cooked;
if (m_pc)
cooked = modelPath.getCookedPath(SpecEntMP1PC);
else
cooked = modelPath.getCookedPath(SpecEntMP1);
doCook(modelPath, cooked, fast, btok, progress);
return true;
});
}
else if (hecl::StringUtils::EndsWith(in.getAuxInfo(), _S(".ANIM")))
return {SBIG('ANIM'), path.hash().val32()};
*/
Actor actor = ds.compileActor();
DNAMP1::ANCS::Cook(out, in, actor, ds, m_pc, [&](const hecl::ProjectPath& modelPath) -> bool {
hecl::ProjectPath cooked;
if (m_pc)
cooked = modelPath.getCookedPath(SpecEntMP1PC);
else
cooked = modelPath.getCookedPath(SpecEntMP1);
doCook(modelPath, cooked, fast, btok, progress);
return true;
});
{
Actor actor = ds.compileActorCharacterOnly();
DNAMP1::ANCS::CookANIM(out, in, actor, ds, m_pc);
}
else
{
Actor actor = ds.compileActor();
DNAMP1::ANCS::Cook(out, in, actor);
}
}
void cookArea(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast,
@ -734,9 +746,24 @@ struct SpecMP1 : SpecBase
void cookWorld(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds, bool fast,
hecl::BlenderToken& btok, FCookProgress progress)
{
BlendStream::World world = ds.compileWorld();
ds.close();
DNAMP1::MLVL::Cook(out, in, world, btok);
if (hecl::StringUtils::EndsWith(in.getAuxInfo(), _S("MAPW")))
{
BlendStream::World world = ds.compileWorld();
ds.close();
DNAMP1::MLVL::CookMAPW(out, world, btok);
}
else if (hecl::StringUtils::EndsWith(in.getAuxInfo(), _S("SAVW")))
{
BlendStream::World world = ds.compileWorld();
ds.close();
DNAMP1::MLVL::CookSAVW(out, world);
}
else
{
BlendStream::World world = ds.compileWorld();
ds.close();
DNAMP1::MLVL::Cook(out, in, world, btok);
}
}
void cookGuiFrame(const hecl::ProjectPath& out, const hecl::ProjectPath& in, BlendStream& ds,
@ -909,6 +936,10 @@ struct SpecMP1 : SpecBase
{
DNAMP1::HINT::Cook(in, out);
}
else if (!classStr.compare(DNAMP1::EVNT::DNAType()))
{
DNAMP1::EVNT::Cook(in, out);
}
else if (!classStr.compare("ATBL"))
{
DNAAudio::ATBL::Cook(in, out);
@ -984,6 +1015,12 @@ struct SpecMP1 : SpecBase
font.read(reader);
font.gatherDependencies(pathsOut);
}
else if (!classStr.compare(DNAMP1::EVNT::DNAType()))
{
DNAMP1::EVNT evnt;
evnt.read(reader);
evnt.gatherDependencies(pathsOut);
}
}
}

View File

@ -103,9 +103,9 @@ void CGuiTextSupport::Update(float dt)
{
if (CTextRenderBuffer* buf = GetCurrentPageRenderBuffer())
{
float chStartTime = 0.f;
for (s32 i=0 ; i<buf->GetPrimitiveCount() ; ++i)
{
float chStartTime = 0.f;
for (const std::pair<float, int>& p : x40_primStartTimes)
{
if (p.second < i)
@ -116,14 +116,9 @@ void CGuiTextSupport::Update(float dt)
break;
}
#if 0
CTextRenderBuffer::Primitive prim = x54_renderBuf->GetPrimitive(i);
prim.x0_color1.a = std::min(std::max(0.f, (x30_curTime - chStartTime) / x48_chFadeTime), 1.f);
x54_renderBuf->SetPrimitive(prim, i);
#else
buf->SetPrimitiveOpacity(i,
std::min(std::max(0.f, (x3c_curTime - chStartTime) / x54_chFadeTime), 1.f));
#endif
chStartTime += 1.f / x58_chRate;
}
}
x3c_curTime += dt;

2
hecl

@ -1 +1 @@
Subproject commit bcffcf3105090648ba44b3aa69a5d906986be531
Subproject commit 406079b5bcfffd2953f541d27fdc869a07c88c95