diff --git a/dZoneManager/Level.cpp b/dZoneManager/Level.cpp index 72fbdcdd..d405a1c8 100644 --- a/dZoneManager/Level.cpp +++ b/dZoneManager/Level.cpp @@ -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(file, si.filename, BinaryIO::ReadType::String); + if (version >= 34) { + BinaryIO::ReadString(file, si.skyLayerFilename, BinaryIO::ReadType::String); + for (auto& rl : si.ringLayer) { + BinaryIO::ReadString(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(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(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(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. diff --git a/dZoneManager/Level.h b/dZoneManager/Level.h index 22860fe7..21ee0409 100644 --- a/dZoneManager/Level.h +++ b/dZoneManager/Level.h @@ -4,6 +4,67 @@ #include #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 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 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 m_ChunkHeaders; + LvlEnvironmentData m_Environment; + bool m_HasEnvironment{}; + std::vector 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); }; diff --git a/dZoneManager/Raw.h b/dZoneManager/Raw.h index b6d1c64c..593d4e00 100644 --- a/dZoneManager/Raw.h +++ b/dZoneManager/Raw.h @@ -102,9 +102,6 @@ struct Chunk { std::vector meshVertSize; std::vector meshTri; - // Unknown data for version < 32 - std::vector unknown1; - std::vector unknown2; }; /** diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 94e59005..d0a3dd30 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -85,11 +85,29 @@ void Zone::LoadZoneIntoMemory() { LoadScene(file); } - //Read generic zone info: - BinaryIO::ReadString(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(packed & 0xFFFF); + boundary.destInstanceID = static_cast((packed >> 16) & 0xFFFF); + BinaryIO::BinaryRead(file, boundary.destSceneID); + BinaryIO::BinaryRead(file, boundary.spawnLocation); + m_Boundaries.push_back(boundary); + } + + // Zone info strings BinaryIO::ReadString(file, m_ZoneRawPath, BinaryIO::ReadType::String); - BinaryIO::ReadString(file, m_ZoneName, BinaryIO::ReadType::String); - BinaryIO::ReadString(file, m_ZoneDesc, BinaryIO::ReadType::String); + if (m_FileFormatVersion > Zone::FileFormatVersion::PrePreAlpha) { + BinaryIO::ReadString(file, m_ZoneName, BinaryIO::ReadType::String); + BinaryIO::ReadString(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(scene.sceneType)); + BinaryIO::ReadString(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(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(file, parameter, BinaryIO::ReadType::WideString); + + std::string value; + BinaryIO::ReadString(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(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(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(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(file, parameter, BinaryIO::ReadType::WideString); - - std::string value; - BinaryIO::ReadString(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); } } diff --git a/dZoneManager/Zone.h b/dZoneManager/Zone.h index 5f1a8d64..a1177943 100644 --- a/dZoneManager/Zone.h +++ b/dZoneManager/Zone.h @@ -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 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& GetBoundaries() const { return m_Boundaries; } const std::vector& GetSceneTransitions() const { return m_SceneTransitions; } const std::map& GetScenes() const { return m_Scenes; } @@ -255,6 +270,7 @@ private: Raw::TerrainMesh m_TerrainMesh; // Pre-generated terrain mesh for fast scene lookups std::map m_Scenes; + std::vector m_Boundaries; std::vector m_SceneTransitions; uint32_t m_PathDataLength; diff --git a/dZoneManager/dZMCommon.h b/dZoneManager/dZMCommon.h index 53f1d3c4..2e9c5d08 100644 --- a/dZoneManager/dZMCommon.h +++ b/dZoneManager/dZMCommon.h @@ -4,6 +4,20 @@ #include "NiQuaternion.h" #include "LDFFormat.h" #include +#include +#include + +struct RenderAttr { + std::string name; + uint32_t numFloats{}; + bool isColor{}; + std::array values{}; +}; + +struct RenderTechnique { + std::string name; + std::vector 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; }; diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 6e847470..9e029b42 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -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()); } @@ -381,7 +381,7 @@ void dZoneManager::BuildSceneGraph() { // Group transition points by their scene IDs to find unique connections std::set 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); } }