Изменения значения interpolation_duration на 10.0 не даёт перехода в 10 секунд, по факту переход гораздо быстрее. При переключении с 3rd на 1st нету плавного проезда камеры, переход происходит моментально. Возможно нужно добавить какие-то переменные для хранения параметров камеры (position, yaw, pitch, distance, height) и даже добавить больше уточняющих параметров.

# --- Start of main.py ---
import pygame
import math
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from plane_module import Plane
from game_objects import FBXModel
from interface_module import GameInterface

pygame.init()
screen = pygame.display.set_mode((800, 600), DOUBLEBUF | OPENGL)
pygame.display.set_caption("3D Shooter with OpenGL")

glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_COLOR_MATERIAL)
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

glLightfv(GL_LIGHT0, GL_POSITION, (0, 100, 0, 1))
glLightfv(GL_LIGHT0, GL_AMBIENT, (0.2, 0.2, 0.2, 1))
glLightfv(GL_LIGHT0, GL_DIFFUSE, (0.8, 0.8, 0.8, 1))

player_x, player_y, player_z = 0, 0, 0
move_speed = 0.8  # Скорость движения в обычном режиме
jump_force = 1.5
gravity = 0.08

camera_distance = 20
camera_height = 15
camera_pitch = 0
camera_yaw = 0
mouse_sensitivity = 0.2
min_pitch, max_pitch = -89, 89

# Флаг для режима камеры от третьего лица
third_person_mode = False

plane = Plane(size=20000, grid=50, heightmap="Height_Map.exr", diffusemap="Diffuse_Map.exr", height_scale=20000)
model = FBXModel(model_path="characterMedium.obj", position=[player_x, player_y + 50, player_z], scale=15)
interface = GameInterface(minimap_filename="mini_map.png", minimap_size=200)

noclip_mode = False
third_person_camera_height = 10
first_person_offset_forward = 1  # Смещение вперёд (в метрах)
first_person_offset_up = 2       # Смещение вверх (в метрах)

# Добавим новые переменные для хранения начальных и целевых значений параметров камеры
initial_camera_params = {
    'position': model.position.copy(),
    'yaw': camera_yaw,
    'pitch': camera_pitch,
    'distance': camera_distance,
    'height': camera_height
}

target_camera_params = initial_camera_params.copy()

# Переменная для отслеживания прогресса интерполяции
interpolation_progress = 0.0
interpolation_duration = 0.5  # Длительность интерполяции в секундах

def set_camera(player_x, player_y, player_z, yaw, pitch, distance, height, interpolation_factor=1.0):
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(60, (800 / 600), 0.1, 10000.0)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    if third_person_mode:
        # Режим камеры от третьего лица
        look_x = player_x + math.sin(math.radians(yaw)) * math.cos(math.radians(pitch))
        look_y = player_y + math.sin(math.radians(pitch))
        look_z = player_z - math.cos(math.radians(yaw)) * math.cos(math.radians(pitch))

        # Позиция камеры от третьего лица
        camera_offset_x = player_x - math.sin(math.radians(yaw)) * distance
        camera_offset_z = player_z + math.cos(math.radians(yaw)) * distance
        camera_offset_y = player_y + height

        gluLookAt(
            camera_offset_x, camera_offset_y, camera_offset_z,
            look_x, look_y, look_z,  # Цель камеры смотрит на игрока
            0, 1, 0  # Вектор "вверх" остаётся неизменным
        )
    else:
        # Режим камеры от первого лица
        # Расчёт смещения камеры
        camera_offset_x = player_x + math.sin(math.radians(yaw)) * first_person_offset_forward
        camera_offset_z = player_z - math.cos(math.radians(yaw)) * first_person_offset_forward
        camera_offset_y = player_y + first_person_offset_up

        look_x = camera_offset_x + math.sin(math.radians(yaw)) * math.cos(math.radians(pitch))
        look_y = camera_offset_y + math.sin(math.radians(pitch))
        look_z = camera_offset_z - math.cos(math.radians(yaw)) * math.cos(math.radians(pitch))

        gluLookAt(
            camera_offset_x, camera_offset_y, camera_offset_z,
            look_x, look_y, look_z,
            0, 1, 0
        )

clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
pygame.event.set_grab(True)
vertical_momentum = 0
is_grounded = True
running = True

