-- Loads a sun + sky environment from the Sun and Reflection sections of an IBL file -- -- @description Loads a sun + sky environment from an IBL file -- @author Roeland Schoukens -- @shortcut -- @version 0.1 -- -- magic number in Octane local BORDER_MODE_CLAMP = 3 -- magic numbers used in sIBL files for the REFmap values local LON_LAT, CYLINDRICAL, ANGULAR, SCREEN = 1, 2, 3, 4 -- section headers we read from sIBL files local mapId = "Reflection" local sunId = "Sun" -- show error function showError(s) octane.gui.showDialog{ type = octane.gui.dialogType.BUTTON_DIALOG, text = s, title = "load sIBL" } end -- parse an INI value function parseval(v) -- nil if v == nil then return nil end v = v:match("^%s*(.-)%s*$") if v == "" then return nil end -- quoted string local str = v:match('"(.*)"') if str ~= nil then return str end -- color local r, g, b = v:match("([%d.]+),([%d.]+),([%d.]+)") if b ~= nil then return {tonumber(r), tonumber(g), tonumber(b)} end -- float local fl = tonumber(v) if fl ~= nil then return fl end -- return token as string return v end -- check if we selected a daylight environment node to replace local envNode = octane.project.getSelection()[1] if envNode and envNode.nodeType == octane.NT_RENDERTARGET then envNode = envNode:getConnectedNode("environment") if envNode then octane.project.clearSelection() octane.project.select(envNode) end end if not envNode or envNode.nodeType ~= octane.NT_ENV_DAYLIGHT then showError("Select a daylight environment node or render target node") return end -- browse for file local ret = octane.gui.showDialog { type = octane.gui.dialogType.FILE_DIALOG, title = "Select sIBL file", wildcards = "*.ibl", path = file, save = false, } if ret.result == "" then return end local section = nil local map = {} local sun = {} -- parse the sIBL file (INI syntax) for line in io.lines(ret.result) do local sectionTitle = line:match("%[(.+)%]") if sectionTitle ~= nil then if sectionTitle == mapId then section = map elseif sectionTitle == sunId then section = sun else section = nil end elseif section ~= nil then local k, v = line:match("%s*(%a+)%s+=%s+(.+)") if k ~= nil then section[k] = parseval(v) end end end -- we want a REF and SUN section if not map.REFfile or not sun.SUNu then showError("We can load only sIBL files with an environment + sun") return end -- helper function to put a node of some type in a pin, and optionally set a value function PN(node, pin, type, attrId, value) local n = octane.node.create { pinOwnerNode = node, pinOwnerId = pin, type = type } if attrId ~= nil and value ~= nil then n:setAttribute(attrId, value) print(attrId, value[2]) end return n end if map.REFmap == LON_LAT then -- read the north offset local northOffset = envNode:getPinValue(octane.P_NORTHOFFSET) -- some values can be applied directly local texFile = octane.file.join(ret.result, "..", map.REFfile) local tex = PN(envNode, octane.P_TEXTURE, octane.NT_TEX_IMAGE, octane.A_FILENAME, texFile) tex:setPinValue(octane.P_GAMMA, map.REFgamma) tex:setPinValue(octane.P_POWER, map.REFmulti) PN(tex, octane.P_TRANSFORM, octane.NT_TRANSFORM_VALUE, octane.A_TRANSLATION, {map.REFu, map.REFv, 0}) -- set texture mapping, taking north offset into account local projection = PN(tex, octane.P_PROJECTION, octane.NT_PROJ_SPHERICAL) PN(projection, octane.P_TRANSFORM, octane.NT_TRANSFORM_VALUE, octane.A_ROTATION, {0, -northOffset * 180, 0}) -- calculate the sun direction sun.SUNu = sun.SUNu + map.REFu sun.SUNv = sun.SUNv + map.REFv local sinth = math.sin(sun.SUNv * math.pi) local x = - math.sin(sun.SUNu * 2 * math.pi) * sinth local y = math.cos(sun.SUNv * math.pi) local z = math.cos(sun.SUNu * 2 * math.pi) * sinth PN(envNode, octane.P_SUN_DIR, octane.NT_FLOAT, octane.A_VALUE, {x, y, z}) else showError("We can only render environments with lon-lat mapping") end -- we are ignoring sun.SUNmulti and sun.SUNcolor for now octane.changemanager.update()