Привет. Вот код, ничего не меняй, только добавь, чтобы в принт выводились те данные, которые изменялись, а именно: hex оффсеты каждого блока координат:

bl_info = {
    "name": "Track F1 Vis Editor",
    "author": "AlonsoBeast",
    "version": (1, 0),
    "blender": (3, 3, 0),
    "location": "View3D > Sidebar > F1 Track Builder Toolkit",
    "description": "Imports and Exports track.vis files and creates Empty objects from coordinates",
    "warning": "",
    "wiki_url": "",
    "category": "Import-Export",
}

import bpy
import struct
import math

class ImportVisOperator(bpy.types.Operator):
    bl_idname = "import_mesh.vis"
    bl_label = "Import .vis"
    bl_options = {'REGISTER', 'UNDO'}

    filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    def execute(self, context):
        # Убираем подписчика на изменение, если он есть
        if check_object_changes in bpy.app.handlers.depsgraph_update_post:
            bpy.app.handlers.depsgraph_update_post.remove(check_object_changes)

        try:
            with open(self.filepath, 'rb') as file:
                file.seek(0xBE800)
                coordinates = []
                while True:
                    chunk = file.read(16)
                    if len(chunk) < 16:
                        break
                    
                    x_hex, y_hex, z_hex, separator = struct.unpack('4s4s4s4s', chunk)

                    try:
                        x = struct.unpack('<f', x_hex)[0]
                        y = struct.unpack('<f', y_hex)[0]
                        z = struct.unpack('<f', z_hex)[0]

                        if (x != 0.0 and not math.isnan(x) and not math.isinf(x) and
                            y != 0.0 and not math.isnan(y) and not math.isinf(y) and
                            z != 0.0 and not math.isnan(z) and not math.isinf(z)):
                            coordinates.append((x, z, y, separator))  

                    except struct.error as e:
                        self.report({'ERROR'}, f"Struct error: {str(e)}")
                        continue
                    except ValueError as e:
                        self.report({'ERROR'}, f"Value error: {str(e)}")
                        continue

                bpy.ops.object.select_all(action='DESELECT')
                
                # Create empty objects without change marker set immediately
                for x, z, y, separator in coordinates:
                    empty = bpy.data.objects.new("Empty", None)
                    empty.empty_display_type = 'PLAIN_AXES'
                    empty.location = (x, z, y)
                    empty['original_location'] = (x, z, y)
                    empty['is_changed'] = False  # Initial state: not changed
                    context.collection.objects.link(empty)

            self.report({'INFO'}, "Values successfully parsed and objects created")
            
            # Возвращаем подписчика после импорта
            bpy.app.handlers.depsgraph_update_post.append(check_object_changes)
            return {'FINISHED'}

        except Exception as e:
            self.report({'ERROR'}, f"File read error: {str(e)}")
            return {'CANCELLED'}

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

def check_object_changes(scene):
    for obj in scene.objects:
        if obj.type != 'EMPTY':
            continue
        original_location = obj.get('original_location')
        if original_location:
            current_location = obj.location
            # Проверка изменений по сравнению с оригинальными координатами
            is_changed = (current_location.x != original_location[0] or
                          current_location.y != original_location[1] or
                          current_location.z != original_location[2])
            obj['is_changed'] = is_changed

class ExportVisOperator(bpy.types.Operator):
    bl_idname = "export_mesh.vis"
    bl_label = "Export .vis"
    bl_options = {'REGISTER', 'UNDO'}

    filepath: bpy.props.StringProperty(subtype="FILE_PATH")

    def execute(self, context):
        try:
            with open(self.filepath, 'r+b') as file:
                file.seek(0xBE800)
                
                for obj in context.collection.objects:
                    if obj.type == 'EMPTY' and obj.get('is_changed', False):
                        x, z, y = obj.location

                        # Записываем только если изменено
                        file.write(struct.pack('<f', x))  # X
                        file.write(struct.pack('<f', z))  # Y (временно Z в Blender)
                        file.write(struct.pack('<f', y))  # Z (временно Y в Blender)

                        separator = file.read(4)
                        file.seek(-4, os.SEEK_CUR) 
                        file.write(separator)
                    else:
                        file.seek(16, os.SEEK_CUR)  # Skip this data block

            self.report({'INFO'}, "Values successfully exported")
            return {'FINISHED'}

        except Exception as e:
            self.report({'ERROR'}, f"File write error: {str(e)}")
            return {'CANCELLED'}

    def invoke(self, context, event):
        context.window_manager.fileselect_add(self)
        return {'RUNNING_MODAL'}

class F1TrackBuilderPanel(bpy.types.Panel):
    bl_label = "F1 Track Vis Editor"
    bl_idname = "VIEW3D_PT_f1_track_builder"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'F1 Track Builder Toolkit'

    def draw(self, context):
        layout = self.layout
        layout.operator(ImportVisOperator.bl_idname, text="Import .vis")
        layout.operator(ExportVisOperator.bl_idname, text="Export .vis")

def register():
    bpy.utils.register_class(ImportVisOperator)
    bpy.utils.register_class(ExportVisOperator)
    bpy.utils.register_class(F1TrackBuilderPanel)
    bpy.app.handlers.depsgraph_update_post.append(check_object_changes)

def unregister():
    bpy.utils.unregister_class(ImportVisOperator)
    bpy.utils.unregister_class(ExportVisOperator)
    bpy.utils.unregister_class(F1TrackBuilderPanel)
    bpy.app.handlers.depsgraph_update_post.remove(check_object_changes)

if __name__ == "__main__":
    register()