Exemple #1
0
    def load(
        self,
        datablock: T.ID,
        context: Context,
    ) -> DatablockProxy:
        """Load a datablock into this proxy.

        Args:
            datablock: the embedded datablock to load into this proxy
            context: visit and proxy state

        Returns:
            this DatablockProxy
        """

        self.clear_data()
        self._has_datablock = True
        properties = context.synchronized_properties.properties(datablock)
        # this assumes that specifics.py apply only to ID, not Struct
        properties = specifics.conditional_properties(datablock, properties)
        with context.visit_state.enter_datablock(self, datablock):
            for name, bl_rna_property in properties:
                attr = getattr(datablock, name)
                attr_value = read_attribute(attr, name, bl_rna_property,
                                            context)
                # Also write None values to reset attributes like Camera.dof.focus_object
                # TODO for scene, test difference, only send update if dirty as continuous updates to scene
                # master collection will conflicting writes with Master Collection
                self._data[name] = attr_value

        self.attach_filepath_raw(datablock)
        self.attach_media_descriptor(datablock, context)
        self._custom_properties.load(datablock)
        return self
Exemple #2
0
    def load(self, attribute: T.bpy_struct, key: Union[int, str],
             context: Context) -> StructProxy:
        """
        Load the attribute Blender struct into this proxy

        Args:
            attribute: the Blender struct to load into this proxy, (e.g an ObjectDisplay instance)
            key: the identifier of attribute in its parent (e.g. "display")
            context: the proxy and visit state
        """
        self.clear_data()
        properties = context.synchronized_properties.properties(attribute)
        # includes properties from the bl_rna only, not the "view like" properties like MeshPolygon.edge_keys
        # that we do not want to load anyway
        properties = specifics.conditional_properties(attribute, properties)
        context.visit_state.path.append(key)
        try:
            for name, bl_rna_property in properties:
                attr = getattr(attribute, name)
                attr_value = read_attribute(attr, name, bl_rna_property,
                                            context)
                self._data[name] = attr_value
        finally:
            context.visit_state.path.pop()

        return self
Exemple #3
0
    def _diff(self, attribute: T.bpy_struct, key: Union[int,
                                                        str], prop: T.Property,
              context: Context, diff: StructProxy) -> Optional[Delta]:
        """
        Computes the difference between the state of an item tracked by this proxy and its Blender state
        and attached the difference to diff.

        See diff()

        Args:
            attribute: the struct to update (e.g. a Material instance)
            key: the key that identifies attribute in parent (e.g "Material")
            prop: the Property of struct as found in its enclosing object
            context: proxy and visit state
            diff: the proxy that holds the difference and will be transmitted in a Delta

        Returns:
            a delta if any difference is found, None otherwise
        """
        # PERF accessing the properties from the synchronized_properties is **far** cheaper that iterating over
        # _data and the getting the properties with
        #   member_property = struct.bl_rna.properties[k]
        # line to which py-spy attributes 20% of the total diff !
        if prop is not None:
            context.visit_state.path.append(key)
        try:
            properties = context.synchronized_properties.properties(attribute)
            properties = specifics.conditional_properties(
                attribute, properties)
            for k, member_property in properties:
                # TODO in test_differential.StructDatablockRef.test_remove
                # target et a scene, k is world and v (current world value) is None
                # so diff fails. v should be a BpyIDRefNoneProxy

                # make a difference between None value and no member
                try:
                    member = getattr(attribute, k)
                except AttributeError:
                    logger.info(f"diff: unknown attribute {k} in {attribute}")
                    continue

                proxy_data = self._data.get(k)
                delta = diff_attribute(member, k, member_property, proxy_data,
                                       context)

                if delta is not None:
                    diff._data[k] = delta
        finally:
            if prop is not None:
                context.visit_state.path.pop()

        # TODO detect media updates (reload(), and attach a media descriptor to diff)
        # difficult ?

        # if anything has changed, wrap the hollow proxy in a DeltaUpdate. This may be superfluous but
        # it is homogenous with additions and deletions
        if len(diff._data):
            return DeltaUpdate(diff)

        return None
