-- Octane Render Advanced Imported Animation Renderer: ORAIAR -- by Alexander Zubkov (contact@artdds.com) -- This script is a free software. There are no limitations on using and modifying this software. USE IT ON YOUR OWN RISK! -- Этот скрипт является свободной программой. Не предусмотрено никаких ограничений на использование и модификацию данного ПО, -- равно как и какой-либо отвественности, возлагаемой на авторов. ИСПОЛЬЗУЙТЕ НА СВОЙ СТРАХ И РИСК! -- Requirements: Octane Render Standalone 2.1+ -- @version "0.1 a" -- @shortcut Alt+A sVersion = "0.1a" -- Получаем root Graph (сцену) graph = octane.project.getSceneGraph() -- Получаем текущий Render Target (тот, какой рендерится в данный момент - уж звиняйте) rendertarget = octane.render.getRenderTargetNode() if (rendertarget == nil) then warnDialog = octane.gui.showDialog { type = 1, icon = 3, title = "Error!", text = "No render target selected!", buttonTexts = {"ОК"} } return end -- Получаем Render Passes renderpasses = rendertarget:getConnectedNode(octane.P_RENDER_PASSES) -- Получаем Render Kernel renderkernel = rendertarget:getConnectedNode(octane.P_KERNEL) -- Получаем временнОй диапазон timespan = graph:getAnimationTimeSpan() -- Получаем настройки проекта - пойдут в настройки рендера prefs = octane.project.getProjectSettings() -- Задаем таблицу типов файлов для рендера output_file_formats_text = {"Portable Network Graphics 8bpc", "Portable Network Graphics 16bpc", "OpenEXR Untonemapped", "OpenEXR Tonemapped" } output_file_extensions = {".png", ".png", ".exr", ".exr"} output_file_formats = { octane.render.imageType.PNG8, octane.render.imageType.PNG16, octane.render.imageType.EXR, octane.render.imageType.EXRTONEMAPPED} -- Цвет информационных подсказок info_colour = {210,210,210,100} -- Рендер отменен IS_CANCELLED = true -- Имя прогресс-бара function pbName (frame_num) local rt = "Active Render target: “"..rendertarget:getProperties().name.."”" local frames = "Frames "..tostring(iface_renderfrom:getProperties().value).."-"..tostring(iface_renderto:getProperties().value).." ("..tostring(iface_renderto:getProperties().value-iface_renderfrom:getProperties().value+1)..")" return (frame_num == nil) and (rt.." || "..frames) or "Rendering frame "..frame_num.."... || "..frames end -- СОЗДАЕМ ИНТЕРФЕЙС РЕНДЕРА -- -- Интерфейс выбора временного промежутка и информирования iface_label_renderfrom = octane.gui.create { -- Заголовок для начала рендера type = octane.gui.componentType.LABEL, text = "Start Frame:", } iface_renderfrom = octane.gui.create { -- С какого кадра начинать рендер type = octane.gui.componentType.SLIDER, name = "Start Frame", minValue = math.floor(timespan[1] / prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), maxValue = math.ceil(timespan[2] * prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), step = 1, value = math.floor(timespan[1] / prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), width = 400, } iface_label_renderto = octane.gui.create { -- Заголовок для конца рендера type = octane.gui.componentType.LABEL, text = "End Frame:", } iface_renderto = octane.gui.create { -- Каким кадром заканчивать рендер type = octane.gui.componentType.SLIDER, name = "End Frame", minValue = math.floor(timespan[1] / prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), maxValue = math.ceil(timespan[2] * prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), step = 1, value = math.ceil(timespan[2] * prefs:getAttribute(octane.A_FRAMES_PER_SECOND)), width = 400, } iface_label_info = octane.gui.create { -- Заголовок инфо-лейбла type = octane.gui.componentType.LABEL, text = "Animation info:", textColour = info_colour } iface_info = octane.gui.create { -- Текст инфо-лейбла type = octane.gui.componentType.LABEL, text = "FPS: "..prefs:getAttribute(octane.A_FRAMES_PER_SECOND)..", Shutter: "..prefs:getAttribute(octane.A_SHUTTER_TIME), width = 400, textColour = info_colour } iface_group_timespan = octane.gui.create { -- Группа элементов управления диапазоном времени для рендера type = octane.gui.componentType.GROUP, border = true, text = "Timespan Settings", rows = 3, cols = 2, children = { iface_label_renderfrom, iface_renderfrom, iface_label_renderto, iface_renderto, iface_label_info, iface_info }, height = 300, width = 400, padding = {4}, inset = {5}, x = 5, y = 5, } -- Интерфейс установки качества iface_label_format = octane.gui.create { -- Заголовок для списка графических форматов type = octane.gui.componentType.LABEL, text = "File format:", } iface_format = octane.gui.create { -- Список графических форматов type = octane.gui.componentType.COMBO_BOX, items = output_file_formats_text, width = 400, selectedIx = 3 } iface_label_info_Q = octane.gui.create { -- Заголовок инфо-лейбла type = octane.gui.componentType.LABEL, text = "Rendering info:", textColour = info_colour } imgresolution = rendertarget:getConnectedNode(octane.P_RESOLUTION) renderalpha = renderkernel:getPinValue(octane.P_ALPHA_CHANNEL) and "ON" or "OFF" iface_info_Q = octane.gui.create { -- Текст инфо-лейбла type = octane.gui.componentType.LABEL, text = "Samples/px: "..renderkernel:getPinValue(octane.P_MAX_SAMPLES)..", Image Size: "..imgresolution:getAttribute(octane.A_VALUE)[1].."x"..imgresolution:getAttribute(octane.A_VALUE)[2].." px, ".."Alpha channel: "..renderalpha, width = 400, textColour = info_colour } iface_group_q = octane.gui.create { -- Группа элементов управления качеством type = octane.gui.componentType.GROUP, border = true, text = "Quality Settings", rows = 2, cols = 2, children = { iface_label_format, iface_format, iface_label_info_Q, iface_info_Q }, height = 300, width = 400, padding = {4}, inset = {5}, x = 5, y = 118, } -- Интерфейс установки выходных файлов и работы с ними iface_button_output_folder = octane.gui.create { -- Кнопка для выбора каталога type = octane.gui.componentType.BUTTON, text = "Output Folder:", } iface_output_folder = octane.gui.create { -- Выбор каталога сохранения файлов type = octane.gui.componentType.TEXT_EDITOR, name = "TEXT_OUTPUT_FOLDER", text = "", width = 400 } iface_label_output_file = octane.gui.create { -- Заголовок для выбора имени файла type = octane.gui.componentType.LABEL, text = "Filename base:", } iface_output_file = octane.gui.create { -- Выбор базы имени файла type = octane.gui.componentType.TEXT_EDITOR, name = "TEXT_OUTPUT_FILE", text = "", width = 400 } iface_label_numbering_from = octane.gui.create { -- Заголовок для задания нумераци type = octane.gui.componentType.LABEL, text = "Numbering:", } iface_use_numbering = octane.gui.create { type = octane.gui.componentType.CHECK_BOX, text = "Start file numbering at:", checked = false, width = 200 } iface_numbering_start = octane.gui.create { type = octane.gui.componentType.NUMERIC_BOX, value = 0, maxValue = 10000, width = 200, } iface_numbering_from = octane.gui.create { -- Задание нумерации файлов type = octane.gui.componentType.GROUP, border = false, rows = 1, cols = 2, children = { iface_use_numbering, iface_numbering_start }, width = 400 } iface_label_filehandling = octane.gui.create { -- Заголовок для работы с файлами type = octane.gui.componentType.LABEL, text = "File Handling:", } iface_fileskip = octane.gui.create { type = octane.gui.componentType.CHECK_BOX, text = "Skip existing image files", checked = true, width = 200 } iface_group_o = octane.gui.create { -- Группа элементов управления каталогом рендера и файлами type = octane.gui.componentType.GROUP, border = true, text = "Output Settings", rows = 4, cols = 2, children = { iface_button_output_folder, iface_output_folder, iface_label_output_file, iface_output_file, iface_label_numbering_from, iface_numbering_from, iface_label_filehandling, iface_fileskip }, height = 300, width = 400, padding = {4}, inset = {5}, x = 5, y = 203, } -- Прогресс-бар iface_progress_bar = octane.gui.create { type = octane.gui.componentType.PROGRESS_BAR, text = pbName(), width = 500, x = 23, y = 358 } -- Кнопки управления процессом button_width = 163 iface_button_render = octane.gui.create { -- Запуск рендера type = octane.gui.componentType.BUTTON, text = "Start Render", width = button_width, } iface_button_pause = octane.gui.create { -- Пауза type = octane.gui.componentType.BUTTON, text = "Pause", width = button_width, enable = false, } iface_button_cancel = octane.gui.create { -- Отмена type = octane.gui.componentType.BUTTON, text = "Cancel", width = button_width, enable = false, } iface_buttons = octane.gui.create { -- Задание нумерации файлов type = octane.gui.componentType.GROUP, border = false, rows = 1, cols = 2, children = { iface_button_render, iface_button_cancel }, width = 500, padding = {3}, inset = {5}, x = 96, y = 383, } -- ОКНО ИНТЕРФЕЙСА -- local render_window = octane.gui.create { type = octane.gui.componentType.WINDOW, text = ("ORA Imported Animation Render "..sVersion), children = { iface_group_timespan, iface_group_q, iface_group_o, iface_progress_bar, iface_buttons }, width = iface_group_timespan.width + iface_group_timespan.x * 2, height = 430, } -- ПРОЦЕСС РЕНДЕРА -- function cancelRender() -- ОТМЕНА РЕНДЕРА IS_CANCELLED = true octane.render.callbackStop() iface_button_render:updateProperties{ enable = true } iface_button_cancel:updateProperties{ enable = false } iface_button_render:updateProperties{ text = "Start Render" } end function runRender() -- ЗАПУСК РЕНДЕРА iface_button_cancel:updateProperties{ enable = true } iface_button_render:updateProperties{ enable = false } IS_CANCELLED = false --Проверка существования пути сохранения outputPath = iface_output_folder:getProperties().text if ((outputPath == nil or outputPath == "") or not octane.file.exists(outputPath)) then warnDialog = octane.gui.showDialog { type = 1, icon = 3, title = "Error!", text = "Output folder doesn't exists. Please choose the existing path.", buttonTexts = {"ОК"} } print ("----Please, set output path! Default path doesn't exist.-- \n\t--Пожалуйста, выберите путь! Директория по умолчанию не существует----\n") outputPath="" cancelRender() do return end iface_button_render:updateProperties{ enable = false } end if (outputPath ~= nil and outputPath ~= "" and string.match(outputPath, ".$")~="\\") then outputPath = (outputPath.."\\") end outputName = iface_output_file:getProperties().text..'_' -- даже при пустом имени файла ошибка не возникнет - будет 1 символ "_" в имени -- Проверка возможности записи в директорию if (octane.file.exists(outputPath)==true) then local testFile = octane.file.createFile(outputPath..outputName..".file") if testFile == false then warnDialog = octane.gui.showDialog { type = 1, icon = 3, title = "Error!", text = "File could not be written to selected directory!", buttonTexts = {"ОК"} } cancelRender() do return end else octane.file.remove(outputPath..outputName..".file") end end -- Подготовка именования файлов file_index = iface_use_numbering:getProperties().checked and iface_numbering_start:getProperties().value-1 or iface_renderfrom:getProperties().value-1 -- Минус первый индекс файла digits_count = "%05d" -- (5 цифр) - потом дописать: определить количество цифр в максимальном номере кадра с учетом смещения нумерации - там и будет нужное к-во цифр -- Рендеринг iface_progress_total = iface_renderto.value - iface_renderfrom.value iface_progress = 0 for i = iface_renderfrom:getProperties().value, iface_renderto:getProperties().value do -- прогресс-бар iface_progress_bar:updateProperties{ text = (pbName(i)), progress = (iface_progress / iface_progress_total)} octane.gui.updateStatus("Rendering animation...", iface_progress / iface_progress_total) -- кнопка iface_button_render:updateProperties{ text = "Rendering..." } -- имя файла и проверка его наличия file_index = file_index + 1 outputFile = outputPath..outputName..string.format(digits_count, file_index)..output_file_extensions[iface_format:getProperties().selectedIx] -- имя файла if (octane.file.exists(outputFile) and iface_fileskip:getProperties().checked) then -- пропускаем существующий файл в соответствии с настройками print ("File "..outputFile.." exists — skipped") else -- можно рендерить -- прерывание рендера if (IS_CANCELLED==true) then iface_button_render:updateProperties{ text = "Start Render" } iface_progress_bar:updateProperties{ text = "Render cancelled"} break end -- остановка рендера перед стартом if octane.render.isPaused() == true then octane.render.restart() octane.render.callbackStop() else octane.render.callbackStop() end -- Задаем текущий кадр (время) рендеринга graph:updateTime(i/prefs:getAttribute(octane.A_FRAMES_PER_SECOND)) -- Старт рендеринга octane.render.start {renderTargetNode=rendertarget} -- Приостановка перед сохранением octane.render.callbackStop() -- Сохранение изображения if (octane.render.saveImage(outputFile, output_file_formats[iface_format:getProperties().selectedIx])) then -- запись в определенном формате print ("Image saved as "..outputFile) else print ("Warning! Image was not saved as "..outputFile.." — something went wrong...") end end -- / можно рендерить -- прогресс-бар iface_progress = iface_progress + 1 end -- прогресс-бар cancelRender() iface_progress_bar:updateProperties{ text = "Render finished!", progress = 1} -- /Рендеринг end -- GUI CALLBACK -- local function guiCallback(component, event) if component == iface_button_output_folder then dir = iface_output_folder.text result = octane.gui.showDialog { type = octane.gui.dialogType.FILE_DIALOG, browseDirectory = true, path = dir, title = "Choose a folder to save rendered images", save = false, } -- Каталог выбран if result.result ~= "" then iface_button_render:updateProperties{ enable = true } iface_output_folder:updateProperties{ text = (result.result.."\\")} print(outputPath) else iface_button_render:updateProperties{ enable = false } iface_output_folder:updateProperties{ text = dir } end end -- установка надписи на прогресс-баре при изменении диапазона кадров if component == iface_renderfrom or component == iface_renderto then iface_progress_bar:updateProperties{ text = (pbName())} end -- контроль значений начального и конечного кадров if component == iface_renderfrom then if iface_renderfrom.value > iface_renderto.value then iface_renderto:updateProperties { value = iface_renderfrom.value } end end if component == iface_renderto then if iface_renderfrom.value > iface_renderto.value then iface_renderfrom:updateProperties { value = iface_renderto.value } end end if component == iface_button_render then runRender() elseif component == iface_button_pause then pauseRender() elseif component == iface_button_cancel then cancelRender() end end iface_button_output_folder:updateProperties { callback = guiCallback } iface_button_render:updateProperties { callback = guiCallback } iface_button_pause:updateProperties { callback = guiCallback } iface_button_cancel:updateProperties { callback = guiCallback } iface_renderfrom:updateProperties { callback = guiCallback } iface_renderto:updateProperties { callback = guiCallback } render_window:showWindow()