Привет, сравни код, пожалуйста. Вот первый,потом второй скину:


bl_info = {
    "name": "F1 Track ReduceMechanics Maker ",
    "author": "AlonsoBeast",
    "version": (1, 0, 2),
    "blender": (3, 3, 0),
    "location": "View3D > Sidebar > F1 Track Builder Toolkit",
    "category": "3D View",
}

import struct
import bpy
from bpy.types import Operator
from mathutils import Matrix, Vector
from bpy.types import Panel



class F1ImportTrackOperator(bpy.types.Operator):
    """Import F1 Track"""
    bl_idname = "f1.import_track"
    bl_label = "Import Track"
    bl_options = {'REGISTER', 'UNDO'}

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

    def execute(self, context):
        import_track(self.filepath)
        return {'FINISHED'}

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

class F1ExportTrackOperator(bpy.types.Operator):
    """Export F1 Track"""
    bl_idname = "f1.export_track"
    bl_label = "Export Track"
    bl_options = {'REGISTER', 'UNDO'}

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

    def execute(self, context):
        try:
            export_track(self.filepath)
            self.report({'INFO'}, "Track exported successfully.")  # Успешный экспорт
        except FileNotFoundError:
            self.report({'WARNING'}, "IMPORTANT! Select the original track.rm and save.")  # Ошибка файла
        return {'FINISHED'}

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

def import_track(filename):
    left_points = []
    right_points = []

    with open(filename, 'rb') as f:
        version = struct.unpack('i', f.read(4))[0]

        while True:
            gate_data = f.read(17)
            if not gate_data:
                break
            
            x = struct.unpack('BBB', gate_data[0:3])
            y = struct.unpack('BBB', gate_data[3:6])
            z = struct.unpack('BBB', gate_data[6:9])
            xo = struct.unpack('b', gate_data[9:10])[0]
            yo = struct.unpack('b', gate_data[10:11])[0]
            zo = struct.unpack('b', gate_data[11:12])[0]
            racingAcrossProportion = struct.unpack('B', gate_data[12:13])[0]
            leftNormalX = struct.unpack('b', gate_data[13:14])[0]
            leftNormalZ = struct.unpack('b', gate_data[14:15])[0]
            rightNormalX = struct.unpack('b', gate_data[15:16])[0]
            rightNormalZ = struct.unpack('b', gate_data[16:17])[0]

            # Decompress data
            bX = ((x[0] << 16) + (x[1] << 8) + x[2] - 0x800000) / 200.0
            bY = ((y[0] << 16) + (y[1] << 8) + y[2] - 0x800000) / 200.0
            bZ = ((z[0] << 16) + (z[1] << 8) + z[2] - 0x800000) / 200.0
            
            oX = xo / 50.0
            oY = yo / 100.0
            oZ = zo / 50.0
            
            lx = bX - oX
            ly = bY - oY
            lz = bZ - oZ
            
            rx = bX + oX
            ry = bY + oY
            rz = bZ + oZ
            
            # Печатаем оригинальные точки
            print(f"Original Left Point: ({lx}, {ly}, {lz})")
            print(f"Original Right Point: ({rx}, {ry}, {rz})")
            
            left_points.append(Vector((lx, ly, lz)))
            right_points.append(Vector((rx, ry, rz)))

    # Создаем меш
    mesh_object = create_mesh_from_splines(left_points, right_points)
    
    # Применяем зеркалирование только к mesh_object без влияния на другие объекты
    apply_mirror_to_object(mesh_object)

def apply_mirror_to_object(obj):
    # Устанавливаем объект как активный
    bpy.context.view_layer.objects.active = obj
    
    # Убеждаемся, что только этот объект выделен
    bpy.ops.object.select_all(action='DESELECT')
    obj.select_set(True)
    
    # Переключаемся в режим объекта
    bpy.ops.object.mode_set(mode='OBJECT')
    
    # Применяем зеркалирование по глобальной оси Y
    bpy.ops.transform.mirror(orient_type='GLOBAL', constraint_axis=(False, True, False))
    
    # Снимаем выделение
    obj.select_set(False)

