Beispiel #1
0
def _walk_point_cache(ctx: ModifierContext, block_name: bytes,
                      bfile: blendfile.BlendFile,
                      pointcache: blendfile.BlendFileBlock, extension: bytes):
    flag = pointcache[b'flag']
    if flag & cdefs.PTCACHE_EXTERNAL:
        path, field = pointcache.get(b'path', return_field=True)
        log.info('    external cache at %s', path)
        bpath = bpathlib.BlendPath(path)
        yield result.BlockUsage(pointcache,
                                bpath,
                                path_full_field=field,
                                is_sequence=True,
                                block_name=block_name)
    elif flag & cdefs.PTCACHE_DISK_CACHE:
        # See ptcache_path() in pointcache.c
        name, field = pointcache.get(b'name', return_field=True)
        if not name:
            # See ptcache_filename() in pointcache.c
            idname = ctx.owner[b'id', b'name']
            name = idname[2:].hex().upper().encode()
        path = b'//%b%b/%b_*%b' % (
            cdefs.PTCACHE_PATH, bfile.filepath.stem.encode(), name, extension)
        log.info('   disk cache at %s', path)
        bpath = bpathlib.BlendPath(path)
        yield result.BlockUsage(pointcache,
                                bpath,
                                path_full_field=field,
                                is_sequence=True,
                                block_name=block_name)
Beispiel #2
0
def _expand_group(block: blendfile.BlendFileBlock):
    log.debug('Collection/group Block: %s (name=%s)', block, block.id_name)

    objects = block.get_pointer((b'gobject', b'first'))
    for item in iterators.listbase(objects):
        yield item.get_pointer(b'ob')

    # Recurse through child collections.
    try:
        children = block.get_pointer((b'children', b'first'))
    except KeyError:
        # 'children' was introduced in Blender 2.8 collections
        pass
    else:
        for child in iterators.listbase(children):
            subcoll = child.get_pointer(b'collection')
            if subcoll is None:
                continue

            if subcoll.dna_type_id == b'ID':
                # This issue happened while recursing a linked-in 'Hidden'
                # collection in the Chimes set of the Spring project. Such
                # collections named 'Hidden' were apparently created while
                # converting files from Blender 2.79 to 2.80. This error
                # isn't reproducible with just Blender 2.80.
                yield subcoll
                continue

            log.debug('recursing into child collection %s (name=%r, type=%r)',
                      subcoll, subcoll.id_name, subcoll.dna_type_name)
            yield from _expand_group(subcoll)
