-----------------------------------------------------------------------------
-- Camera Manager для 3ds Max
-- Удобная панель управления камерами сцены:
--   - список всех камер сцены
--   - переключение активного вьюпорта на выбранную камеру
--   - создание Target / Free камер из текущего вида
--   - удаление, переименование, дублирование камер
--   - изменение фокусного расстояния (мм) и FOV
--   - индивидуальное разрешение рендера для каждой камеры (сохраняется
--     в сцене и может автоматически применяться к рендереру при
--     переключении на камеру)
--   - включение/выключение Safe Frames
--   - быстрый переход между камерами (Prev/Next)
--
-- Установка: Drag & Drop файла .ms в окно 3ds Max,
-- либо MAXScript > Run Script...
-----------------------------------------------------------------------------

global CameraManagerRollout

try (destroyDialog CameraManagerRollout) catch()

rollout CameraManagerRollout "Camera Manager" width:300
(
    listBox lst_cameras "Камеры сцены:" height:12 width:280
    button btn_refresh "Обновить список" width:280 across:1

    group "Переключение вида"
    (
        button btn_setActive "Смотреть через выбранную камеру" width:280
        button btn_prev "<< Пред." width:135 across:2
        button btn_next "След. >>" width:135
    )

    group "Создание / удаление"
    (
        button btn_createTarget "Создать Target-камеру из вида" width:280
        button btn_createFree   "Создать Free-камеру из вида"   width:280
        button btn_duplicate    "Дублировать выбранную"         width:280
        button btn_rename       "Переименовать..."              width:135 across:2
        button btn_delete       "Удалить"                       width:135
    )

    group "Параметры камеры"
    (
        spinner spn_lens  "Фокус (мм):" range:[3,1200,43] fieldWidth:60 across:2
        spinner spn_fov   "FOV (град):" range:[1,175,45]   fieldWidth:60
        checkbox chk_safeframe "Показывать Safe Frames" checked:false
    )

    group "Разрешение рендера камеры"
    (
        spinner spn_resW "Ширина:"  range:[4,16000,1920] type:#integer fieldWidth:60 across:2
        spinner spn_resH "Высота:"  range:[4,16000,1080] type:#integer fieldWidth:60
        dropdownList ddl_presets "Пресет:" items:#("Custom", "1920 x 1080 (FullHD)", \
            "3840 x 2160 (4K UHD)", "1280 x 720 (HD)", "2048 x 1080 (2K DCI)", \
            "4096 x 2160 (4K DCI)", "1000 x 1000 (Square)", "2000 x 3000 (Print Portrait)") width:280
        button btn_saveRes    "Сохранить разрешение для камеры" width:280
        button btn_applyRes   "Применить разрешение к рендеру сейчас" width:280
        checkbox chk_autoApplyRes "Авто-применение при переключении на камеру" checked:true
    )

    -----------------------------------------------------------------------
    -- вспомогательные функции
    -----------------------------------------------------------------------

    fn getSceneCameras =
    (
        for c in cameras where isValidNode c collect c
    )

    fn refreshCameraList =
    (
        local cams = getSceneCameras()
        lst_cameras.items = for c in cams collect c.name
        lst_cameras.selection = 0
    )

    fn getSelectedCamera =
    (
        local cams = getSceneCameras()
        local idx = lst_cameras.selection
        if idx > 0 and idx <= cams.count then cams[idx] else undefined
    )

    fn syncSpinnersWithCamera cam =
    (
        if cam != undefined and isProperty cam #fov do
        (
            spn_fov.value = (cam.fov as integer)
            if isProperty cam #lens do spn_lens.value = (cam.lens as integer)
        )
    )

    -- разрешение рендера хранится в UserProp камеры (переживает сохранение сцены)
    fn getStoredResolution cam =
    (
        local w = undefined
        local h = undefined
        if cam != undefined then
        (
            local wStr = getUserProp cam "RenderWidth"
            local hStr = getUserProp cam "RenderHeight"
            if wStr != undefined do w = wStr as integer
            if hStr != undefined do h = hStr as integer
        )
        -- если для камеры ничего не сохранено - используем текущее разрешение рендерера
        if w == undefined do w = renderWidth
        if h == undefined do h = renderHeight
        #(w, h)
    )

    fn setStoredResolution cam w h =
    (
        if cam != undefined do
        (
            setUserProp cam "RenderWidth" (w as string)
            setUserProp cam "RenderHeight" (h as string)
        )
    )

    fn applyResolutionToRenderer w h =
    (
        renderWidth = w
        renderHeight = h
    )

    fn syncResolutionSpinnersWithCamera cam =
    (
        local res = getStoredResolution cam
        spn_resW.value = res[1]
        spn_resH.value = res[2]
        ddl_presets.selection = 1
    )

    -----------------------------------------------------------------------
    -- обработчики событий
    -----------------------------------------------------------------------

    on CameraManagerRollout open do
    (
        refreshCameraList()
    )

    on btn_refresh pressed do
    (
        refreshCameraList()
    )

    on lst_cameras selected itm do
    (
        local cam = getSelectedCamera()
        if cam != undefined do
        (
            clearSelection()
            selectMore cam
            syncSpinnersWithCamera cam
            syncResolutionSpinnersWithCamera cam
        )
    )

    on btn_setActive pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            viewport.setCamera cam
            if chk_autoApplyRes.checked do
            (
                local res = getStoredResolution cam
                applyResolutionToRenderer res[1] res[2]
                syncResolutionSpinnersWithCamera cam
            )
            completeRedraw()
        )
        else messageBox "Сначала выберите камеру в списке."
    )

    on btn_prev pressed do
    (
        local cnt = lst_cameras.items.count
        if cnt > 0 do
        (
            local idx = lst_cameras.selection - 1
            if idx < 1 then idx = cnt
            lst_cameras.selection = idx
            local cam = getSelectedCamera()
            clearSelection(); selectMore cam
            syncSpinnersWithCamera cam
            syncResolutionSpinnersWithCamera cam
            viewport.setCamera cam
            if chk_autoApplyRes.checked do
            (
                local res = getStoredResolution cam
                applyResolutionToRenderer res[1] res[2]
            )
            completeRedraw()
        )
    )

    on btn_next pressed do
    (
        local cnt = lst_cameras.items.count
        if cnt > 0 do
        (
            local idx = lst_cameras.selection + 1
            if idx > cnt then idx = 1
            lst_cameras.selection = idx
            local cam = getSelectedCamera()
            clearSelection(); selectMore cam
            syncSpinnersWithCamera cam
            syncResolutionSpinnersWithCamera cam
            viewport.setCamera cam
            if chk_autoApplyRes.checked do
            (
                local res = getStoredResolution cam
                applyResolutionToRenderer res[1] res[2]
            )
            completeRedraw()
        )
    )

    on btn_createTarget pressed do
    (
        local vp = viewport.getViewSize()
        local tCam = Targetcamera transform:(viewport.getTM())
        tCam.name = uniquename "Camera"
        setStoredResolution tCam renderWidth renderHeight
        max modify mode
        refreshCameraList()
    )

    on btn_createFree pressed do
    (
        local fCam = Freecamera transform:(inverse (viewport.getTM()))
        fCam.name = uniquename "Camera"
        setStoredResolution fCam renderWidth renderHeight
        refreshCameraList()
    )

    on btn_duplicate pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            local newCam = copy cam
            newCam.name = uniquename (cam.name + "_copy")
            refreshCameraList()
        )
        else messageBox "Сначала выберите камеру в списке."
    )

    on btn_rename pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            local newName = getKBValue prompt:"Новое имя камеры:" default:cam.name
            if newName != undefined and newName != "" do
            (
                cam.name = newName
                refreshCameraList()
            )
        )
        else messageBox "Сначала выберите камеру в списке."
    )

    on btn_delete pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            if queryBox ("Удалить камеру \"" + cam.name + "\"?") title:"Подтверждение" do
            (
                delete cam
                refreshCameraList()
            )
        )
        else messageBox "Сначала выберите камеру в списке."
    )

    on spn_lens changed val do
    (
        local cam = getSelectedCamera()
        if cam != undefined and isProperty cam #lens do
        (
            cam.lens = val
            spn_fov.value = (cam.fov as integer)
        )
    )

    on spn_fov changed val do
    (
        local cam = getSelectedCamera()
        if cam != undefined and isProperty cam #fov do
        (
            cam.fov = val
            if isProperty cam #lens do spn_lens.value = (cam.lens as integer)
        )
    )

    on ddl_presets selected itm do
    (
        case ddl_presets.selection of
        (
            2: ( spn_resW.value = 1920; spn_resH.value = 1080 )  -- FullHD
            3: ( spn_resW.value = 3840; spn_resH.value = 2160 )  -- 4K UHD
            4: ( spn_resW.value = 1280; spn_resH.value = 720  )  -- HD
            5: ( spn_resW.value = 2048; spn_resH.value = 1080 )  -- 2K DCI
            6: ( spn_resW.value = 4096; spn_resH.value = 2160 )  -- 4K DCI
            7: ( spn_resW.value = 1000; spn_resH.value = 1000 )  -- Square
            8: ( spn_resW.value = 2000; spn_resH.value = 3000 )  -- Print Portrait
            default: ()
        )
    )

    on btn_saveRes pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            setStoredResolution cam spn_resW.value spn_resH.value
            messageBox ("Разрешение " + (spn_resW.value as string) + " x " + \
                (spn_resH.value as string) + " сохранено для камеры \"" + cam.name + "\".")
        )
        else messageBox "Сначала выберите камеру в списке."
    )

    on btn_applyRes pressed do
    (
        local cam = getSelectedCamera()
        if cam != undefined then
        (
            applyResolutionToRenderer spn_resW.value spn_resH.value
            messageBox "Разрешение применено к рендереру."
        )
        else
        (
            -- камера не выбрана - просто применяем значения из спиннеров
            applyResolutionToRenderer spn_resW.value spn_resH.value
        )
    )

    on chk_safeframe changed state do
    (
        local vp = viewport.activeViewport
        displaySafeFrames = state
        completeRedraw()
    )
)

createDialog CameraManagerRollout