def create_mesh_from_splines(left_points, right_points):
    if len(left_points) != len(right_points):
        raise ValueError("Left and right point lists must be the same length.")

    vertices = []
    edges = []
    faces = []

    for i in range(len(left_points)):
        left_coord = left_points[i]
        right_coord = right_points[i]

        vertices.append((left_coord.x, left_coord.z, left_coord.y))  # X, Z, Y
        vertices.append((right_coord.x, right_coord.z, right_coord.y))  # X, Z, Y

        if i < len(left_points) - 1:
            idx1 = i * 2
            idx2 = idx1 + 1
            idx3 = idx1 + 2
            idx4 = idx3 + 1

            edges.append((idx1, idx2))
            faces.append((idx1, idx2, idx4, idx3))

    mesh_data = bpy.data.meshes.new("RaceTrackMesh")
    mesh_data.from_pydata(vertices, edges, faces)
    mesh_data.update()

    mesh_object = bpy.data.objects.new("RaceTrack", mesh_data)
    bpy.context.collection.objects.link(mesh_object)

    return mesh_object

def apply_global_y_mirror(obj):
    bpy.context.view_layer.objects.active = obj
    obj.select_set(True)

    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.transform.mirror(orient_type='GLOBAL', constraint_axis=(False, True, False))
    obj.select_set(False)