Exemple #4
0
    def load(
        self,
        bl_instance: T.ID,
        key: str,
        context: Context,
        bpy_data_collection_name: str = None,
    ):
        """
        Load a datablock into this proxy
        """
        if bl_instance.is_embedded_data and bpy_data_collection_name is not None:
            logger.error(
                f"DatablockProxy.load() for {bl_instance} : is_embedded_data is True and bpy_prop_collection is {bpy_data_collection_name}. Item ignored"
            )
            return

        if bl_instance.is_embedded_data:
            self._bpy_data_collection = None

        if bpy_data_collection_name is not None:
            self._bpy_data_collection = bpy_data_collection_name

        self._class_name = bl_instance.__class__.__name__
        self._data.clear()
        properties = context.synchronized_properties.properties(bl_instance)
        # this assumes that specifics.py apply only to ID, not Struct
        properties = specifics.conditional_properties(bl_instance, properties)
        try:
            context.visit_state.datablock_proxy = self
            for name, bl_rna_property in properties:
                attr = getattr(bl_instance, name)
                attr_value = read_attribute(attr, name, bl_rna_property,
                                            context)
                # Also write None values to reset attributes like Camera.dof.focus_object
                # TODO for scene, test difference, only send update if dirty as continuous updates to scene
                # master collection will conflicting writes with Master Collection
                self._data[name] = attr_value
        finally:
            context.visit_state.datablock_proxy = None

        specifics.post_save_id(self, bl_instance)

        uuid = bl_instance.get("mixer_uuid")
        if uuid:
            # It is a bpy.data ID, not an ID "embedded" inside another ID, like scene.collection
            id_ = context.proxy_state.datablocks.get(uuid)
            if id_ is not bl_instance:
                # this occurs when
                # - when we find a reference to a BlendData ID that was not loaded
                # - the ID are not properly ordred at creation time, for instance (objects, meshes)
                # instead of (meshes, objects) : a bug
                logger.debug(
                    "DatablockProxy.load(): %s not in context.proxy_state.datablocks[uuid]",
                    bl_instance)
            self._datablock_uuid = bl_instance.mixer_uuid
            context.proxy_state.proxies[uuid] = self

        self.attach_media_descriptor(bl_instance)
        return self
Exemple #5
0
    def _diff(self, struct: T.Mesh, key: str, prop: T.Property,
              context: Context,
              diff: MeshProxy) -> Optional[Union[DeltaUpdate, DeltaReplace]]:

        if self.requires_clear_geometry(struct):
            # If any mesh buffer changes requires a clear geometry on the receiver, the receiver will clear all
            # buffers, including uv_layers and vertex_colors.
            # Resend everything
            diff.load(struct, context)

            # force ObjectProxy._diff to resend the Vertex groups
            context.visit_state.dirty_vertex_groups.add(struct.mixer_uuid)
            return DeltaReplace(diff)
        else:
            if prop is not None:
                context.visit_state.path.append(key)
            try:
                # vertex groups are always replaced as a whole
                mesh_vertex_groups = VertexGroups.from_mesh(
                    struct).to_array_sequence()
                proxy_vertex_groups: ArrayGroup = self._arrays.get(
                    "vertex_groups", [])
                if mesh_vertex_groups != proxy_vertex_groups:
                    diff._arrays["vertex_groups"] = mesh_vertex_groups

                    # force Object update. This requires that Object updates are processed later, which seems to be
                    # the order  they are listed in Depsgraph.updates
                    context.visit_state.dirty_vertex_groups.add(
                        struct.mixer_uuid)

                properties = context.synchronized_properties.properties(struct)
                properties = specifics.conditional_properties(
                    struct, properties)
                for k, member_property in properties:
                    try:
                        member = getattr(struct, k)
                    except AttributeError:
                        logger.warning("diff: unknown attribute ...")
                        logger.warning(
                            f"... {context.visit_state.display_path()}.{k}")
                        continue

                    proxy_data = self._data.get(k)
                    delta = diff_attribute(member, k, member_property,
                                           proxy_data, context)

                    if delta is not None:
                        diff._data[k] = delta

            finally:
                if prop is not None:
                    context.visit_state.path.pop()

            if len(diff._data) or len(diff._arrays):
                return DeltaUpdate(diff)

            return None