while running:
    dt = clock.tick(60) * 0.1

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glClearColor(0.53, 0.81, 0.98, 1)

    # Обработка интерполяции
    if interpolation_progress < interpolation_duration:
        interpolation_progress += dt
        factor = interpolation_progress / interpolation_duration

        # Интерполяция параметров камеры
        interpolated_camera_params = {
            'position': [
                initial_camera_params['position'][0] * (1 - factor) + target_camera_params['position'][0] * factor,
                initial_camera_params['position'][1] * (1 - factor) + target_camera_params['position'][1] * factor,
                initial_camera_params['position'][2] * (1 - factor) + target_camera_params['position'][2] * factor
            ],
            'yaw': initial_camera_params['yaw'] * (1 - factor) + target_camera_params['yaw'] * factor,
            'pitch': initial_camera_params['pitch'] * (1 - factor) + target_camera_params['pitch'] * factor,
            'distance': initial_camera_params['distance'] * (1 - factor) + target_camera_params['distance'] * factor,
            'height': initial_camera_params['height'] * (1 - factor) + target_camera_params['height'] * factor
        }

        set_camera(
            interpolated_camera_params['position'][0],
            interpolated_camera_params['position'][1],
            interpolated_camera_params['position'][2],
            interpolated_camera_params['yaw'],
            interpolated_camera_params['pitch'],
            interpolated_camera_params['distance'],
            interpolated_camera_params['height'],
            factor
        )
    else:
        # Установка камеры без интерполяции
        set_camera(*model.position, camera_yaw, camera_pitch, camera_distance, camera_height)

    glPushMatrix()
    plane.render()
    model.render()  # Рендерим модель вместо куба
    glPopMatrix()

    glDisable(GL_DEPTH_TEST)
    interface.render_minimap(model.position[0], model.position[2], plane_size=20000)
    glEnable(GL_DEPTH_TEST)

    for event in pygame.event.get():
        if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_n:  # Переключение режима noclip
            noclip_mode = not noclip_mode
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_f:  # Переключение режима камеры
            third_person_mode = not third_person_mode

            # Сохраняем начальные параметры камеры
            initial_camera_params = {
                'position': model.position.copy(),
                'yaw': camera_yaw,
                'pitch': camera_pitch,
                'distance': camera_distance,
                'height': camera_height
            }

            # Устанавливаем целевые параметры камеры
            if third_person_mode:
                target_camera_params = {
                    'position': model.position.copy(),
                    'yaw': camera_yaw,
                    'pitch': camera_pitch,
                    'distance': camera_distance,
                    'height': third_person_camera_height
                }
            else:
                target_camera_params = {
                    'position': model.position.copy(),
                    'yaw': camera_yaw,
                    'pitch': camera_pitch,
                    'distance': first_person_offset_forward,
                    'height': first_person_offset_up
                }

            # Сбрасываем прогресс интерполяции
            interpolation_progress = 0.0

    dx, dy = pygame.mouse.get_rel()
    camera_yaw += dx * mouse_sensitivity
    camera_pitch -= dy * mouse_sensitivity
    camera_pitch = max(min_pitch, min(max_pitch, camera_pitch))

    keys = pygame.key.get_pressed()
    current_move_speed = move_speed * 10 if noclip_mode else move_speed

    move_forward = (keys[pygame.K_w] - keys[pygame.K_s]) * current_move_speed * dt
    move_strafe = (keys[pygame.K_d] - keys[pygame.K_a]) * current_move_speed * dt

    angle = math.radians(camera_yaw)
    forward_x = math.sin(angle)
    forward_z = -math.cos(angle)
    strafe_x = math.cos(angle)
    strafe_z = math.sin(angle)

    old_model_position = model.position.copy()

    if noclip_mode:
        new_x = model.position[0] + forward_x * move_forward + strafe_x * move_strafe
        new_z = model.position[2] + forward_z * move_forward + strafe_z * move_strafe
        vertical_momentum = 0
        new_y = model.position[1] + (keys[pygame.K_SPACE] - keys[pygame.K_LCTRL]) * current_move_speed * dt
        model.update_position([new_x, new_y, new_z])
    else:
        new_x = model.position[0] + forward_x * move_forward + strafe_x * move_strafe
        new_z = model.position[2] + forward_z * move_forward + strafe_z * move_strafe

        terrain_height = plane.get_height(new_x, new_z)
        if model.position[1] < terrain_height:
            model.position[1] = terrain_height
            vertical_momentum = 0
            is_grounded = True

        if not is_grounded:
            vertical_momentum -= gravity * dt
            model.position[1] += vertical_momentum
        else:
            vertical_momentum = 0

        if is_grounded and keys[pygame.K_SPACE]:
            vertical_momentum = jump_force
            is_grounded = False

        model.update_position([new_x, model.position[1], new_z])

    pygame.display.flip()

pygame.quit()
# --- End of main.py ---