def export_track(filepath):
    if "RaceTrack" not in bpy.data.objects:
        print("RaceTrack object not found.")
        return

    obj = bpy.data.objects["RaceTrack"]
    mesh = obj.data

    # Получаем вершины в глобальных координатах
    vertices = [obj.matrix_world @ vert.co for vert in mesh.vertices]

    # Проверяем количество вершин (должно быть чётным)
    if len(vertices) % 2 != 0:
        raise ValueError("Number of vertices must be even (pairs of left and right points).")

    try:
        with open(filepath, 'r+b') as f:  # Открываем файл в режиме чтения и записи
            f.seek(0)
            version = struct.unpack('i', f.read(4))[0]  # Читаем версию файла

            for i in range(0, len(vertices), 2):
                # Берём пару вершин
                vert1 = vertices[i]
                vert2 = vertices[i + 1]

                # Вычисляем центр между точками
                center = (vert1 + vert2) / 2

                # Определяем левую и правую точки на основе их положения относительно центра
                if vert1.x < vert2.x:
                    left_coord = vert1
                    right_coord = vert2
                else:
                    left_coord = vert2
                    right_coord = vert1

                # Учитываем поворот на -90° по X
                rotation_matrix = Matrix.Rotation(-1.5708, 4, 'X')  # -90° по X
                left_rotated = rotation_matrix @ Vector((left_coord.x, left_coord.y, left_coord.z))
                right_rotated = rotation_matrix @ Vector((right_coord.x, right_coord.y, right_coord.z))

                # Вычисляем базовые координаты и смещения
                oX = (right_rotated.x - left_rotated.x) * 0.5
                oY = (right_rotated.y - left_rotated.y) * 0.5
                oZ = (right_rotated.z - left_rotated.z) * 0.5

                bX = left_rotated.x + oX
                bY = left_rotated.y + oY
                bZ = left_rotated.z + oZ

                # Кодируем координаты для записи
                bX = int(bX * 200 + 0x800000)
                bY = int(bY * 200 + 0x800000)
                bZ = int(bZ * 200 + 0x800000)

                # Позиция в файле: 4 байта версия + 17 байт на каждую запись
                f.seek(4 + (i // 2) * 17)

                # Записываем данные
                f.write(struct.pack('BBB', (bX >> 16) & 0xFF, (bX >> 8) & 0xFF, bX & 0xFF))
                f.write(struct.pack('BBB', (bY >> 16) & 0xFF, (bY >> 8) & 0xFF, bY & 0xFF))
                f.write(struct.pack('BBB', (bZ >> 16) & 0xFF, (bZ >> 8) & 0xFF, bZ & 0xFF))
                f.write(struct.pack('b', int(oX * 50)))
                f.write(struct.pack('b', int(oY * 100)))
                f.write(struct.pack('b', int(oZ * 50)))
                f.write(struct.pack('B', 128))  # racingAcrossProportion (по умолчанию)
                f.write(struct.pack('b', 0))  # leftNormalX
                f.write(struct.pack('b', 0))  # leftNormalZ
                f.write(struct.pack('b', 0))  # rightNormalX
                f.write(struct.pack('b', 0))  # rightNormalZ

                print(f"Writing: Left ({left_rotated}), Right ({right_rotated})")

        print("Export completed successfully.")

    except FileNotFoundError:
        raise FileNotFoundError("File not found.")  # Передаем ошибку вверх

class TRANSFER_OT_spline_to_mesh(Operator):
    """Transfer Track_Line Spline to RaceTrack Mesh by reshaping existing RaceTrack"""
    bl_idname = "transfer.spline_to_mesh"
    bl_label = "Transfer Spline to Mesh"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        # Получаем имя объекта Track_Line из StringProperty
        track_line_name = context.scene.track_line_object
        
        # Получаем сам объект по имени
        track_line_object = bpy.data.objects.get(track_line_name)

        if track_line_object and track_line_object.type == 'CURVE':
            # Устанавливаем Track_Line как активный объект
            bpy.context.view_layer.objects.active = track_line_object
            track_line_object.select_set(True)  # Выделяем объект
            
            # Снимаем выделение со всех остальных объектов
            for obj in bpy.context.selected_objects:
                if obj != track_line_object:
                    obj.select_set(False)

            # Преобразуем кривую Track_Line в меш
            bpy.ops.object.convert(target='MESH')
            new_mesh_object = bpy.context.active_object

            if new_mesh_object and new_mesh_object.type == 'MESH':
                new_vertices = [v.co.copy() for v in new_mesh_object.data.vertices]

                # Получаем RaceTrack
                race_track_name = "RaceTrack"
                race_track_object = bpy.data.objects.get(race_track_name)

                if not race_track_object or race_track_object.type != 'MESH':
                    self.report({'ERROR'}, "RaceTrack object not found or is not a MESH.")
                    return {'CANCELLED'}

                race_track_vertices = race_track_object.data.vertices
                race_track_count = len(race_track_vertices)

                # Проверка на количество вершин
                if race_track_count == 0 or len(new_vertices) < 2:
                    self.report({'ERROR'}, "RaceTrack has no vertices or Track_Line has less than 2 vertices.")
                    return {'CANCELLED'}

                # Вычисляем общую длину сплайна
                spline_length = 0.0
                for i in range(len(new_vertices) - 1):
                    spline_length += (new_vertices[i + 1] - new_vertices[i]).length

                # Количество пар вершин в RaceTrack (левые и правые)
                race_track_pairs = race_track_count // 2
                if race_track_count % 2 != 0:
                    self.report({'WARNING'}, "RaceTrack vertex count is odd, last vertex will be ignored.")

                # Шаг вдоль сплайна для каждой пары точек
                step_length = spline_length / (race_track_pairs - 1) if race_track_pairs > 1 else spline_length

                # Обновление координат RaceTrack
                current_distance = 0.0
                spline_index = 0  # Текущий сегмент сплайна

                for pair_idx in range(race_track_pairs):
                    # Находим позицию вдоль сплайна
                    target_distance = pair_idx * step_length

                    # Ищем сегмент сплайна, в который попадает target_distance
                    while spline_index < len(new_vertices) - 1:
                        segment_start = new_vertices[spline_index]
                        segment_end = new_vertices[spline_index + 1]
                        segment_length = (segment_end - segment_start).length

                        if current_distance + segment_length >= target_distance or spline_index == len(new_vertices) - 2:
                            # Интерполируем точку на сегменте
                            segment_progress = (target_distance - current_distance) / segment_length if segment_length > 0 else 0
                            segment_progress = min(max(segment_progress, 0.0), 1.0)  # Ограничиваем 0..1
                            current_point = segment_start.lerp(segment_end, segment_progress)
                            break
                        current_distance += segment_length
                        spline_index += 1

                    # Вычисляем направление и боковой вектор
                    if spline_index < len(new_vertices) - 1:
                        direction_vector = (new_vertices[spline_index + 1] - new_vertices[spline_index]).normalized()
                    else:
                        direction_vector = (new_vertices[-1] - new_vertices[-2]).normalized()

                    rotation_quaternion = direction_vector.to_track_quat('Z', 'Y')
                    side_vector = rotation_quaternion @ Vector((1, 0, 0)) * (context.scene.track_width / 2)

                    # Определяем левую и правую точки
                    left_vert = current_point + side_vector
                    right_vert = current_point - side_vector

                    # Зеркалирование по оси Y
                    left_vert.y = -left_vert.y
                    right_vert.y = -right_vert.y

                    # Записываем координаты в RaceTrack
                    left_idx = pair_idx * 2
                    right_idx = left_idx + 1

                    if left_idx < race_track_count:
                        race_track_vertices[left_idx].co = left_vert
                    if right_idx < race_track_count:
                        race_track_vertices[right_idx].co = right_vert

                # Обновляем меш
                race_track_object.data.update()

                self.report({'INFO'}, "RaceTrack reshaped successfully with uniform distribution.")
                return {'FINISHED'}

        self.report({'ERROR'}, "Track_Line not found or is not a curve.")
        return {'CANCELLED'}
        
class F1TrackBuilderPanel(bpy.types.Panel):
    """Creates a panel for the F1 Track Builder Toolkit"""
    bl_label = "F1 Track ReduceMechanics Maker"
    bl_idname = "PT_F1_Track_Builder"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'F1 Track Progress'

    @classmethod
    def poll(cls, context):
        return context.scene is not None

    def draw(self, context):
        layout = self.layout
        
        # Операторы импорта и экспорта
        layout.operator(F1ImportTrackOperator.bl_idname, text="Import Track", icon='IMPORT')
        layout.operator(F1ExportTrackOperator.bl_idname, text="Export Track", icon='EXPORT')
        
        layout.label(text="Convert Track_Line (ai_track.xml) to RaceTrack Mesh", icon='MOD_REMESH')
        
        # Поле для выбора Track_Line через StringProperty
        row = layout.row(align=True)
        row.prop_search(context.scene, "track_line_object", bpy.data, "objects", text="")
        
        # Кнопка переноса
        row.operator(TRANSFER_OT_spline_to_mesh.bl_idname, text="Transfer", icon='ARROW_LEFTRIGHT')

        # Поле для ширины трека
        #layout.prop(context.scene, "track_width", text="Width")
        

def register():
    bpy.utils.register_class(F1ImportTrackOperator)
    bpy.utils.register_class(F1ExportTrackOperator)
    bpy.utils.register_class(F1TrackBuilderPanel)
    bpy.utils.register_class(TRANSFER_OT_spline_to_mesh)
    
    # Используем StringProperty с дефолтным значением "Track_Line"....
    bpy.types.Scene.track_line_object = bpy.props.StringProperty(
        name="Track Line",
        default="Track_Line",
        description="Name of the Track_Line spline object"
    )
    bpy.types.Scene.track_width = bpy.props.FloatProperty(name="Track Width", default=3.0, min=0.0)

def unregister():
    bpy.utils.unregister_class(F1ImportTrackOperator)
    bpy.utils.unregister_class(F1ExportTrackOperator)
    bpy.utils.unregister_class(F1TrackBuilderPanel)
    bpy.utils.unregister_class(TRANSFER_OT_spline_to_mesh)

    del bpy.types.Scene.track_line_object
    del bpy.types.Scene.track_width
    

if __name__ == "__main__":
    register()