Exemple #6
0
    def _diff(self, struct: T.Struct, key: str, prop: T.Property,
              context: Context, diff: MeshProxy) -> Optional[DeltaUpdate]:
        try:

            # If any mesh buffer change requires a clear geometry on the receiver, send all buffers
            # This is the case if a face is separated from a cube. The number of vertices is unchanged
            # but the number of faces changes, which requires the receiver to call Mesh.clear_geometry(),
            # hence to reload tall the geometry, including parts that were unchanged.
            # As an optimized alternative, it should be possible not to send the unchanged arrays
            # but have MeshProxy.apply() to reload unchanged buffers from in-memory copies.
            force_send_all = proxy_requires_clear_geometry(self, struct)
            if force_send_all:
                logger.debug("requires_clear for %s", struct)

            if prop is not None:
                context.visit_state.path.append(key)

            properties = context.synchronized_properties.properties(struct)
            properties = specifics.conditional_properties(struct, properties)
            for k, member_property in properties:
                try:
                    member = getattr(struct, k)
                except AttributeError:
                    logger.warning(f"diff: unknown attribute {k} in {struct}")
                    continue

                proxy_data = self._data.get(k)
                force_diff = force_send_all and k in mesh_resend_on_clear
                try:
                    if force_diff:
                        context.visit_state.scratchpad["force_soa_diff"] = True
                    delta = diff_attribute(member, k, member_property,
                                           proxy_data, context)

                    if delta is not None:
                        diff._data[k] = delta
                    elif force_send_all and k in mesh_resend_on_clear:
                        diff._data[k] = DeltaUpdate.deep_wrap(proxy_data)
                finally:
                    if force_diff:
                        del context.visit_state.scratchpad["force_soa_diff"]

        finally:
            if prop is not None:
                context.visit_state.path.pop()

        if len(diff._data):
            return DeltaUpdate(diff)

        return None
Exemple #7
0
    def _diff(self, struct: T.Struct, key: str, prop: T.Property,
              context: Context, diff: StructProxy) -> Optional[DeltaUpdate]:
        # PERF accessing the properties from the synchronized_properties is **far** cheaper that iterating over
        # _data and the getting the properties with
        #   member_property = struct.bl_rna.properties[k]
        # line to which py-spy attributes 20% of the total diff !
        try:
            if prop is not None:
                context.visit_state.path.append(key)
            properties = context.synchronized_properties.properties(struct)
            properties = specifics.conditional_properties(struct, properties)
            for k, member_property in properties:
                # TODO in test_differential.StructDatablockRef.test_remove
                # target et a scene, k is world and v (current world value) is None
                # so diff fails. v should be a BpyIDRefNoneProxy

                # make a difference between None value and no member
                try:
                    member = getattr(struct, k)
                except AttributeError:
                    logger.warning(f"diff: unknown attribute {k} in {struct}")
                    continue

                proxy_data = self._data.get(k)
                delta = diff_attribute(member, k, member_property, proxy_data,
                                       context)

                if delta is not None:
                    diff._data[k] = delta
        finally:
            if prop is not None:
                context.visit_state.path.pop()

        # TODO detect media updates (reload(), and attach a media descriptor to diff)
        # difficult ?

        # if anything has changed, wrap the hollow proxy in a DeltaUpdate. This may be superfluous but
        # it is homogenous with additions and deletions
        if len(diff._data):
            return DeltaUpdate(diff)

        return None
Exemple #8
0
    def load(self, bl_instance: Any, parent_key: Union[int, str],
             context: Context):
        """
        Load a Blender object into this proxy
        """
        self._data.clear()
        properties = context.synchronized_properties.properties(bl_instance)
        # includes properties from the bl_rna only, not the "view like" properties like MeshPolygon.edge_keys
        # that we do not want to load anyway
        properties = specifics.conditional_properties(bl_instance, properties)
        try:
            context.visit_state.path.append(parent_key)
            for name, bl_rna_property in properties:
                attr = getattr(bl_instance, name)
                attr_value = read_attribute(attr, name, bl_rna_property,
                                            context)

                # Also write None values. We use them to reset attributes like Camera.dof.focus_object
                self._data[name] = attr_value
        finally:
            context.visit_state.path.pop()

        return self