mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-22 14:44:22 +00:00
feat: add eSceneType enum, filter scene graph to general scenes, zone parsing improvements
- Add eSceneType enum (General, Audio) replacing raw uint32_t in SceneRef - Filter BuildSceneGraph to only include General scenes - Skip transitions referencing non-general scenes in adjacency graph - Rename SceneRef unknown fields to scenePosition/sceneRadius - Zone parsing and Level improvements Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -107,7 +107,7 @@ void Level::ReadChunks(std::istream& file) {
|
||||
uint32_t initPos = uint32_t(file.tellg());
|
||||
uint32_t header = 0;
|
||||
BinaryIO::BinaryRead(file, header);
|
||||
if (header == CHNK_HEADER) { //Make sure we're reading a valid CHNK
|
||||
if (header == CHNK_HEADER) {
|
||||
Header header;
|
||||
BinaryIO::BinaryRead(file, header.id);
|
||||
BinaryIO::BinaryRead(file, header.chunkVersion);
|
||||
@@ -118,83 +118,39 @@ void Level::ReadChunks(std::istream& file) {
|
||||
uint32_t target = initPos + header.size;
|
||||
file.seekg(header.startPosition);
|
||||
|
||||
//We're currently not loading env or particle data
|
||||
if (header.id == ChunkTypeID::FileInfo) {
|
||||
ReadFileInfoChunk(file, header);
|
||||
} else if (header.id == ChunkTypeID::SceneEnviroment) {
|
||||
ReadEnvironmentChunk(file, header);
|
||||
} else if (header.id == ChunkTypeID::SceneObjectData) {
|
||||
ReadSceneObjectDataChunk(file, header);
|
||||
} else if (header.id == ChunkTypeID::SceneParticleData) {
|
||||
ReadParticleChunk(file, header);
|
||||
}
|
||||
|
||||
m_ChunkHeaders.insert(std::make_pair(header.id, header));
|
||||
file.seekg(target);
|
||||
} else {
|
||||
if (initPos == std::streamoff(0)) { //Really old chunk version
|
||||
if (initPos == std::streamoff(0)) {
|
||||
// Old LVL format without CHNK headers — environment + objects inline
|
||||
file.seekg(0);
|
||||
Header header;
|
||||
header.id = ChunkTypeID::FileInfo;
|
||||
BinaryIO::BinaryRead(file, header.chunkVersion);
|
||||
BinaryIO::BinaryRead(file, header.chunkType);
|
||||
uint8_t important = 0;
|
||||
BinaryIO::BinaryRead(file, important);
|
||||
// file.ignore(1); //probably used
|
||||
uint8_t hasEditorData = 0;
|
||||
BinaryIO::BinaryRead(file, hasEditorData);
|
||||
if (header.chunkVersion > 36) {
|
||||
BinaryIO::BinaryRead(file, header.fileInfo.revision);
|
||||
}
|
||||
// HARDCODED 3
|
||||
if (header.chunkVersion >= 45) file.ignore(4);
|
||||
file.ignore(4 * (4 * 3));
|
||||
|
||||
if (header.chunkVersion >= 31) {
|
||||
if (header.chunkVersion >= 39) {
|
||||
file.ignore(12 * 4);
|
||||
|
||||
if (header.chunkVersion >= 40) {
|
||||
uint32_t s = 0;
|
||||
BinaryIO::BinaryRead(file, s);
|
||||
for (uint32_t i = 0; i < s; ++i) {
|
||||
file.ignore(4 * 3); //a uint and two floats
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file.ignore(8);
|
||||
}
|
||||
|
||||
file.ignore(3 * 4);
|
||||
}
|
||||
|
||||
if (header.chunkVersion >= 36) {
|
||||
file.ignore(3 * 4);
|
||||
}
|
||||
|
||||
if (header.chunkVersion < 42) {
|
||||
file.ignore(3 * 4);
|
||||
|
||||
if (header.chunkVersion >= 33) {
|
||||
file.ignore(4 * 4);
|
||||
}
|
||||
}
|
||||
|
||||
// skydome info
|
||||
uint32_t count = 0;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
file.ignore(count);
|
||||
|
||||
if (header.chunkVersion >= 33) {
|
||||
for (uint32_t i = 0; i < 5; ++i) {
|
||||
uint32_t count = 0;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
file.ignore(count);
|
||||
}
|
||||
}
|
||||
// editor settings
|
||||
if (!important && header.chunkVersion >= 37){
|
||||
file.ignore(4);
|
||||
|
||||
uint32_t count = 0;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
file.ignore(count * 12);
|
||||
|
||||
// Read environment data inline (no absolute offsets in old format)
|
||||
ReadLighting(file, header.chunkVersion);
|
||||
ReadSkydome(file, header.chunkVersion);
|
||||
if (!hasEditorData && header.chunkVersion >= 37) {
|
||||
ReadEditor(file);
|
||||
}
|
||||
m_HasEnvironment = true;
|
||||
|
||||
header.id = ChunkTypeID::SceneObjectData;
|
||||
header.fileInfo.version = header.chunkVersion;
|
||||
@@ -213,6 +169,157 @@ void Level::ReadFileInfoChunk(std::istream& file, Header& header) {
|
||||
BinaryIO::BinaryRead(file, header.fileInfo.particleChunkStart);
|
||||
}
|
||||
|
||||
void Level::ReadLighting(std::istream& file, uint32_t version) {
|
||||
auto& li = m_Environment.lighting;
|
||||
if (version >= 45) BinaryIO::BinaryRead(file, li.blendTime);
|
||||
for (float& f : li.ambient) BinaryIO::BinaryRead(file, f);
|
||||
for (float& f : li.specular) BinaryIO::BinaryRead(file, f);
|
||||
for (float& f : li.upperHemi) BinaryIO::BinaryRead(file, f);
|
||||
BinaryIO::BinaryRead(file, li.position);
|
||||
|
||||
if (version >= 39) {
|
||||
li.hasDrawDistances = true;
|
||||
auto readDD = [&](LvlDrawDistances& dd) {
|
||||
BinaryIO::BinaryRead(file, dd.fogNear);
|
||||
BinaryIO::BinaryRead(file, dd.fogFar);
|
||||
BinaryIO::BinaryRead(file, dd.postFogSolid);
|
||||
BinaryIO::BinaryRead(file, dd.postFogFade);
|
||||
BinaryIO::BinaryRead(file, dd.staticObjDistance);
|
||||
BinaryIO::BinaryRead(file, dd.dynamicObjDistance);
|
||||
};
|
||||
readDD(li.minDraw);
|
||||
readDD(li.maxDraw);
|
||||
}
|
||||
if (version >= 40) {
|
||||
uint32_t numCull = 0;
|
||||
BinaryIO::BinaryRead(file, numCull);
|
||||
li.cullVals.reserve(numCull);
|
||||
for (uint32_t i = 0; i < numCull; ++i) {
|
||||
LvlCullVal cv;
|
||||
BinaryIO::BinaryRead(file, cv.groupID);
|
||||
BinaryIO::BinaryRead(file, cv.min);
|
||||
BinaryIO::BinaryRead(file, cv.max);
|
||||
li.cullVals.push_back(cv);
|
||||
}
|
||||
}
|
||||
if (version >= 31 && version < 39) {
|
||||
BinaryIO::BinaryRead(file, li.fogNear);
|
||||
BinaryIO::BinaryRead(file, li.fogFar);
|
||||
}
|
||||
if (version >= 31) {
|
||||
for (float& f : li.fogColor) BinaryIO::BinaryRead(file, f);
|
||||
}
|
||||
if (version >= 36) {
|
||||
for (float& f : li.dirLight) BinaryIO::BinaryRead(file, f);
|
||||
}
|
||||
if (version < 42) {
|
||||
li.hasSpawn = true;
|
||||
BinaryIO::BinaryRead(file, li.startPosition);
|
||||
if (version >= 33) {
|
||||
BinaryIO::BinaryRead(file, li.startRotation.w);
|
||||
BinaryIO::BinaryRead(file, li.startRotation.x);
|
||||
BinaryIO::BinaryRead(file, li.startRotation.y);
|
||||
BinaryIO::BinaryRead(file, li.startRotation.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Level::ReadSkydome(std::istream& file, uint32_t version) {
|
||||
auto& si = m_Environment.skydome;
|
||||
BinaryIO::ReadString<uint32_t>(file, si.filename, BinaryIO::ReadType::String);
|
||||
if (version >= 34) {
|
||||
BinaryIO::ReadString<uint32_t>(file, si.skyLayerFilename, BinaryIO::ReadType::String);
|
||||
for (auto& rl : si.ringLayer) {
|
||||
BinaryIO::ReadString<uint32_t>(file, rl, BinaryIO::ReadType::String);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Level::ReadEditor(std::istream& file) {
|
||||
auto& ed = m_Environment.editor;
|
||||
uint32_t blockSize = 0;
|
||||
BinaryIO::BinaryRead(file, blockSize);
|
||||
uint32_t numColors = 0;
|
||||
BinaryIO::BinaryRead(file, numColors);
|
||||
ed.savedColors.reserve(numColors);
|
||||
for (uint32_t i = 0; i < numColors; ++i) {
|
||||
LvlEditorColor c;
|
||||
BinaryIO::BinaryRead(file, c.r);
|
||||
BinaryIO::BinaryRead(file, c.g);
|
||||
BinaryIO::BinaryRead(file, c.b);
|
||||
ed.savedColors.push_back(c);
|
||||
}
|
||||
m_Environment.hasEditor = true;
|
||||
}
|
||||
|
||||
void Level::ReadEnvironmentChunk(std::istream& file, Header& header) {
|
||||
uint32_t version = 0;
|
||||
// Find the version from the fib chunk if we've already read it
|
||||
auto fibIt = m_ChunkHeaders.find(ChunkTypeID::FileInfo);
|
||||
if (fibIt != m_ChunkHeaders.end()) {
|
||||
version = fibIt->second.fileInfo.version;
|
||||
}
|
||||
|
||||
// Environment chunk payload: 3 absolute u32 offsets
|
||||
uint32_t ofsLighting, ofsSkydome, ofsEditor;
|
||||
BinaryIO::BinaryRead(file, ofsLighting);
|
||||
BinaryIO::BinaryRead(file, ofsSkydome);
|
||||
BinaryIO::BinaryRead(file, ofsEditor);
|
||||
|
||||
if (ofsLighting > 0) {
|
||||
file.seekg(ofsLighting);
|
||||
ReadLighting(file, version);
|
||||
}
|
||||
if (ofsSkydome > 0) {
|
||||
file.seekg(ofsSkydome);
|
||||
ReadSkydome(file, version);
|
||||
}
|
||||
if (version >= 37 && ofsEditor > 0) {
|
||||
file.seekg(ofsEditor);
|
||||
ReadEditor(file);
|
||||
}
|
||||
m_HasEnvironment = true;
|
||||
}
|
||||
|
||||
void Level::ReadParticleChunk(std::istream& file, Header& header) {
|
||||
uint32_t version = 0;
|
||||
auto fibIt = m_ChunkHeaders.find(ChunkTypeID::FileInfo);
|
||||
if (fibIt != m_ChunkHeaders.end()) {
|
||||
version = fibIt->second.fileInfo.version;
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
m_Particles.reserve(count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
LvlParticle p;
|
||||
if (version >= 43) BinaryIO::BinaryRead(file, p.priority);
|
||||
BinaryIO::BinaryRead(file, p.position);
|
||||
BinaryIO::BinaryRead(file, p.rotation.w);
|
||||
BinaryIO::BinaryRead(file, p.rotation.x);
|
||||
BinaryIO::BinaryRead(file, p.rotation.y);
|
||||
BinaryIO::BinaryRead(file, p.rotation.z);
|
||||
|
||||
// effect_names: u4_wstr
|
||||
BinaryIO::ReadString<uint32_t>(file, p.effectNames, BinaryIO::ReadType::WideString);
|
||||
|
||||
// null terminator (version < 46)
|
||||
if (version < 46) {
|
||||
uint16_t null_term;
|
||||
BinaryIO::BinaryRead(file, null_term);
|
||||
}
|
||||
|
||||
// config: u4_wstr parsed as LDF
|
||||
std::string configStr;
|
||||
BinaryIO::ReadString<uint32_t>(file, configStr, BinaryIO::ReadType::WideString);
|
||||
for (const auto& token : GeneralUtils::SplitString(configStr, '\n')) {
|
||||
p.config.ParseInsert(token);
|
||||
}
|
||||
|
||||
m_Particles.push_back(std::move(p));
|
||||
}
|
||||
}
|
||||
|
||||
void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
|
||||
uint32_t objectsCount = 0;
|
||||
BinaryIO::BinaryRead(file, objectsCount);
|
||||
@@ -236,9 +343,9 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
|
||||
BinaryIO::BinaryRead(file, obj.lot);
|
||||
|
||||
if (header.fileInfo.version >= 38) {
|
||||
int32_t tmp = 1;
|
||||
BinaryIO::BinaryRead(file, tmp);
|
||||
if (tmp > -1 && tmp < 11) obj.nodeType = tmp;
|
||||
uint32_t nodeType;
|
||||
BinaryIO::BinaryRead(file, nodeType);
|
||||
obj.nodeType = (nodeType <= 15) ? nodeType : 1;
|
||||
}
|
||||
|
||||
if (header.fileInfo.version >= 32) {
|
||||
@@ -249,7 +356,29 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
|
||||
BinaryIO::BinaryRead(file, obj.rotation);
|
||||
BinaryIO::BinaryRead(file, obj.scale);
|
||||
BinaryIO::ReadString<uint32_t>(file, ldfString);
|
||||
BinaryIO::BinaryRead(file, obj.value3);
|
||||
|
||||
if (header.fileInfo.version >= 7) {
|
||||
uint32_t numRenderAttrs = 0;
|
||||
BinaryIO::BinaryRead(file, numRenderAttrs);
|
||||
if (numRenderAttrs > 0) {
|
||||
char nameBuf[64]{};
|
||||
file.read(nameBuf, 64);
|
||||
obj.renderTechnique.name.assign(nameBuf, strnlen(nameBuf, 64));
|
||||
|
||||
obj.renderTechnique.attrs.resize(numRenderAttrs);
|
||||
for (uint32_t a = 0; a < numRenderAttrs; ++a) {
|
||||
auto& attr = obj.renderTechnique.attrs[a];
|
||||
char attrName[64]{};
|
||||
file.read(attrName, 64);
|
||||
attr.name.assign(attrName, strnlen(attrName, 64));
|
||||
BinaryIO::BinaryRead(file, attr.numFloats);
|
||||
uint8_t isColor = 0;
|
||||
BinaryIO::BinaryRead(file, isColor);
|
||||
attr.isColor = isColor != 0;
|
||||
for (float& f : attr.values) BinaryIO::BinaryRead(file, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//This is a little bit of a bodge, but because the alpha client (HF) doesn't store the
|
||||
//spawn position / rotation like the later versions do, we need to check the LOT for the spawn pos & set it.
|
||||
|
||||
@@ -4,6 +4,67 @@
|
||||
#include <iostream>
|
||||
#include "Zone.h"
|
||||
|
||||
struct LvlDrawDistances {
|
||||
float fogNear{};
|
||||
float fogFar{};
|
||||
float postFogSolid{};
|
||||
float postFogFade{};
|
||||
float staticObjDistance{};
|
||||
float dynamicObjDistance{};
|
||||
};
|
||||
|
||||
struct LvlCullVal {
|
||||
uint32_t groupID{};
|
||||
float min{};
|
||||
float max{};
|
||||
};
|
||||
|
||||
struct LvlLightingInfo {
|
||||
float blendTime{};
|
||||
float ambient[3]{};
|
||||
float specular[3]{};
|
||||
float upperHemi[3]{};
|
||||
NiPoint3 position;
|
||||
bool hasDrawDistances{};
|
||||
LvlDrawDistances minDraw;
|
||||
LvlDrawDistances maxDraw;
|
||||
std::vector<LvlCullVal> cullVals;
|
||||
float fogNear{};
|
||||
float fogFar{};
|
||||
float fogColor[3]{};
|
||||
float dirLight[3]{};
|
||||
NiPoint3 startPosition;
|
||||
NiQuaternion startRotation = QuatUtils::IDENTITY;
|
||||
bool hasSpawn{};
|
||||
};
|
||||
|
||||
struct LvlSkydomeInfo {
|
||||
std::string filename;
|
||||
std::string skyLayerFilename;
|
||||
std::string ringLayer[4];
|
||||
};
|
||||
|
||||
struct LvlEditorColor { float r{}, g{}, b{}; };
|
||||
|
||||
struct LvlEditorSettings {
|
||||
std::vector<LvlEditorColor> savedColors;
|
||||
};
|
||||
|
||||
struct LvlEnvironmentData {
|
||||
LvlLightingInfo lighting;
|
||||
LvlSkydomeInfo skydome;
|
||||
LvlEditorSettings editor;
|
||||
bool hasEditor{};
|
||||
};
|
||||
|
||||
struct LvlParticle {
|
||||
uint16_t priority{};
|
||||
NiPoint3 position;
|
||||
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||
std::string effectNames;
|
||||
LwoNameValue config;
|
||||
};
|
||||
|
||||
class Level {
|
||||
public:
|
||||
enum ChunkTypeID : uint16_t {
|
||||
@@ -39,15 +100,23 @@ public:
|
||||
|
||||
public:
|
||||
Level(Zone* parentZone, const std::string& filepath);
|
||||
|
||||
|
||||
static void MakeSpawner(const SceneObject& obj);
|
||||
|
||||
std::map<uint32_t, Header> m_ChunkHeaders;
|
||||
LvlEnvironmentData m_Environment;
|
||||
bool m_HasEnvironment{};
|
||||
std::vector<LvlParticle> m_Particles;
|
||||
private:
|
||||
Zone* m_ParentZone;
|
||||
|
||||
//private functions:
|
||||
void ReadChunks(std::istream& file);
|
||||
void ReadFileInfoChunk(std::istream& file, Header& header);
|
||||
void ReadEnvironmentChunk(std::istream& file, Header& header);
|
||||
void ReadLighting(std::istream& file, uint32_t version);
|
||||
void ReadSkydome(std::istream& file, uint32_t version);
|
||||
void ReadEditor(std::istream& file);
|
||||
void ReadSceneObjectDataChunk(std::istream& file, Header& header);
|
||||
void ReadParticleChunk(std::istream& file, Header& header);
|
||||
};
|
||||
|
||||
@@ -102,9 +102,6 @@ struct Chunk {
|
||||
std::vector<uint16_t> meshVertSize;
|
||||
std::vector<MeshTri> meshTri;
|
||||
|
||||
// Unknown data for version < 32
|
||||
std::vector<uint8_t> unknown1;
|
||||
std::vector<uint8_t> unknown2;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,11 +85,29 @@ void Zone::LoadZoneIntoMemory() {
|
||||
LoadScene(file);
|
||||
}
|
||||
|
||||
//Read generic zone info:
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZonePath, BinaryIO::ReadType::String);
|
||||
// Zone boundary lines
|
||||
uint8_t numBoundaries = 0;
|
||||
BinaryIO::BinaryRead(file, numBoundaries);
|
||||
m_Boundaries.reserve(numBoundaries);
|
||||
for (uint8_t i = 0; i < numBoundaries; ++i) {
|
||||
ZoneBoundary boundary;
|
||||
BinaryIO::BinaryRead(file, boundary.normal);
|
||||
BinaryIO::BinaryRead(file, boundary.point);
|
||||
uint32_t packed;
|
||||
BinaryIO::BinaryRead(file, packed);
|
||||
boundary.destMapID = static_cast<uint16_t>(packed & 0xFFFF);
|
||||
boundary.destInstanceID = static_cast<uint16_t>((packed >> 16) & 0xFFFF);
|
||||
BinaryIO::BinaryRead(file, boundary.destSceneID);
|
||||
BinaryIO::BinaryRead(file, boundary.spawnLocation);
|
||||
m_Boundaries.push_back(boundary);
|
||||
}
|
||||
|
||||
// Zone info strings
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZoneRawPath, BinaryIO::ReadType::String);
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZoneName, BinaryIO::ReadType::String);
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZoneDesc, BinaryIO::ReadType::String);
|
||||
if (m_FileFormatVersion > Zone::FileFormatVersion::PrePreAlpha) {
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZoneName, BinaryIO::ReadType::String);
|
||||
BinaryIO::ReadString<uint8_t>(file, m_ZoneDesc, BinaryIO::ReadType::String);
|
||||
}
|
||||
|
||||
auto zoneFolderPath = m_ZoneFilePath.substr(0, m_ZoneFilePath.rfind('/') + 1);
|
||||
if (!Game::assetManager->HasFile(zoneFolderPath + m_ZoneRawPath)) {
|
||||
@@ -271,14 +289,15 @@ void Zone::LoadScene(std::istream& file) {
|
||||
}
|
||||
if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha) {
|
||||
BinaryIO::BinaryRead(file, scene.sceneType);
|
||||
lwoSceneID.SetLayerID(scene.sceneType);
|
||||
lwoSceneID.SetLayerID(static_cast<uint32_t>(scene.sceneType));
|
||||
|
||||
|
||||
BinaryIO::ReadString<uint8_t>(file, scene.name, BinaryIO::ReadType::String);
|
||||
}
|
||||
|
||||
if (m_FileFormatVersion == Zone::FileFormatVersion::LatePreAlpha) {
|
||||
BinaryIO::BinaryRead(file, scene.unknown1);
|
||||
BinaryIO::BinaryRead(file, scene.unknown2);
|
||||
BinaryIO::BinaryRead(file, scene.scenePosition);
|
||||
BinaryIO::BinaryRead(file, scene.sceneRadius);
|
||||
}
|
||||
|
||||
if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha) {
|
||||
@@ -385,11 +404,39 @@ void Zone::LoadSceneTransition(std::istream& file) {
|
||||
|
||||
SceneTransitionInfo Zone::LoadSceneTransitionInfo(std::istream& file) {
|
||||
SceneTransitionInfo info;
|
||||
BinaryIO::BinaryRead(file, info.sceneID);
|
||||
uint32_t sceneID, layerID;
|
||||
BinaryIO::BinaryRead(file, sceneID);
|
||||
BinaryIO::BinaryRead(file, layerID);
|
||||
info.sceneID = LWOSCENEID(static_cast<int32_t>(sceneID), layerID);
|
||||
BinaryIO::BinaryRead(file, info.position);
|
||||
return info;
|
||||
}
|
||||
|
||||
static void ReadLdfConfig(std::istream& file, PathWaypoint& waypoint, PathType pathType) {
|
||||
uint32_t count;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
std::string parameter;
|
||||
BinaryIO::ReadString<uint8_t>(file, parameter, BinaryIO::ReadType::WideString);
|
||||
|
||||
std::string value;
|
||||
BinaryIO::ReadString<uint8_t>(file, value, BinaryIO::ReadType::WideString);
|
||||
|
||||
if (pathType == PathType::Movement || pathType == PathType::Rail) {
|
||||
parameter.erase(std::remove_if(parameter.begin(), parameter.end(), ::isspace), parameter.end());
|
||||
auto waypointCommand = WaypointCommandType::StringToWaypointCommandType(parameter);
|
||||
if (waypointCommand == eWaypointCommandType::DELAY) value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
|
||||
if (waypointCommand != eWaypointCommandType::INVALID) {
|
||||
auto& command = waypoint.commands.emplace_back();
|
||||
command.command = waypointCommand;
|
||||
command.data = value;
|
||||
} else LOG("Tried to load invalid waypoint command '%s'", parameter.c_str());
|
||||
} else {
|
||||
waypoint.config.ParseInsert(parameter + "=" + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Zone::LoadPath(std::istream& file) {
|
||||
Path path = Path();
|
||||
|
||||
@@ -397,6 +444,34 @@ void Zone::LoadPath(std::istream& file) {
|
||||
|
||||
BinaryIO::ReadString<uint8_t>(file, path.pathName, BinaryIO::ReadType::WideString);
|
||||
|
||||
if (path.pathVersion <= 2) {
|
||||
// Legacy format: type_name string present, no behavior field,
|
||||
// unified waypoint format (pos + rot + lock + speed + wait + config).
|
||||
std::string typeName;
|
||||
BinaryIO::ReadString<uint8_t>(file, typeName, BinaryIO::ReadType::WideString);
|
||||
|
||||
BinaryIO::BinaryRead(file, path.pathType);
|
||||
BinaryIO::BinaryRead(file, path.flags);
|
||||
|
||||
BinaryIO::BinaryRead(file, path.waypointCount);
|
||||
path.pathWaypoints.reserve(path.waypointCount);
|
||||
for (uint32_t i = 0; i < path.waypointCount; ++i) {
|
||||
PathWaypoint waypoint = PathWaypoint();
|
||||
BinaryIO::BinaryRead(file, waypoint.position);
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.w);
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.x);
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.y);
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.z);
|
||||
BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer);
|
||||
BinaryIO::BinaryRead(file, waypoint.speed);
|
||||
BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait);
|
||||
ReadLdfConfig(file, waypoint, path.pathType);
|
||||
path.pathWaypoints.push_back(waypoint);
|
||||
}
|
||||
m_Paths.push_back(path);
|
||||
return;
|
||||
}
|
||||
|
||||
BinaryIO::BinaryRead(file, path.pathType);
|
||||
BinaryIO::BinaryRead(file, path.flags);
|
||||
BinaryIO::BinaryRead(file, path.pathBehavior);
|
||||
@@ -435,7 +510,6 @@ void Zone::LoadPath(std::istream& file) {
|
||||
BinaryIO::ReadString<uint8_t>(file, path.camera.nextPath, BinaryIO::ReadType::WideString);
|
||||
if (path.pathVersion >= 14) {
|
||||
BinaryIO::BinaryRead(file, path.camera.rotatePlayer);
|
||||
|
||||
}
|
||||
} else if (path.pathType == PathType::Spawner) {
|
||||
BinaryIO::BinaryRead(file, path.spawner.spawnedLOT);
|
||||
@@ -443,7 +517,9 @@ void Zone::LoadPath(std::istream& file) {
|
||||
BinaryIO::BinaryRead(file, path.spawner.maxToSpawn);
|
||||
BinaryIO::BinaryRead(file, path.spawner.amountMaintained);
|
||||
BinaryIO::BinaryRead(file, path.spawner.spawnerObjID);
|
||||
BinaryIO::BinaryRead(file, path.spawner.spawnerNetActive);
|
||||
if (path.pathVersion >= 9) {
|
||||
BinaryIO::BinaryRead(file, path.spawner.spawnerNetActive);
|
||||
}
|
||||
}
|
||||
|
||||
// Read waypoints
|
||||
@@ -457,7 +533,6 @@ void Zone::LoadPath(std::istream& file) {
|
||||
BinaryIO::BinaryRead(file, waypoint.position.y);
|
||||
BinaryIO::BinaryRead(file, waypoint.position.z);
|
||||
|
||||
|
||||
if (path.pathType == PathType::Spawner || path.pathType == PathType::MovingPlatform || path.pathType == PathType::Race || path.pathType == PathType::Camera || path.pathType == PathType::Rail) {
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.w);
|
||||
BinaryIO::BinaryRead(file, waypoint.rotation.x);
|
||||
@@ -491,37 +566,11 @@ void Zone::LoadPath(std::istream& file) {
|
||||
|
||||
// object LDF configs
|
||||
if (path.pathType == PathType::Movement || path.pathType == PathType::Spawner || path.pathType == PathType::Rail) {
|
||||
uint32_t count;
|
||||
BinaryIO::BinaryRead(file, count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
std::string parameter;
|
||||
BinaryIO::ReadString<uint8_t>(file, parameter, BinaryIO::ReadType::WideString);
|
||||
|
||||
std::string value;
|
||||
BinaryIO::ReadString<uint8_t>(file, value, BinaryIO::ReadType::WideString);
|
||||
|
||||
if (path.pathType == PathType::Movement || path.pathType == PathType::Rail) {
|
||||
// cause NetDevil puts spaces in things that don't need spaces
|
||||
parameter.erase(std::remove_if(parameter.begin(), parameter.end(), ::isspace), parameter.end());
|
||||
auto waypointCommand = WaypointCommandType::StringToWaypointCommandType(parameter);
|
||||
if (waypointCommand == eWaypointCommandType::DELAY) value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
|
||||
if (waypointCommand != eWaypointCommandType::INVALID) {
|
||||
auto& command = waypoint.commands.emplace_back();
|
||||
command.command = waypointCommand;
|
||||
command.data = value;
|
||||
} else LOG("Tried to load invalid waypoint command '%s'", parameter.c_str());
|
||||
} else {
|
||||
waypoint.config.ParseInsert(parameter + "=" + value);
|
||||
}
|
||||
|
||||
}
|
||||
ReadLdfConfig(file, waypoint, path.pathType);
|
||||
}
|
||||
|
||||
// We verify the waypoint heights against the navmesh because in many movement paths,
|
||||
// the waypoint is located near 0 height,
|
||||
if (path.pathType == PathType::Movement) {
|
||||
if (dpWorld::IsLoaded()) {
|
||||
// 2000 should be large enough for every world.
|
||||
waypoint.position.y = dpWorld::GetNavMesh()->GetHeightAtPoint(waypoint.position, 2000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,18 @@ struct WaypointCommand {
|
||||
};
|
||||
|
||||
|
||||
enum class eSceneType : uint32_t {
|
||||
General = 0,
|
||||
Audio = 1,
|
||||
};
|
||||
|
||||
struct SceneRef {
|
||||
std::string filename;
|
||||
uint32_t id{};
|
||||
uint32_t sceneType{}; //0 = general, 1 = audio?
|
||||
eSceneType sceneType{};
|
||||
std::string name;
|
||||
NiPoint3 unknown1;
|
||||
float unknown2{};
|
||||
NiPoint3 scenePosition; // version 33 only: editor bounding sphere center
|
||||
float sceneRadius{}; // version 33 only: editor bounding sphere radius
|
||||
uint8_t color_r{};
|
||||
uint8_t color_g{};
|
||||
uint8_t color_b{};
|
||||
@@ -36,8 +41,17 @@ struct SceneRef {
|
||||
std::map<uint32_t, LUTriggers::Trigger*> triggers;
|
||||
};
|
||||
|
||||
struct ZoneBoundary {
|
||||
NiPoint3 normal;
|
||||
NiPoint3 point;
|
||||
uint16_t destMapID{};
|
||||
uint16_t destInstanceID{};
|
||||
uint32_t destSceneID{};
|
||||
NiPoint3 spawnLocation;
|
||||
};
|
||||
|
||||
struct SceneTransitionInfo {
|
||||
uint64_t sceneID{}; //id of the scene being transitioned to.
|
||||
LWOSCENEID sceneID;
|
||||
NiPoint3 position;
|
||||
};
|
||||
|
||||
@@ -232,6 +246,7 @@ public:
|
||||
const Raw::Raw& GetZoneRaw() const { return m_Raw; }
|
||||
const Raw::TerrainMesh& GetTerrainMesh() const { return m_TerrainMesh; }
|
||||
const SceneRef* GetScene(LWOSCENEID sceneID) const;
|
||||
const std::vector<ZoneBoundary>& GetBoundaries() const { return m_Boundaries; }
|
||||
const std::vector<SceneTransition>& GetSceneTransitions() const { return m_SceneTransitions; }
|
||||
const std::map<LWOSCENEID, SceneRef>& GetScenes() const { return m_Scenes; }
|
||||
|
||||
@@ -255,6 +270,7 @@ private:
|
||||
Raw::TerrainMesh m_TerrainMesh; // Pre-generated terrain mesh for fast scene lookups
|
||||
|
||||
std::map<LWOSCENEID, SceneRef> m_Scenes;
|
||||
std::vector<ZoneBoundary> m_Boundaries;
|
||||
std::vector<SceneTransition> m_SceneTransitions;
|
||||
|
||||
uint32_t m_PathDataLength;
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
#include "NiQuaternion.h"
|
||||
#include "LDFFormat.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
struct RenderAttr {
|
||||
std::string name;
|
||||
uint32_t numFloats{};
|
||||
bool isColor{};
|
||||
std::array<float, 4> values{};
|
||||
};
|
||||
|
||||
struct RenderTechnique {
|
||||
std::string name;
|
||||
std::vector<RenderAttr> attrs;
|
||||
};
|
||||
|
||||
struct SceneObject {
|
||||
LWOOBJID id;
|
||||
@@ -13,7 +27,7 @@ struct SceneObject {
|
||||
NiPoint3 position;
|
||||
NiQuaternion rotation = QuatUtils::IDENTITY;
|
||||
float scale = 1.0f;
|
||||
uint32_t value3;
|
||||
RenderTechnique renderTechnique;
|
||||
LwoNameValue settings;
|
||||
};
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ void dZoneManager::BuildSceneGraph() {
|
||||
// Initialize adjacency list with all scenes
|
||||
const auto& scenes = m_pZone->GetScenes();
|
||||
for (const auto& [sceneID, sceneRef] : scenes) {
|
||||
// Ensure every scene has an entry, even if it has no transitions
|
||||
if (sceneRef.sceneType != eSceneType::General) continue;
|
||||
m_SceneAdjacencyList.try_emplace(sceneID, std::vector<LWOSCENEID>());
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ void dZoneManager::BuildSceneGraph() {
|
||||
// Group transition points by their scene IDs to find unique connections
|
||||
std::set<LWOSCENEID> connectedScenes;
|
||||
for (const auto& point : transition.points) {
|
||||
if (point.sceneID != LWOSCENEID_INVALID) {
|
||||
if (point.sceneID != LWOSCENEID_INVALID && m_SceneAdjacencyList.contains(point.sceneID)) {
|
||||
connectedScenes.insert(point.sceneID);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user