Skip to content

Instantly share code, notes, and snippets.

@tbttfox
Last active June 4, 2025 18:03
Show Gist options
  • Save tbttfox/2b0715ac49820b8aa2686b78cf47fbab to your computer and use it in GitHub Desktop.
Save tbttfox/2b0715ac49820b8aa2686b78cf47fbab to your computer and use it in GitHub Desktop.
A quick script to print the hierarchy of an alembic file and its properties.Sorry, no nice command line interface. You've gotta edit it yourself
import os
from alembic.Abc import IArchive
from alembic.AbcGeom import (
IXform,
IPolyMesh,
ICamera,
ICurves,
ILight,
INuPatch,
IPoints,
ISubD,
IFaceSet,
)
PREFIXES = [[" \u2560 ", " \u2551 "], [" \u255a ", " "]]
PROP_PREFIXES = [[" \u251c ", " \u2502 "], [" \u2514 ", " "]]
PRINT_ALL = object()
def getTypedIObject(obj):
"""Cast an alembic IObject to its AbcGeom subtype"""
ilist = [
IXform,
IPolyMesh,
ICamera,
ICurves,
ILight,
INuPatch,
IPoints,
ISubD,
IFaceSet,
]
md = obj.getMetaData()
for abcType in ilist:
if abcType.matches(md):
return abcType(obj.getParent(), obj.getName())
return None
def getFrange(schObj):
"""Get the frame range of animation for the given object with a schema"""
sch = schObj.getSchema()
ts = sch.getTimeSampling()
tst = ts.getTimeSamplingType()
if tst.isAcyclic():
times = ts.getStoredTimes()
tpc = 1.0
if len(times) > 1:
tpc = times[1] - times[0]
else:
tpc = tst.getTimePerCycle()
fps = 1.0 / tpc
# get the relevant values
numSamples = sch.getNumSamples()
start = round(ts.getSampleTime(0) * fps)
end = round(ts.getSampleTime(numSamples - 1) * fps)
return [start, end], fps
def _getSkippedChildren(chis, skipTypes):
"""Convenience function to Filter a list of objects by alembic type"""
if skipTypes:
chis = [c for c in chis if c.getMetaData().get("schema") not in skipTypes]
return chis
def printPropertyValue(iPar, printValueNames, parPfx=""):
"""Print a hierarchy of alembic properties
Arguments:
iPar (IObject): The parent object
printValueNames (None | list[str] | Literal[PRINT_ALL]): Which properties to print the actual
values of. If you pass the PRINT_ALL object, then all property values will be printed
parPfx (str): (INTERNAL USE) The current indented printing prefix
"""
numProps = iPar.getNumProperties()
for i in range(numProps):
prop = iPar.getProperty(i)
pfxs = PROP_PREFIXES[1 if i == numProps - 1 else 0]
propName = prop.getName()
print(parPfx + pfxs[0] + propName)
if (propName is PRINT_ALL) or (propName in printValueNames):
print(parPfx + ' > ', prop.getValue())
if prop.isCompound():
printPropertyValue(prop, printValueNames, parPfx + pfxs[1])
def printObjects(
obj,
skipTypes=None,
doPrintProps=True,
printValueNames=None,
parPfx="",
):
"""Print a hierarchy of alembic objects
Arguments:
obj (IObject): The current alembic IObject
skipTypes (list[str]): Skip the objects whose types are in this list
Good for reducing the clutter in the hierarchy
doPrintProps (bool): Whether to print the property hierarchies under each object hierarchy
printValueNames (None | list[str] | Literal[PRINT_ALL]): Which properties to print the actual
values of. If you pass the PRINT_ALL object, then all property values will be printed
parPfx (str): (INTERNAL USE) The current indented printing prefix
"""
printValueNames = [] if printValueNames is None else printValueNames
chis = _getSkippedChildren(obj.children, skipTypes)
for i, child in enumerate(chis):
lastChild = i == len(chis) - 1
pfxs = PREFIXES[1 if lastChild else 0]
tChild = getTypedIObject(child)
print(
parPfx + pfxs[0] + child.getName(), type(tChild).__name__, getFrange(tChild)
)
objPfx = PREFIXES[0][1] if _getSkippedChildren(child.children, skipTypes) else ""
if doPrintProps:
printPropertyValue(child.getProperties(), printValueNames, parPfx + pfxs[1] + objPfx)
printObjects(
child,
skipTypes=skipTypes,
doPrintProps=doPrintProps,
printValueNames=printValueNames,
parPfx=parPfx + pfxs[1],
)
def main():
paths = [
r'path/to/file.abc',
]
for a in paths:
if not os.path.exists(a):
print("\n\nArchive:", a)
raise ValueError("File does not exist: {0}".format(a))
arch = IArchive(str(a))
try:
print("\n\nArchive:", a, arch.getCoreType())
top = arch.getTop()
printObjects(
top,
skipTypes=["AbcGeom_FaceSet_v1"],
doPrintProps=True,
printValueNames=['P'],
)
finally:
# If python crashes, and we don't do this, then windows
# will lock the files until the python process is killed
del arch, top
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment