Hi Richard,
thx for the reply. But this particular issue was dealing with geometry that was there, but not there. My best guess now is that the cache from an ‘anomynos Layer’ was not erased correctly.
As I am digging into USD and the amazing workflows that can be achieved using layers (which make this format unique), I am going through the ‘USD Survival Guide’ and found some insights on Anonymous layers
Anonymous layers have these special features: (source: ‘USD Survival Guide’)
- They are in-memory layers that have no real path or asset information fields.
- We can additionally give a custom name suffix, so that we can identify the layer better visually
- The identifier is not run through the asset resolver (Edit: I have to verify this again, but I’m fairly certain)
- They cannot be saved via
layer.Save()
, it will return an error
- We can convert them to “normal” layers, by assigning a non-anonymous identifier (
layer.identifier="/file/path/myIdentifier.usd"
), this also removes the save permission lock.
Here is how it might be solved in the future ( I have not testede it, it is an answer that I got from a prepromted DeepSeek R1, also being pointed at the ‘USD survival Guide’
Ah, this sounds like an issue with anonymous layers (temporary, in-memory layers) or composed overrides that are not visible. Here are steps to solve it:
1. Identify the “Ghost Prim”
Question: Where is the prim coming from?
- Possibility 1: It is hidden in an anonymous layer (not saved to disk).
- Possibility 2: It was loaded via a Reference/Payload that no longer exists.
- Possibility 3: A composed override (e.g., in a Variant) is keeping it alive.
2. Solutions
A. Check All Anonymous Layers
Anonymous layers are not displayed in the layer list, but they exist in memory. Here’s how to find them:
from pxr import Sdf, Usd
# List all loaded layers (including anonymous ones)
all_layers = Sdf.Layer.GetLoadedLayers()
for layer in all_layers:
if not layer.identifier: # Anonymous layers have no file ID
print("Anonymous layer found:", layer)
# Search the layer for the prim
for prim in layer.rootPrims:
if prim.name == "NAME_OF_GHOST_PRIM":
print("Prim found in anonymous layer:", prim.path)
# Delete the prim
layer.RemoveRootPrim(prim)
B. Clean Up the Stage
Sometimes prims “stick around” even after their layers are removed:
stage = omni.usd.get_context().get_stage() # Omniverse
# Force a stage cleanup
stage.Reload()
C. Edit Low-Level Sdf Layers
If the prim is stuck in an anonymous layer that isn’t visible:
# Find all anonymous layers
anon_layers = [layer for layer in Sdf.Layer.GetLoadedLayers() if not layer.identifier]
# Delete the prim in all anonymous layers
target_prim_path = Sdf.Path("/Path/To/GhostPrim")
for layer in anon_layers:
if layer.HasPrim(target_prim_path):
layer.DeletePrim(target_prim_path)
D. Flatten the Stage (Nuclear Option)
Create a new stage without composition/overrides:
flattened_stage = stage.Flatten()
flattened_stage.Export("CleanedScene.usda")
3. Debugging Tools
usdview
- Open the scene in
usdview
and use Layer Stack → Composition (Ctrl + L) to see where the prim is defined.
Inspect Layer Stack
stage = Usd.Stage.Open("scene.usda")
# Show all layers in the stack (including anonymous ones)
print(stage.GetLayerStack())
Mute Layers
Disable layers to find the culprit:
stage.MuteLayer(layer.identifier) # Omniverse: omni.usd.get_context().mute_layer()
4. Common Causes
- Overridden Payloads: A payload was loaded but not unloaded.
- Instanced Prims: The prim is part of an instance (e.g., via
UsdGeom.PointInstancer
).
- Undo History: Omniverse/Apps sometimes keep undo data in memory.
5. Workflow for Omniverse
- Reset the Stage:
omni.usd.get_context().new_stage() # Clears all temporary layers
- Clear Undo History:
omni.kit.undo.get_undo_stack().clear()
- Clear Cache:
stage.ClearMetadata("customLayerData") # Removes persistent data
Summary
- Anonymous layers are often the cause – check them with
Sdf.Layer.GetLoadedLayers()
.
- Flattening the stage removes all overrides/composition.
- In Omniverse: Use
new_stage()
or clear the undo stack.
p.s.
, the code snippets I provided are meant to be run in the Script Editor of your application (e.g., Omniverse, Blender, or any Python environment where USD is available). Here’s how to use them:
- Omniverse: Open the Script Editor (
Window → Script Editor
) and paste the code.
- Blender: Use the Scripting workspace and run the code in the text editor.
- Standalone Python: Ensure you have the
pxr
module installed (pip install usd-core
) and run the code in a Python environment.