Beispiel #3
0
def _expand_particle_settings(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_mtex(block)

    block_ren_as = block[b'ren_as']
    if block_ren_as == cdefs.PART_DRAW_GR:
        yield block.get_pointer(b'dup_group')
    elif block_ren_as == cdefs.PART_DRAW_OB:
        yield block.get_pointer(b'dup_ob')
Beispiel #4
0
def mesh(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Mesh data blocks."""
    block_external = block.get_pointer((b'ldata', b'external'), None)
    if block_external is None:
        block_external = block.get_pointer((b'fdata', b'external'), None)
    if block_external is None:
        return

    path, field = block_external.get(b'filename', return_field=True)
    yield result.BlockUsage(block, path, path_full_field=field)
Beispiel #5
0
def _expand_curve(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_material(block)

    for fieldname in (b'vfont', b'vfontb', b'vfonti', b'vfontbi', b'bevobj',
                      b'taperobj', b'textoncurve'):
        yield block.get_pointer(fieldname)
Beispiel #6
0
def scene(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Scene data blocks."""
    # Sequence editor is the only interesting bit.
    block_ed = block.get_pointer(b'ed')
    if block_ed is None:
        return

    single_asset_types = {
        cdefs.SEQ_TYPE_MOVIE, cdefs.SEQ_TYPE_SOUND_RAM, cdefs.SEQ_TYPE_SOUND_HD
    }
    asset_types = single_asset_types.union({cdefs.SEQ_TYPE_IMAGE})

    for seq, seq_type in iterators.sequencer_strips(block_ed):
        if seq_type not in asset_types:
            continue

        seq_strip = seq.get_pointer(b'strip')
        if seq_strip is None:
            continue
        seq_stripdata = seq_strip.get_pointer(b'stripdata')
        if seq_stripdata is None:
            continue

        dirname, dn_field = seq_strip.get(b'dir', return_field=True)
        basename, bn_field = seq_stripdata.get(b'name', return_field=True)
        asset_path = bpathlib.BlendPath(dirname) / basename

        is_sequence = seq_type not in single_asset_types
        yield result.BlockUsage(seq_strip,
                                asset_path,
                                is_sequence=is_sequence,
                                path_dir_field=dn_field,
                                path_base_field=bn_field)
Beispiel #7
0
def vector_font(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Vector Font data blocks."""
    path, field = block.get(b'name', return_field=True)
    if path == b'<builtin>':  # builtin font
        return
    yield result.BlockUsage(block, path, path_full_field=field)
Beispiel #8
0
def image(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Image data blocks."""
    # old files miss this
    image_source = block.get(b'source', default=cdefs.IMA_SRC_FILE)
    if image_source not in {
            cdefs.IMA_SRC_FILE, cdefs.IMA_SRC_SEQUENCE, cdefs.IMA_SRC_MOVIE
    }:
        return

    pathname, field = block.get(b'name', return_field=True)
    is_sequence = image_source == cdefs.IMA_SRC_SEQUENCE

    yield result.BlockUsage(block,
                            pathname,
                            is_sequence,
                            path_full_field=field)
Beispiel #9
0
def modifier_filepath(ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    """Just yield the 'filepath' field."""
    path, field = modifier.get(b'filepath', return_field=True)
    yield result.BlockUsage(modifier,
                            path,
                            path_full_field=field,
                            block_name=block_name)
Beispiel #10
0
def _expand_generic_mtex(block: blendfile.BlendFileBlock):
    if not block.dna_type.has_field(b'mtex'):
        # mtex was removed in Blender 2.8
        return

    for mtex in block.iter_fixed_array_of_pointers(b'mtex'):
        yield mtex.get_pointer(b'tex')
        yield mtex.get_pointer(b'object')
Beispiel #11
0
def modifier_cloth(ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    pointcache = modifier.get_pointer(b'point_cache')
    if pointcache is None:
        return

    yield from _walk_point_cache(ctx, block_name, modifier.bfile, pointcache,
                                 cdefs.PTCACHE_EXT)
Beispiel #12
0
def movie_clip(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """MovieClip data blocks."""
    path, field = block.get(b'name', return_field=True)
    # TODO: The assumption that this is not a sequence may not be true for all modifiers.
    yield result.BlockUsage(block,
                            path,
                            is_sequence=False,
                            path_full_field=field)
Beispiel #13
0
def _expand_material(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_nodetree_id(block)
    yield from _expand_generic_mtex(block)

    try:
        yield block.get_pointer(b'group')
    except KeyError:
        # Groups were removed from Blender 2.8
        pass
Beispiel #14
0
def _get_texture(prop_name: bytes, dblock: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    """Yield block usages from a texture propery.

    Assumes dblock[prop_name] is a texture data block.
    """
    if dblock is None:
        return

    tx = dblock.get_pointer(prop_name)
    yield from _get_image(b'ima', tx, block_name)
Beispiel #15
0
def modifier_ocean(ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    if not modifier[b'cached']:
        return

    path, field = modifier.get(b'cachepath', return_field=True)
    # The path indicates the directory containing the cached files.
    yield result.BlockUsage(modifier,
                            path,
                            is_sequence=True,
                            path_full_field=field,
                            block_name=block_name)
Beispiel #16
0
def _expand_generic_nodetree(block: blendfile.BlendFileBlock):
    assert block.dna_type.dna_type_id == b'bNodeTree'

    nodes = block.get_pointer((b'nodes', b'first'))
    for node in iterators.listbase(nodes):
        if node[b'type'] == cdefs.CMP_NODE_R_LAYERS:
            continue
        yield node

        # The 'id' property points to whatever is used by the node
        # (like the image in an image texture node).
        yield node.get_pointer(b'id')
Beispiel #17
0
def _expand_object(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_material(block)

    yield block.get_pointer(b'data')

    if block[b'transflag'] & cdefs.OB_DUPLIGROUP:
        yield block.get_pointer(b'dup_group')

    yield block.get_pointer(b'proxy')
    yield block.get_pointer(b'proxy_group')

    # 'ob->pose->chanbase[...].custom'
    block_pose = block.get_pointer(b'pose')
    if block_pose:
        assert block_pose.dna_type.dna_type_id == b'bPose'
        # sdna_index_bPoseChannel = block_pose.file.sdna_index_from_id[b'bPoseChannel']
        channels = block_pose.get_pointer((b'chanbase', b'first'))
        for pose_chan in iterators.listbase(channels):
            yield pose_chan.get_pointer(b'custom')

    # Expand the objects 'ParticleSettings' via 'ob->particlesystem[...].part'
    # sdna_index_ParticleSystem = block.file.sdna_index_from_id.get(b'ParticleSystem')
    # if sdna_index_ParticleSystem is not None:
    psystems = block.get_pointer((b'particlesystem', b'first'))
    for psystem in iterators.listbase(psystems):
        yield psystem.get_pointer(b'part')
Beispiel #18
0
def _expand_scene(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_nodetree_id(block)
    yield block.get_pointer(b'camera')
    yield block.get_pointer(b'world')
    yield block.get_pointer(b'set', default=None)
    yield block.get_pointer(b'clip', default=None)

    # sdna_index_Base = block.file.sdna_index_from_id[b'Base']
    # for item in bf_utils.iter_ListBase(block.get_pointer((b'base', b'first'))):
    #     yield item.get_pointer(b'object', sdna_index_refine=sdna_index_Base)
    bases = block.get_pointer((b'base', b'first'))
    for base in iterators.listbase(bases):
        yield base.get_pointer(b'object')

    # Sequence Editor
    block_ed = block.get_pointer(b'ed')
    if not block_ed:
        return

    strip_type_to_field = {
        cdefs.SEQ_TYPE_SCENE: b'scene',
        cdefs.SEQ_TYPE_MOVIECLIP: b'clip',
        cdefs.SEQ_TYPE_MASK: b'mask',
        cdefs.SEQ_TYPE_SOUND_RAM: b'sound',
    }
    for strip, strip_type in iterators.sequencer_strips(block_ed):
        try:
            field_name = strip_type_to_field[strip_type]
        except KeyError:
            continue
        yield strip.get_pointer(field_name)
Beispiel #19
0
def modifier_mesh_sequence_cache(
        ctx: ModifierContext, modifier: blendfile.BlendFileBlock,
        block_name: bytes) -> typing.Iterator[result.BlockUsage]:
    """Yield the Alembic file(s) used by this modifier"""
    cache_file = modifier.get_pointer(b'cache_file')
    if cache_file is None:
        return

    is_sequence = bool(cache_file[b'is_sequence'])
    cache_block_name = cache_file.id_name
    assert cache_block_name is not None

    path, field = cache_file.get(b'filepath', return_field=True)
    yield result.BlockUsage(cache_file,
                            path,
                            path_full_field=field,
                            is_sequence=is_sequence,
                            block_name=cache_block_name)
Beispiel #20
0
def object_block(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Object data blocks."""
    ctx = modifier_walkers.ModifierContext(owner=block)

    # 'ob->modifiers[...].filepath'
    mods = block.get_pointer((b'modifiers', b'first'))
    for mod_idx, block_mod in enumerate(
            iterators.listbase(mods, next_path=(b'modifier', b'next'))):
        block_name = b'%s.modifiers[%d]' % (block.id_name, mod_idx)
        mod_type = block_mod[b'modifier', b'type']
        log.debug('Tracing modifier %s, type=%d', block_name.decode(),
                  mod_type)

        try:
            mod_handler = modifier_walkers.modifier_handlers[mod_type]
        except KeyError:
            continue
        yield from mod_handler(ctx, block_mod, block_name)
Beispiel #21
0
def modifier_smoke_sim(ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    my_log = log.getChild('modifier_smoke_sim')

    domain = modifier.get_pointer(b'domain')
    if domain is None:
        my_log.debug('Modifier %r (%r) has no domain',
                     modifier[b'modifier', b'name'], block_name)
        return

    pointcache = domain.get_pointer(b'point_cache')
    if pointcache is None:
        return

    format = domain.get(b'cache_file_format')
    extensions = {
        cdefs.PTCACHE_FILE_PTCACHE: cdefs.PTCACHE_EXT,
        cdefs.PTCACHE_FILE_OPENVDB: cdefs.PTCACHE_EXT_VDB
    }
    yield from _walk_point_cache(ctx, block_name, modifier.bfile, pointcache,
                                 extensions[format])
Beispiel #22
0
def modifier_fluid_sim(ctx: ModifierContext, modifier: blendfile.BlendFileBlock, block_name: bytes) \
        -> typing.Iterator[result.BlockUsage]:
    my_log = log.getChild('modifier_fluid_sim')

    fss = modifier.get_pointer(b'fss')
    if fss is None:
        my_log.debug('Modifier %r (%r) has no fss',
                     modifier[b'modifier', b'name'], block_name)
        return

    path, field = fss.get(b'surfdataPath', return_field=True)

    # This may match more than is used by Blender, but at least it shouldn't
    # miss any files.
    # The 'fluidsurface' prefix is defined in source/blender/makesdna/DNA_object_fluidsim_types.h
    bpath = bpathlib.BlendPath(path)
    yield result.BlockUsage(fss,
                            bpath,
                            path_full_field=field,
                            is_sequence=True,
                            block_name=block_name)
Beispiel #23
0
def _expand_generic_animdata(block: blendfile.BlendFileBlock):
    block_adt = block.get_pointer(b'adt')
    if block_adt:
        yield block_adt.get_pointer(b'action')
Beispiel #24
0
def _expand_mesh(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_material(block)
    yield block.get_pointer(b'texcomesh')
Beispiel #25
0
def _expand_texture(block: blendfile.BlendFileBlock):
    yield from _expand_generic_animdata(block)
    yield from _expand_generic_nodetree_id(block)
    yield block.get_pointer(b'ima')
Beispiel #26
0
def _expand_generic_material(block: blendfile.BlendFileBlock):
    array_len = block.get(b'totcol')
    yield from block.iter_array_of_pointers(b'mat', array_len)
Beispiel #27
0
def _expand_generic_nodetree_id(block: blendfile.BlendFileBlock):
    block_ntree = block.get_pointer(b'nodetree', None)
    if block_ntree is not None:
        yield from _expand_generic_nodetree(block_ntree)
Beispiel #28
0
def sound(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Sound data blocks."""
    path, field = block.get(b'name', return_field=True)
    yield result.BlockUsage(block, path, path_full_field=field)
Beispiel #29
0
    def wrapper(block: blendfile.BlendFileBlock, *args, **kwargs):
        if block.get(b'packedfile', default=False):
            log.debug('Datablock %r is packed; skipping', block.id_name)
            return

        yield from wrapped(block, *args, **kwargs)
Beispiel #30
0
def cache_file(
        block: blendfile.BlendFileBlock) -> typing.Iterator[result.BlockUsage]:
    """Cache file data blocks."""
    path, field = block.get(b'filepath', return_field=True)
    yield result.BlockUsage(block, path, path_full_field=field)