lexicon in accordance with PackageName.cpp
-
“Long Package Name” : virtual path that the engine uses to reference packages and assets
eg: /Game/MyAsset
-
“Filename” : real path of the asset on disk before being PAKed, but is also the path expected by unreal’s FileSystem(s) when loading a package
eg: ../../../MyProject/Content/MyAsset.uasset
-
“Root Path” : a root of the virtual file system, there are multiple
eg: /Game
and /Engine
-
“Mount Point” : mapping a “Root Path” to a “Filename” (or rather, a directory name)
eg: /Game
→ ../../../MyProject/Content
References to assets/objects are always in LongPackageName/LongObjectName form. Whenever the engine needs to load an asset, it finds the MountPoint that best matches the input LongPackageName, converts to real file path, and loads the file via whichever filesystem is available.
Step by step :
- you request load object
/Game/MyBlueprint.MyBlueprint_C
- engine first needs to find and load the package, ie.
/Game/MyBlueprint
- it finds the mount point with the closest root path, which is
/Game
- that mount point points to
../../../MyProject/Content
- path is converted to
../../../MyProject/Content/MyBlueprint
- asset is loaded via filesystem
Important note here : the PAK filesystem is an abstraction of the filesystem only. It has no business in the loading part, or in the path-conversion part. And neither of those two parts know anything about PAK files. They are completely dissociate.
Understanding this, what happens in your use case is pretty straightforward :
-
There is a default mount point /Game
→ YourProject/Content
, that allows your project to access its own content
-
You add mount point /Game
→ MountedPakProject/Content
. This mount point takes priority over existing one.
-
From this point on, any content from your main project becomes unreachable, because all references to /Game/Something
will be converted to MountedPakProject/Content/Something
-
Remove the mount point
-
Content from main project is now reachable again, but content from mounted pak is no longer reachable
Unfortunately, there’s no simple solution unless you have a degree of control over the PAKing of assets.
Let’s say for example your inspected pak has a StaticMesh (SM_MyStatic) and a Texture (T_MyTex), and the StaticMesh references the Texture as its texture. The reference will look like /Game/T_MyTex
. When you load the static mesh, it will automatically load its dependencies and look for /Game/T_MyTex
.
- If
/Game
points to your project content, it will not find T_MyTex there.
- If you override
/Game
to point to the inspected pak contents, you already know what happens.
- If you use a different mount point, eg
/ThePak
to point to the pak’s contents, your project contents will still work, and you may be able to load PAK assets individually, but the references/dependencies will not work - static mesh will be untextured because it still looks for /Game/T_MyTex
and not for /ThePak/T_MyTex
Unfortunately, there is no way (AFAIK) that will let you remap those references at runtime, that would probably require editing uasset files in memory when mounting the pak file which would be ultra wonky.
The better way to handle this is something I described in the Primer you read - it involves placing content in a plugin rather than in project, which results in assets (as well as their references/dependencies!) having their own root path such as /Plugin1
, then you can pak it that way and when mounting the pak you just need to register mount point /Plugin1
to pak content. Of course this requires having control over assets before they are cooked/paked, which may not be your case…
In your case, a couple other options may be worth considering. Since you are loading PAKs from other projects into yours, I assume those PAKs are not encrypted, or that you have already decrypted them. That means you have capability to unpak and repak assets. This will not help you remap references between assets, however by doing that you can change the real path of files within the PAK. So instead of having files at ../../../OtherProject/Content/something
you can repak them at ../../../YourProject/Content/something
. With that, the content from both your main PAK and the inspected PAK should be reachable simultaneously from the standard /Game/
root. Keep in mind however that this is equivalent to unzipping two zip archives into the same folder - if there are conflicts, one will overwrite the other. This would be especially bad if the conflicting assets are not of the same type. To avoid issues, you can wrap all your project assets into a folder with a low risk of conflict, ie. something like /Game/SomeRandomString/...
But you might still encounter those issues if you try to inspect multiple PAKs simultaneously.
Alternatively, it would be worth looking into making a subclass of the PakFile subsystem, or just making a custom filesystem in general. It doesn’t have to be super complex, you’d make some kind of wrapper filesystem that just delegates to the PAKFile filesystem, but you’d have the chance to remap the input file path into a different one before delegating. Basically, create a IPlatformFile subclass, implement the IPlatformFile interface (functions like FileExists and OpenRead would be the most important), transform the Filename parameter, then forward to the PlatformFilePak implementation. This is just an idea, I haven’t really dug into it.
In retrospect, maybe I went a bit overboard. If you are just making a static mesh viewer, you probably don’t need to enter into that level of complexity. Some more ideas :
-
Load the static mesh and provide your own default material. Then you don’t have to care about dependencies and texture streaming failing after unmounting.
-
Disable texture streaming. This should at least correct the error you are getting. Maybe there’s streaming for Mesh LOD as well though, not sure. Maybe it can be disabled as well.