地图数据、IMAP/ITYP 装载与 LOD 层次
本页解释 GTAV 的静态世界是如何从磁盘数据变成场景中的实体的:关卡启动时 CFileLoader 的装载顺序、RPF 清单(_manifest)中的 IMAP↔ITYP 依赖与 IMAP 组、游戏侧对 fwMapData/fwMapTypes 的扩展(CMapData/CMapDataContents/CMapTypes)、运行时 IMAP 的按位置流式与剔除(CIplCullBox、CManagedImapGroupMgr、CMapChangeMgr、CInstancePriority),以及 LOD 层次(HD→LOD→SLOD1..4)的距离、缩放与淡入淡出。最后概述 DLC 内容通过 CDataFileMgr 变更集(content change set)改变地图的机制。
背景与相关页面#
- IMAP(
.imap,map data)是实体实例的集合;ITYP(.ityp,map types)是 archetype(模型信息)定义的集合。两者的存储(fwMapDataStore、fwMapTypesStore)、fwBoxStreamer盒子流式、LOD 树(fwLodData、fwLodAttach)位于框架层,见../04-rage-framework/scene-graph-and-visibility.md与../04-rage-framework/streaming-engine.md。本页聚焦游戏侧扩展与策略。 - IMAP 实例化出的实体类型、
CGameWorld::Add/PostAdd见world-and-entities.md;archetype/模型信息见model-info.md;内景 IMAP(CMloInstanceDef)见interiors-and-portals.md;运行时的场景流式(按可见性请求模型)见game-streaming-and-scene-streamer.md。 - RPF/packfile、
fiDevice见../02-rage-core/file-system-and-rpf.md;parser/PSO 见../02-rage-core/parser-and-metadata.md。
相关源码文件#
| 文件 | 内容 |
|---|---|
game/scene/scene.cpp | CScene::LoadMap:关卡装载入口 |
game/scene/FileLoader.h/.cpp | CFileLoader:RPF 注册、ITYP/元数据/IPL 装载顺序、cacheloader、内容变更集执行 |
game/scene/DataFileMgr.h/.cpp/.psc | CDataFileMgr:数据文件清单、DataFileType、ContentChangeSet |
game/scene/loader/MapData.h/.cpp/.psc | CMapData : fwMapData、CMapDataContents : fwMapDataContents |
game/scene/loader/MapData_*.psc | CEntityDef、CMloInstanceDef、扩展定义(2dEffect)、车生成器、时间周期修改器、LOD 灯 |
game/scene/loader/mapTypes.h/.cpp、MapTypes.psc | CMapTypes : fwMapTypes、复合实体类型 |
game/scene/loader/mapFileMgr.h/.cpp、PackFileMetaData.psc | CMapFileMgr:RPF _manifest 解析、依赖装配 |
game/scene/loader/ManagedImapGroup.* | 时间/天气驱动的 IMAP 组 |
game/scene/ipl/IplCullBox.* | IMAP 剔除盒 |
game/scene/MapChange.* | 模型替换/隐藏的持久地图变更 |
game/scene/InstancePriority.* | 实例优先级上限(随玩法降级 IMAP 实体) |
game/scene/lod/LodMgr.*、LodScale.*、LodDrawable.* | LOD 淡入淡出、LOD 距离缩放、drawable 内部 LOD 选择 |
game/scene/texLod.* | HD 贴图字典切换(CTexLod、CMipSwitcher) |
game/scene/ExtraContent.*、MountableContent.*、ExtraContentDefs.h | DLC 挂载与变更集分组 |
game/streaming/zonedassetmanager.*、IslandHopper.* | 区域常驻模型、岛屿切换 |
1. 关卡装载流程#
CGame 在 INIT_BEFORE_MAP_LOADED 之后调用 CScene::LoadMap(level),随即 CModelIndex::MatchAllModelStrings() src/dev_ng/game/Core/game.cpp:1335-1362。CScene::LoadMap(const char*) 的步骤 src/dev_ng/game/scene/scene.cpp:727-782:
fwScene::LoadMap,DATAFILEMGR.RecallSnapshot();- 若存在 title update 镜像
TITLEUPDATE_IMG,以 extra-content 索引0xff注册到strPackfileManager; DATAFILEMGR.Load(pFile)读入关卡的数据文件清单;CPathServer::ReadIndexMappingFiles()(必须在流式模块注册之前);CFileLoader::LoadLevelInstanceInfo(pFile)——主装载序列;CTrafficLights::Init、远景灯g_distantLights.LoadData()、CWorldPoints::SortWorld2dEffects()。
CFileLoader::LoadLevelInstanceInfo src/dev_ng/game/scene/FileLoader.cpp:2209-2416 的顺序是理解启动依赖的关键:
证据:RPF 阶段 src/dev_ng/game/scene/FileLoader.cpp:2221-2243、src/dev_ng/game/scene/FileLoader.cpp:1332-1346;常驻 ITYP 请求 src/dev_ng/game/scene/FileLoader.cpp:1908-1942;各种数据文件 src/dev_ng/game/scene/FileLoader.cpp:2252-2314;ITYP 与依赖 src/dev_ng/game/scene/FileLoader.cpp:2316-2330;元数据 src/dev_ng/game/scene/FileLoader.cpp:2332-2347;cache/IPL src/dev_ng/game/scene/FileLoader.cpp:2361-2385;收尾 src/dev_ng/game/scene/FileLoader.cpp:2391-2413;CacheLoadStart/End、PostProcessIplLoad src/dev_ng/game/scene/FileLoader.cpp:2110-2203。
要点:
- ITYP 依赖顺序:注释写明 ped 引用胶囊、组件集、任务数据等,所以这些文件必须在 ITYP/ped 元数据之前装载
src/dev_ng/game/scene/FileLoader.cpp:2252-2294。 - cacheloader:
strCacheLoader把 RPF 扫描与 IMAP/静态包围盒注册结果缓存到文件,CacheLoadEnd在 IPL 注册之后保存;它也负责“GTAV 专用 hack”——查找dt1_emissive与ch2_03槽位作为重要自发光 IMAPsrc/dev_ng/game/scene/FileLoader.cpp:2140-2178。 - 旧式文本 IPL:
LoadIplFiles遍历IPL_FILE数据文件,同名(不同设备)的只加载第一份;LoadScene若发现同名二进制 placement 文件存在就直接返回,否则逐行解析inst/mzon/cull/occl/grge/...等段落(如 maparea、popzone、cullzone)src/dev_ng/game/scene/FileLoader.cpp:1432-1478、src/dev_ng/game/scene/FileLoader.cpp:2742-2800。这表明文本 IPL 只承担少量全局数据(如maparea.ipl的城市/乡村区域,见第 6 节),实体实例主要来自流式 IMAP(推断,依据是 IMAP 由fwMapDataStore流式装载,而文本路径在有二进制文件时被跳过)。
1.1 CDataFileMgr:数据文件清单#
CDataFileMgr::DataFileType 枚举了约 200 种数据文件类型(RPF_FILE、IPL_FILE、PERMANENT_ITYP_FILE、PED_METADATA_FILE、VEHICLE_METADATA_FILE、DOOR_TUNING_FILE、IPLCULLBOX_METAFILE、DLC_ITYP_REQUEST、ZONED_ASSET_FILE、ISLAND_HOPPER_FILE 等)src/dev_ng/game/scene/DataFileMgr.h:55-271。每个条目 DataFile 记录文件名、类型、m_contents(PROPS/MAP/LODS/PEDS/…/DLC_MAP_DATA)、安装分区、是否禁用/持久/overlay/patch 等 src/dev_ng/game/scene/DataFileMgr.h:273-324。各子系统用 GetFirstFile(type)/GetNextFile 遍历本类型文件(例如 LoadVehicleMetaData)src/dev_ng/game/scene/FileLoader.cpp:1397-1420。DLC 可通过 CDataFileMount::RegisterMountInterface(type, iface, unloadBehaviour) 为某类型注册运行时加载/卸载回调 src/dev_ng/game/scene/DataFileMgr.h:531-560(如 ped 元数据的 CPedModelMetaDataFileMounter src/dev_ng/game/modelinfo/PedModelInfo.cpp:128-145)。
流式文件本身也是数据驱动的:CStreaming::LoadStreamingMetaFile 读取 platformcrc:/data/streaming,RegisterStreamingFiles 把 STREAMING_FILE 与平台专用 STREAMING_FILE_PLATFORM_* 条目逐个 strPackfileManager::RegisterIndividualFile src/dev_ng/game/streaming/streaming.cpp:405、src/dev_ng/game/streaming/streaming.cpp:518-571。
2. RPF 清单与 IMAP↔ITYP 依赖(CMapFileMgr)#
每个 RPF 挂载后,流式引擎回调游戏接口 CGtaStreamingInterface::PostLoadRpf → CStreaming::PostLoadRpf:把 packfile 临时挂到 localPack:/,交给 CMapFileMgr::LoadPackFileMetaData 读 _manifest 后卸载 src/dev_ng/game/streaming/streaming.cpp:181、src/dev_ng/game/streaming/streaming.cpp:573-581。清单是 PSO 格式的 CPackFileMetaData,以 LOAD_IN_PLACE 方式装载;若有 cacheloader 缓存则直接读缓存 blob src/dev_ng/game/scene/loader/mapFileMgr.cpp:243-302。
CPackFileMetaData 的字段(PackFileMetaData.psc):
| 字段 | 结构 | 用途(由 LoadPackFileMetaDataCommon 处理) | 证据 |
|---|---|---|---|
m_imapDependencies | CImapDependency{imap, ityp, packFile} | 旧格式一对一依赖,记入 m_MapDependencies | src/dev_ng/game/scene/loader/mapFileMgr.cpp:310-323 |
m_imapDependencies_2 | CImapDependencies{imap, manifestFlags, ityp[]} | IMAP→多个 ITYP;INTERIOR_DATA 标志标记 MLO 实例 | src/dev_ng/game/scene/loader/mapFileMgr.cpp:324-349 |
m_itypDependencies_2 | CItypDependencies | ITYP→ITYP 依赖 | src/dev_ng/game/scene/loader/mapFileMgr.cpp:351-375 |
m_Interiors | CInteriorBoundsFiles{name, bounds[1..2]} | 内景的 mover/weapon 包围盒;两者在静态包围盒 box streamer 中被设为忽略(由内景自己请求),weapon 包围盒名存入 m_interiorBounds | src/dev_ng/game/scene/loader/mapFileMgr.cpp:378-425 |
m_MapDataGroups | CMapDataGroup{name, bounds[], flags(TIME/WEATHER_DEPENDENT), weatherTypes[], hoursOnOff} | IMAP 组:注册组的静态包围盒列表;时间/天气相关的交给 CManagedImapGroupMgr | src/dev_ng/game/scene/loader/mapFileMgr.cpp:462-476 |
m_HDTxdBindingArray | CHDTxdAssetBinding{type(TXD/DRB/DWD/FRG), target, HDTxd} | 资产→HD 贴图字典映射,交给 CTexLod::StoreHDMapping | src/dev_ng/game/scene/loader/mapFileMgr.cpp:478-520 |
清单处理中有一个针对“heist island”地形的硬编码 hack:当首个组名为 H4_islandx_terrain_03 时,往首组的包围盒列表里追加 36 个水下碰撞包围盒 src/dev_ng/game/scene/loader/mapFileMgr.cpp:428-460。
CMapFileMgr::SetMapFileDependencies 把累积的依赖写入框架定义:对每个 IMAP 找到 fwMapDataDef,INTERIOR_DATA 时 SetIsMLOInstanceDef(true);对每个非常驻(非 permanent / permanentDLC)ITYP 调用 PushParentTypeDef(容量满则断言“Too many dependencies”),并标记 ITYP 为依赖项;ITYP→ITYP 同理 src/dev_ng/game/scene/loader/mapFileMgr.cpp:603-722。它在会话初始化、关卡装载、DLC 地图变更后都会被调用 src/dev_ng/game/streaming/streaming.cpp:447-456、src/dev_ng/game/scene/FileLoader.cpp:2322、src/dev_ng/game/scene/MountableContent.cpp:611。
为什么重要:实体实例化时框架会校验 archetype 所在 ITYP 是否为该 IMAP 的依赖(或 permanent DLC);不是则警告并跳过该实体 src/dev_ng/rage/framework/src/fwscene/mapdata/mapdata.cpp:100-127。依赖关系也让流式引擎在请求 IMAP 时先拉 ITYP。
3. 游戏侧 IMAP/ITYP 扩展#
3.1 CMapData 与 CMapDataContents#
CMapData : fwMapData 是 IMAP 文件根对象,在框架字段之外增加时间周期修改器盒、车生成器、LOD 灯与远景 LOD 灯(SoA 布局)、导出信息块 CBlockDesc;CreateMapDataContents() 工厂返回 CMapDataContents src/dev_ng/game/scene/loader/MapData.h:79-99、src/dev_ng/game/scene/loader/MapData.psc:16-30。实体定义 CEntityDef : fwEntityDef 只多了 AO 乘数、人造 AO 和色调值(m_tintValue 0..7)src/dev_ng/game/scene/loader/MapData_Entities.psc:10-14。
CMapDataContents 是“一个已装载 IMAP 的运行时内容” src/dev_ng/game/scene/loader/MapData.h:27-63:
| 操作 | 实现 | 效果 | 证据 |
|---|---|---|---|
| 装载 | Load(pDef, pMapData, slot) | Entities_Create(框架,含 LOD 挂接)→ 应用 g_MapChangeMgr 地图变更 → Entities_AddToWorld(可推迟/可剔除)→ Entities_PostAdd(写 m_iplIndex,HD IMAP 才跑 CGameWorld::PostAdd)→ 车生成器、时间周期盒、遮挡体、LOD 灯、远景 LOD 灯 | src/dev_ng/game/scene/loader/MapData.cpp:125-172、src/dev_ng/game/scene/loader/MapData.cpp:245-262 |
| 卸载 | Remove(slot) | 先处理由该 IMAP dummy 升级出的 CObject(恢复 dummy、删对象)→ 移出世界 → HD IMAP 才 Entities_PostRemove(批量移除场景点,单次最多 512)→ 销毁实体 → 拆除车生成器/时间周期/遮挡体/LOD 灯 | src/dev_ng/game/scene/loader/MapData.cpp:178-239、src/dev_ng/game/scene/loader/MapData.cpp:268-306 |
| 内景代理 | ConstructInteriorProxies | 对 CMloInstanceDef 实体创建/覆盖 CInteriorProxy(要求 IMAP 有 ITYP 依赖) | src/dev_ng/game/scene/loader/MapData.cpp:534-568 |
-simplemapdata 参数让工具(高度图/导航网格)只创建实体,跳过车生成器等其余内容 src/dev_ng/game/scene/loader/MapData.cpp:119、src/dev_ng/game/scene/loader/MapData.cpp:149-152。__BANK 下 CScene::Init 注册 g_MapDataGameInterface 以支持缓存块信息 src/dev_ng/game/scene/scene.cpp:615-617。
IMAP 中可出现的扩展定义(CExtensionDef*,即 2dEffect 的数据形态)包括音频发射器、浮力、爆炸、粒子、贴花、风扰动、灯光效果、光柱、滚动条、出生点、摇摆、程序化对象、红绿灯(WalkDontWalk)、脚本、梯子、音频碰撞、攀爬把手、表情、门、灯光、出生点覆盖等 src/dev_ng/game/scene/loader/MapData_Extensions.psc:14-409。哪些扩展挂在 archetype、哪些挂在实体上,由 CModelInfo::Init 的关联注册决定,见 model-info.md。
3.2 CMapTypes(ITYP)#
CMapTypes : fwMapTypes 额外携带 m_compositeEntityTypes(Rayfire 复合实体类型)src/dev_ng/game/scene/loader/mapTypes.h:166-187:
PreConstruct:若本文件 archetype 数 + 已注册数超过fwArchetypeManager::GetMaxArchetypeIndex(),触发“archetype 紧急情况”——转储流式兴趣、断言、g_MapDataStore.RemoveUnrequired(true)、刷渲染线程、卸载无用 ITYP,再次超限则断言“Crash”src/dev_ng/game/scene/loader/mapTypes.cpp:171-199。AllocArchetypeStorage:按 archetype 定义的 parser 结构名哈希(ms_archetypeDefAssociations)统计每种模型信息工厂与 2dEffect 工厂需要的存储块src/dev_ng/game/scene/loader/mapTypes.cpp:217-250。ConstructLocalExtensions:为 archetype 上的每个扩展定义,按ms_extensionDefAssociations找到 2dEffect 工厂创建C2dEffect并挂到CBaseModelInfosrc/dev_ng/game/scene/loader/mapTypes.cpp:30-119。PostConstruct:构造复合实体模型信息(CModelInfo::AddCompEntityModel,要求开始/结束 IMAP 要么都有要么都无),对非常驻 ITYP 的每个 archetype 跑CModelInfo::PostLoadsrc/dev_ng/game/scene/loader/mapTypes.cpp:126-213。
4. 运行时 IMAP 流式与过滤#
每帧 CStreaming::Update 在玩家已定位时以焦点位置(CFocusEntityMgr)驱动 IMAP 流式 src/dev_ng/game/streaming/streaming.cpp:668-727:
- 焦点位移超过 300m 时标记需要刷新渲染线程(避免 ITYP 大量重叠;仅在不渲染时执行刷新)
src/dev_ng/game/streaming/streaming.cpp:672-676、src/dev_ng/game/streaming/streaming.cpp:710-715。 CInstancePriority::Update()。- 高速时关闭 HD IMAP:玩家高速飞行/坠落(
g_ContinuityMgr)或脚本本帧要求抑制时,INSTANCE_STORE.SetHdStreaming(false);PC 可由设置强制开启src/dev_ng/game/streaming/streaming.cpp:682-691。 CIplCullBox::Update→CZonedAssetManager::Update→CManagedImapGroupMgr::Update→INSTANCE_STORE.GetBoxStreamer().RequestAboutPos(pos, 淡出时 PRIORITY_LOAD)→INSTANCE_STORE.Update()→g_MapTypesStore.Update()src/dev_ng/game/streaming/streaming.cpp:694-717。- 元数据 box streamer 以“人口位置”为中心请求(飞行/空中时 Z 向扩大 ±100m),用于场景点区域等
src/dev_ng/game/streaming/streaming.cpp:740-777。
box streamer 还会合并游戏额外提交的搜索(脚本实体碰撞、流式体积、玩家切换、SRL、网络、PC 上的 IMAP 膨胀半径),见 game-streaming-and-scene-streamer.md。
4.1 IMAP 剔除盒(scene/ipl)#
CIplCullBox 从 platform:/data/mapdatacullboxes(及 DLC 的 IPLCULLBOX_METAFILE)读取 CIplCullBoxFile:每个条目有名字、AABB、被剔除的“容器名哈希”列表与启用标志 src/dev_ng/game/scene/ipl/IplCullBox.cpp:27、src/dev_ng/game/scene/ipl/IplCullBox.psc:5-18。运行时每个条目维护一张按 IMAP 槽位的位图 CIplCullBoxCullList(每 32 槽一个 u32),GenerateCullList 时还会递归加入被剔除 IMAP 的相关槽位(据私有函数名 RecursivelyAddCulledDeps 推断,未逐行核对) src/dev_ng/game/scene/ipl/IplCullBox.h:32-87。
Update(playerPos):脚本可一键 ms_bCullEverything;否则对每个盒子判断是否包含测试点,激活新进入的盒子、停用离开的盒子;只要有盒子停用,就对所有仍需要的盒子重新应用剔除标志(推断:停用会清除可能与其他盒子重叠的剔除标志,因此需要重建)src/dev_ng/game/scene/ipl/IplCullBox.cpp:113-203。RegisterContainers 仅把“含实体的父 IMAP 或实例列表 IMAP”按名字注册为可剔除容器(注释:美术按容器名指代地图分块)src/dev_ng/game/scene/ipl/IplCullBox.cpp:209-230。IplCullBoxEditor 是 __BANK 编辑器。
4.2 时间/天气驱动的 IMAP 组#
一般 IMAP 组由脚本或 Rayfire 开关;CManagedImapGroup 处理清单中带 TIME_DEPENDENT/WEATHER_DEPENDENT 的组 src/dev_ng/game/scene/loader/ManagedImapGroup.h:20-56:
IsRequired:时间条件为m_hoursOnOff的当前小时位;天气条件为当前插值天气(插值 <0.5 取前一天气)是否在列表中;两者都设时需同时满足src/dev_ng/game/scene/loader/ManagedImapGroup.cpp:108-142。SwapStateIfSafe:若焦点在该组的流式包围盒内、游戏正在渲染、且实体包围球在游戏视口可见,则不切换,避免可见跳变;ch1_02_fruitstall被硬编码为只在超出范围后移除src/dev_ng/game/scene/loader/ManagedImapGroup.cpp:45-102。- 切换本身调用
fwMapDataStore::RequestGroup/RemoveGroupsrc/dev_ng/game/scene/loader/ManagedImapGroup.cpp:58-69。回放控制世界时不更新src/dev_ng/game/scene/loader/ManagedImapGroup.cpp:183-198。
脚本侧的 IMAP 组“交换”(fwImapGroupMgr,最多 MAX_CONCURRENT_SWAPS 个并发)由场景流式检查完成度:交换 IMAP 已加载且所有(非复合)实体模型都已加载才置为完成 src/dev_ng/game/scene/streamer/SceneStreamerMgr.cpp:571-598。
4.3 持久地图变更(CMapChangeMgr)#
CMapChange 描述一条在某球体范围内对某模型的持久修改:TYPE_SWAP(换模型)、TYPE_HIDE(隐藏)、TYPE_FORCEOBJ(强制成为真实对象而非 dummy),可标记为脚本拥有、是否跨地图重载保留、是否影响脚本对象 src/dev_ng/game/scene/MapChange.h:15-72。管理器在 IMAP 装载时 ApplyChangesToMapData(slot)(在 CMapDataContents::Load 中实体创建后、入世界前调用)、在单个实体上 ApplyChangesToEntity,并可按脚本线程整体移除 src/dev_ng/game/scene/MapChange.h:74-110、src/dev_ng/game/scene/loader/MapData.cpp:136-138。内景实体生成时也检查包围球是否与活动变更相交 src/dev_ng/game/scene/portals/InteriorInst.cpp:587-591。
4.4 实例优先级(CInstancePriority)#
IMAP 中的实体定义带优先级;fwMapData::SetEntityPriorityCap 决定低于上限的实体是否实例化。游戏侧依据玩法降级 src/dev_ng/game/scene/InstancePriority.cpp:86-173:
| 条件 | 上限 |
|---|---|
单机:脚本提示 DRIVING/SNIPING/HELICAM、通缉 ≥5 星、最近高速飞行(HIGH_SPEED_FLIGHT_TIMEOUT=8000 ms) | PRIORITY_LOWEST |
| 单机:脚本提示 SHOOTING 或通缉 >3 星 | PRIORITY_LOW |
| 单机:其他 | PRIORITY_NORMAL |
| 多人 | PRIORITY_LOWEST(!__FINAL 可用 -instancepriority 覆盖) |
PC:强制最低或 -useMinimumSettings | PRIORITY_LOWEST |
| 导航网格导出 | PRIORITY_NORMAL |
切换到多人模式时还会立即 FlushHdMapEntities src/dev_ng/game/scene/InstancePriority.cpp:70-79、src/dev_ng/game/scene/InstancePriority.cpp:26。__BANK 中 CEntity::GetIsUsedInMP() 用同一上限判断某实体是否在 MP 中出现 src/dev_ng/game/scene/Entity.h:700-704。
4.5 区域常驻模型与岛屿切换#
CZonedAssetManager:数据CZonedAssets{name, extents AABB, models[]};焦点移动超过重算距离后,清空旧请求,并为所有包含焦点的区域请求其模型列表src/dev_ng/game/streaming/zonedassets.psc:9-16、src/dev_ng/game/streaming/zonedassetmanager.cpp:195-235。CIslandHopper:数据CIslandHopperData{Name, Cullbox, HeightMap, LodLights, IPLsToEnable[], IMAPsToPreempt[]};RequestToggleIsland后在CStreaming::Update开头处理,激活/停用岛屿时切换 IPL、剔除盒、高度图、LOD 灯等src/dev_ng/game/streaming/islandhopperdata.psc:4-15、src/dev_ng/game/streaming/IslandHopper.h:17-80、src/dev_ng/game/streaming/streaming.cpp:593。该系统随后期 DLC(“HeistIsland”)加入,门户更新中也有对应 hack(见内景页)。
5. LOD 层次#
5.1 LOD 级别与 IMAP 资产类型#
框架定义 7 个 LOD 深度:HD(0)、LOD、SLOD1、SLOD2、SLOD3、ORPHANHD(无父 LOD 的 HD)、SLOD4 src/dev_ng/rage/framework/src/fwscene/lod/LodTypes.h:37-61。实体之间通过 fwLodData 形成父子树(HD 的父是 LOD,依次向上);IMAP 按 LOD 级别拆分成不同“资产类型”,CLodMgr 的表把 LOD 级别映射到 box streamer 的 IMAP 资产掩码:HD/ORPHANHD→MASK_MAPDATA_HIGH,LOD/SLOD1→FLAG_MAPDATA_MEDIUM,SLOD2/3/4→FLAG_MAPDATA_LOW src/dev_ng/game/scene/lod/LodMgr.cpp:30-59。玩家切换在天花板高度只请求某 LOD 级别对应的 IMAP 类型,就是利用这张表(见流式页)。
5.2 LOD 距离与缩放(CLodScale)#
模型的有效 LOD 距离 = 未缩放距离 × 全局缩放(CBaseModelInfo::GetLodDistance)src/dev_ng/game/modelinfo/BaseModelInfo.h:325。CLodScale 每帧在 CGameScan::SetupScan 中更新(随后 g_ContinuityMgr.Update、g_LodMgr.PreUpdate、球形流式半径更新)src/dev_ng/game/scene/world/GameScan.cpp:130-154。输入与输出 src/dev_ng/game/scene/lod/LodScale.h:17-102:
- 根缩放
m_fRoot_In:PC 来自图形设置CalcFinalLodRootScale(),主机为 1.0;-globalLOD可覆盖(钳制 0.5..10)src/dev_ng/game/scene/lod/LodScale.cpp:58-104。SRL 回放期间根缩放被限制为 ≤1,避免平台差异使过场 SRL 失效src/dev_ng/game/scene/lod/LodScale.cpp:145-150。 - 相机缩放:GBuffer 渲染相位按 FOV 计算的
ComputeLodDistanceScale(),乘以根缩放得到全局缩放src/dev_ng/game/scene/lod/LodScale.cpp:156-164。 - 时间周期:
LodMult与各 LOD 级别缩放(HD/OrphanHD/LOD/SLOD1..4);若LodMult < 0.2视为时间周期数据异常而忽略src/dev_ng/game/scene/lod/LodScale.cpp:170-185。 - 草:第一人称瞄准时使用狙击草缩放,乘以草质量缩放
src/dev_ng/game/scene/lod/LodScale.cpp:191-194。 - 覆盖:脚本可设置全局缩放或重映射,结束后按
m_blendOutDuration=10渐出src/dev_ng/game/scene/lod/LodScale.cpp:93-95、src/dev_ng/game/scene/lod/LodScale.cpp:111-138。 - 线程:
GetGlobalScale()断言只在更新线程调用;渲染线程用单独的m_fGlobalRT_Outsrc/dev_ng/game/scene/lod/LodScale.h:25-29。
5.3 LOD 淡入淡出(CLodMgr + 后扫描)#
CLodMgr::PreUpdate 在发生重大镜头变化时禁用基于时间的淡入;脚本 ScriptRequestRenderHdOnly 会启用只渲染 HD/ORPHANHD 的 “SLOD 模式” 一帧 src/dev_ng/game/scene/lod/LodMgr.cpp:95-131。只有 HD/ORPHANHD(或根 LOD)允许随时间淡入;当根缩放 ≤1 且玩家在高度图之上驾驶飞行器时所有级别都允许 src/dev_ng/game/scene/lod/LodMgr.cpp:157-174。各级淡变距离默认 LODTYPES_FADE_DISTF=20m,可由 visual settings lod.fadedist.* 覆盖 src/dev_ng/game/scene/lod/LodMgr.cpp:74-89、src/dev_ng/rage/framework/src/fwscene/lod/LodTypes.h:23-34。
每帧可见性扫描后,CPostScan 对 PVS 做两遍 alpha 更新(LOD 树的“交叉淡变”):
- 第一遍(按 LOD 级别排序后):重置子 alpha;刚创建 drawable 的实体 alpha 置 0;
GetAlphaPt1_FadeUp计算新 alpha——时间淡入每帧+LODTYPES_ALPHA_STEP(32)(PC/新主机按TimeStep/33ms比例),脚本“淡到 0”每帧-16;把结果累加到父 LOD 的ChildAlphasrc/dev_ng/game/renderer/PostScan.cpp:960-990、src/dev_ng/game/scene/lod/LodMgr.h:136-171。 - 第二遍:子实体继承父的 child alpha;有子节点且子节点全部挂接时,
UpdateAlphaPt2_FadeDownRelativeToChildren在“子 LOD 距离 + 20m − 淡变距离”内让父节点随距离线性淡出并置ChildDrawing;可见实体若尚未加载或正在淡入,则强制父 LOD 全显;驾驶高速或狙击时,若父 LOD 也未加载还可强制祖父显示src/dev_ng/game/renderer/PostScan.cpp:1003-1100、src/dev_ng/game/scene/lod/LodMgr.h:173-190。
这保证了“父 LOD 一直显示直到子 HD 已加载并淡入”,是 GTAV 远景无洞的核心不变量。可见范围判定 WithinVisibleRange 用“LOD 父节点位置(basis pivot)到相机距离 ≤ 类型缩放 × LOD 距离 × 级别缩放 + 淡变距离 + 预流式距离” src/dev_ng/game/scene/lod/LodMgr.cpp:137-151。渲染侧的 PostScan 细节归属 ../11-rendering/render-pipeline-and-phases.md。
5.4 Drawable 内部 LOD(LODDrawable)#
与实体级 LOD 树不同,rmcDrawable 自身有多个网格 LOD。LODDrawable 为每个渲染相位(默认/阴影/反射/水/镜面)在 15 位中各用 3 位存一个“强制 LOD”(0x7 表示不强制,0x4 位表示强制,低 2 位为索引),并按到 drawlist 计算点的距离计算 LOD 级别与交叉淡变值 src/dev_ng/game/scene/lod/LodDrawable.h:26-71。drawable 层次本身见 ../03-rage-graphics/models-drawables-and-shaders.md。
5.5 贴图 LOD(CTexLod)#
CTexLod 负责把 HD 贴图字典的顶层 mip 切入/切出普通贴图(CMipSwitcher 持有低/高贴图引用);PS4/XB1 启用 NG_HD_SPLIT 变体 src/dev_ng/game/scene/texLod.h:1-120。资产与 HD 字典的绑定来自 RPF 清单(第 2 节)。CHDAssetManager 还为过场维护最多 32 个模型的 HD 请求 src/dev_ng/game/scene/texLod.h:49-75。
6. 地图区域(CMapAreas)#
CMapAreas 只区分“城市/乡村”两类区域,盒子来自 maparea.ipl(AddAreaBoxToArea 断言名称必须是二者之一);Update() 为空函数 src/dev_ng/game/game/zones.h:26-53、src/dev_ng/game/game/zones.cpp:91-106。地图分区/人口区(CMapZoneManager、CPopZones)属于 ../20-gameplay/world-rules-clock-weather.md。
7. DLC:内容挂载与地图变更集#
DLC 包(CMountableContent)根目录有 setup2.xml(SSetupData:设备名、content.xml 路径、变更集与变更集分组、启动脚本、包类型 TU/LEVEL/COMPAT/PAID、顺序、依赖包、最低版本、子包数)src/dev_ng/game/scene/MountableContent.cpp:37、src/dev_ng/game/scene/ExtraContent.psc:87-107。挂载状态 CS_SETUP_READ/CS_FULLY_MOUNTED/CS_CORRUPTED/...、设备类型(文件夹/packfile/XContent/DLC 光盘)见 src/dev_ng/game/scene/MountableContent.h:26-28。
变更集分组是固定的哈希常量:GROUP_VERSIONINFO、GROUP_EARLY_ON、GROUP_STARTUP、GROUP_MAP、GROUP_MAP_SP、GROUP_ON_DEMAND、GROUP_UPDATE_STREAMING 等,以及 title update 专用的 CCS_TITLE_UPDATE_* src/dev_ng/game/scene/ExtraContentDefs.h:24-50。CDataFileMgr::ContentChangeSet 描述一组动作:失效/禁用/启用文件、卸载/加载 TXD、常驻资源、注销资源、要加载的数据文件,以及是否需要加载画面、执行条件、是否可用 cacheloader src/dev_ng/game/scene/DataFileMgr.h:343-378;它被展开成按优先级排序的 ChangeSetAction(RPF/数据文件/TXD/常驻资源/失效内景/失效可流式对象等)src/dev_ng/game/scene/DataFileMgr.h:382-426,由 CFileLoader::ExecuteContentChangeSet/RevertContentChangeSet 执行 src/dev_ng/game/scene/FileLoader.h:83-84。
地图变更集的执行(GROUP_MAP/GROUP_MAP_SP)是全游戏最重的运行时操作之一 src/dev_ng/game/scene/MountableContent.cpp:529-680:
- 校验执行条件;地图变更必须处于
MCS_UPDATE状态(除非允许任意时刻); CMapFileMgr::Reset;记录CInteriorProxy::ms_currExecutingCS以把新内景标记为 DLC;- 排空
pgStreamer、刷流式加载器与渲染线程;卸载全部导航网格并使 ped 追踪器失效; - 构建依赖图,保存脚本 IMAP 状态,重启
CTexLod会话,执行文件级变更集,再恢复脚本 IMAP 状态; SetMapFileDependencies;若允许则用 DLC 专属 cacheloader 文件(<device>%PLATFORM%/data/cacheLoaderData_DLC/);FixupAllEntriesAfterRegistration、MarkManagedImapFiles、必要时RequestAndRemove静态包围盒、AddAllDummyBounds(changeSet)、box streamerPostProcess;- 最后
RequestDLCItypFiles并标记变更集为激活。
非地图分组的变更集直接执行文件级动作 src/dev_ng/game/scene/MountableContent.cpp:662-668。title update 在流式初始化时先执行 CCS_GROUP_UPDATE_STREAMING 与 CCS_TITLE_UPDATE_STREAMING src/dev_ng/game/streaming/streaming.cpp:267-270。DLC 枚举、云端清单、所有权与平台商店(CExtraContentManager)的完整流程属于平台/在线服务范畴,见 ../22-input-platform/platform-services-and-settings.md;CExtraContentManager 中还存在代码完整性相关的检查函数(IsCodeCompromised/CodeCheck),属于安全子系统,本 Wiki 不展开 src/dev_ng/game/scene/ExtraContent.h:347、src/dev_ng/game/scene/ExtraContent.h:436(见 ../23-debug-security/security-overview.md)。
约束与边界行为#
- archetype 上限:ITYP 装载会因 archetype 总数超过
GetMaxArchetypeIndex()触发紧急卸载并断言;这是“地图密度 × 同时常驻 ITYP 数”的硬上限src/dev_ng/game/scene/loader/mapTypes.cpp:174-198。 - 依赖缺失:实体的 archetype 不在其 IMAP 的依赖 ITYP 中时被跳过(断言);IMAP 含内景却无 ITYP 依赖时断言
src/dev_ng/rage/framework/src/fwscene/mapdata/mapdata.cpp:119-127、src/dev_ng/game/scene/loader/MapData.cpp:563-565。 - 只有 HD IMAP 跑
PostAdd/PostRemove:LOD/SLOD IMAP 的实体不进 scene update、不生成场景点src/dev_ng/game/scene/loader/MapData.cpp:146、src/dev_ng/game/scene/loader/MapData.cpp:217-220。 - 可见时不切换 IMAP 组:托管 IMAP 组在可见时延迟切换,因此时间/天气相关物件可能比条件晚出现或消失
src/dev_ng/game/scene/loader/ManagedImapGroup.cpp:84-101。
未决问题 / 证据不足#
fwMapDataContents::Entities_Create/Entities_AddToWorld(LOD 挂接、推迟加入、剔除许可)在框架中实现,本页未展开,见框架页。CDataFileMgr::Load对content.xml的解析、RemapDataFileName、patch 文件机制未逐行追踪。CExtraContentManager的设备枚举、云端清单与付费包逻辑(4700 行)仅概述;ExtraMetadataMgr(DLC 元数据,如商店服装)未覆盖。CTexLod的切换策略(冷却COOLDOWN_TIME=2000ms的使用位置、请求优先级)未逐行追踪src/dev_ng/game/scene/texLod.h:117。