Files
OpenSpace/scripts/configuration_helper.lua
2022-01-25 12:08:24 +01:00

458 lines
17 KiB
Lua

-- Helper functions that are useful to customize the openspace.cfg loading
-- #######################################################################################
-- ## Public functions ##
-- #######################################################################################
-- SGCT related functions
sgct = {}
sgct.config = {}
-- Creates a configuration file similar to the default 'single.xml':
-- The parameter is a table and can contain the follow attributes:
-- first argument: horizontal window size {default: 1280}
-- second argument: vertical window size {default: 720}
-- res: A table containing the horizontal and vertical resolution [example: res={3840, 2160}]
-- pos: The position of the window on the screen [example: pos={50, 100}] {default: {50, 50}}
-- fullScreen: Whether the application should run in exclusive full screen [example: fullScreen=true] {default: false}
-- border: Whether the application should have window decorations (aka. border) [example: border=false] {default: true}
-- monitor: Determines the monitor on which the application is started [example: monitor=2] {default: 0}
-- shared: Determines whether the contents of the window should be shared using the SPOUT library [example: shared=true] {default: false}
-- Expert settings:
-- vsync: Whether the rendering speed is locked to the refreshrate [example: vsync=true] {default: false}
-- refreshRate: If vsync is enabled, this is the target framerate [example: refreshRate=30] {default: infinity}
-- stereo: Select the stereo rendering mode as supported by SGCT [example: stereo='anaglyph_red_cyan'] {default: 'none'}
-- msaa: The multisampling anti-aliasing factor [example: msaa=8] {default: 4}
-- scene: Global settings to all scene objects (offset, orientation, scaling; each optional)
-- [example: scene = {offset = {x = 1.0, y = 1.0, z = 2.0}, orientation = { yaw = 120, pitch = 15, roll = 0.0 }, scale = 10.0}]
-- sgctDebug: Determines whether a higher debug level in SGCT is enabled [example: sgctDebug=true] {default: false}
-- fov: The field of view settings [example: fov={ left=20, right=30, up=10, down=50}] {default: { left=40.0, right=40.0, up=22.5, down=22.5}}
-- tracked: Determines whether the aspect ratio of the camera defined at application startup should persist when the window is resized [example: tracked=false] {default: true}
-- Thus this function can be called the following ways:
-- sgct.config.single() -> Leading to a 1280x720 resolution window
-- sgct.config.single(1920, 1080) -> Leading to a 1920x1080 resolution window
-- sgct.config.single(640, 360, res={3840, 2160}) -> 640x360 window with 4K rendering resolution
-- sgct.config.single(msaa=1) -> 1280x720 resolution without multisampling
function sgct.config.single(arg) end
-- Creates a configuration file similar to the default 'single_fisheye.xml'
-- The parameter is a table and can contain the follow attributes:
-- first argument: horizontal window size {default: 1280}
-- second argument: vertical window size {default: 720}
-- res: A table containing the horizontal and vertical resolution [example: res={3840, 2160}]
-- pos: The position of the window on the screen [example: pos={50, 100}] {default: {50, 50}}
-- fullScreen: Whether the application should run in exclusive full screen [example: fullScreen=true] {default: false}
-- border: Whether the application should have window decorations (aka. border) [example: border=false] {default: true}
-- monitor: Determines the monitor on which the application is started [example: monitor=2] {default: 0}
-- shared: Determines whether the contents of the window should be shared using the SPOUT library [example: shared=true] {default: false}
-- Expert settings:
-- vsync: Whether the rendering speed is locked to the refreshrate [example: vsync=true] {default: false}
-- refreshRate: If vsync is enabled, this is the target framerate [example: refreshRate=30] {default: infinity}
-- stereo: Select the stereo rendering mode as supported by SGCT [example: stereo='anaglyph_red_cyan'] {default: 'none'}
-- msaa: The multisampling anti-aliasing factor [example: msaa=8] {default: 4}
-- scene: Global settings to all scene objects (offset, orientation, scaling; each optional)
-- [example: scene = {offset = {x = 1.0, y = 1.0, z = 2.0}, orientation = { yaw = 120, pitch = 15, roll = 0.0 }, scale = 10.0}]
-- sgctDebug: Determines whether a higher debug level in SGCT is enabled [example: sgctDebug=true] {default: false}
-- fov: The field of view for the fisheye [example: fov=360] {default: 180}
-- quality: The quality setting for the cubemap textures [example: quality="4k"] {default: "1k"}
-- tilt: The forwards tilt of the fisheye, relative to the center [example: tilt=90] {default: 0.0}
-- background: The background color used outside of the fisheye rendering [example: backgruound={r=1.0, g=0.25, b=0.25, a=1.0}] {default: {r=0.1, g=0.1, b=0.1, a=1.0}}
-- Thus this function can be called the following ways:
-- sgct.config.fisheye() -> Leading to a 1280x720 resolution window
-- sgct.config.fisheye(640, 640) -> Leading to a 640x650 resolution window
-- sgct.config.fisheye(640, 360, res={3840, 3840}) -> 640x360 window with 4K rendering resolution
-- sgct.config.fisheye(msaa=1) -> 1280x720 resolution without multisampling
function sgct.config.fisheye(arg) end
-- Global variable storing the name of the config function called at initialization
sgctconfiginitializeString = ""
--[[
##########################################################################################
Internal helper functions
##########################################################################################
]]--
function generateSingleViewportFOV(down, up, left, right, tracked)
local result = {}
table.insert(result, [[ {]])
if tracked then
table.insert(result, [[ "tracked": ]] .. tostring(tracked) .. [[,]])
end
table.insert(result, [[ "projection": {]])
table.insert(result, [[ "type": "PlanarProjection",]])
table.insert(result, [[ "fov": {]])
table.insert(result, [[ "left": ]] .. left .. [[,]])
table.insert(result, [[ "right": ]] .. right .. [[,]])
table.insert(result, [[ "up": ]] .. up .. [[,]])
table.insert(result, [[ "down": ]] .. down)
table.insert(result, [[ }]])
table.insert(result, [[ }]])
table.insert(result, [[ }]])
return table.concat(result, "\n")
end
function generateFisheyeViewport(fov, quality, tilt, background, crop, offset, tracked)
local result = {}
table.insert(result, [[ {]])
if tracked then
table.insert(result, [[ "tracked": ]] .. tostring(tracked) .. [[,]])
end
table.insert(result, [[ "projection": {]])
table.insert(result, [[ "type": "FisheyeProjection",]])
if fov then
table.insert(result, [[ "fov": ]] .. fov .. [[,]])
end
table.insert(result, [[ "quality": "]] .. quality .. [[",]])
table.insert(result, [[ "tilt": ]] .. tilt .. [[,]])
if crop then
table.insert(result, [[ "crop": {]])
table.insert(result, [[ "left": ]] .. crop["left"] .. [[,]])
table.insert(result, [[ "right": ]] .. crop["right"] .. [[,]])
table.insert(result, [[ "top": ]] .. crop["top"] .. [[,]])
table.insert(result, [[ "bottom": ]] .. crop["bottom"])
table.insert(result, [[ },]])
end
if offset then
table.insert(result, [[ "offset": {]])
table.insert(result, [[ "x": ]] .. offset["x"] .. [[,]])
table.insert(result, [[ "y": ]] .. offset["y"] .. [[,]])
table.insert(result, [[ "z": ]] .. offset["z"])
table.insert(result, [[ },]])
end
table.insert(result, [[ "background": {]])
table.insert(result, [[ "r": ]] .. background["r"] .. [[,]])
table.insert(result, [[ "g": ]] .. background["g"] .. [[,]])
table.insert(result, [[ "b": ]] .. background["b"] .. [[,]])
table.insert(result, [[ "a": ]] .. background["a"])
table.insert(result, [[ }]])
table.insert(result, [[ }]])
table.insert(result, [[ }]])
return table.concat(result, "\n")
end
function generateWindow(result, fullScreen, msaa, border, monitor, tags, stereo, pos,
size, res, viewport)
table.insert(result, [[ "name": "OpenSpace",]])
if fullScreen then
table.insert(result, [[ "fullscreen": ]] .. tostring(fullScreen) .. [[,]])
end
if msaa then
table.insert(result, [[ "msaa": ]] .. msaa .. [[,]])
end
if border then
table.insert(result, [[ "border": ]] .. tostring(border) .. [[,]])
end
if monitor then
table.insert(result, [[ "monitor": ]] .. monitor .. [[,]])
end
if #(tags) > 0 then
local t = table.concat(arg["tags"], [[,]])
table.insert(result, [[ "tags": [ ]] .. t .. [[ ], ]])
end
if stereo then
table.insert(result, [[ "stereo": "]] .. stereo .. [[",]])
end
local px = pos[1]
local py = pos[2]
table.insert(result, [[ "pos": { "x": ]] .. px .. [[, "y": ]] .. py .. [[ },]])
local sx = size[1]
local sy = size[2]
table.insert(result, [[ "size": { "x": ]] .. sx .. [[, "y": ]] .. sy .. [[ },]])
if res then
res[1] = res[1] or size[1]
res[2] = res[2] or size[2]
table.insert(result, [[ "res": {]])
table.insert(result, [[ "x": ]] .. res[1] .. [[,]])
table.insert(result, [[ "y": ]] .. res[2])
table.insert(result, [[ },]])
end
table.insert(result, [[ "viewports": []])
table.insert(result, viewport)
table.insert(result, [[ ] ]])
end
function generateUser(result)
table.insert(result, [[ {]])
table.insert(result, [[ "eyeseparation": 0.06,]])
table.insert(result, [[ "pos": { "x": 0.0, "y": 0.0, "z": 0.0 }]])
table.insert(result, [[ }]])
end
function generateScene(result, scene)
if scene == nil then
return nil
end
local offset = scene["offset"] or { x = 0.0, y = 0.0, z = 0.0 }
local orientation = scene["orientation"] or { yaw = 0.0, pitch = 0.0, roll = 0.0 }
local scale = scene["scale"] or 1.0
table.insert(result, [[{]])
table.insert(result, [[ "offset": {]])
table.insert(result, [[ "x": ]] .. offset["x"] .. [[,]])
table.insert(result, [[ "y": ]] .. offset["y"] .. [[,]])
table.insert(result, [[ "z": ]] .. offset["z"] .. [[,]])
table.insert(result, [[ },]])
table.insert(result, [[ "orientation": {]])
table.insert(result, [[ "yaw": ]] .. orientation["yaw"] .. [[,]])
table.insert(result, [[ "pitch": ]] .. orientation["pitch"] .. [[,]])
table.insert(result, [[ "roll": ]] .. orientation["roll"] .. [[,]])
table.insert(result, [[ },]])
table.insert(result, [[ "scale": ]] .. scale)
table.insert(result, [[}]])
end
function generateSettings(result, refreshRate, vsync)
table.insert(result, [[ "display": {]])
if (arg["refreshRate"]) then
table.insert(result, [[ "refreshrate": ]] .. arg["refreshRate"] .. [[,]])
end
if arg["vsync"] then
table.insert(result, [[ "swapinterval": 1]])
else
table.insert(result, [[ "swapinterval": 0]])
end
table.insert(result, [[ }]])
end
function generateCluster(arg, viewport)
local result = {}
table.insert(result, [[{]])
table.insert(result, [[ "version": 1,]])
table.insert(result, [[ "masteraddress": "127.0.0.1",]])
if arg["sgctDebug"] then
table.insert(result, [[ "debug": ]] .. tostring(arg["sgctdebug"]) .. [[,]])
end
if (arg["settings"]) then
table.insert(result, [[ "settings": {]])
generateSettings(result, arg["refreshRate"], arg["vsync"])
table.insert(result, [[ },]])
end
if arg["scene"] then
table.insert(result, [[ "scene": {]])
generateScene(result, arg["scene"])
table.insert(result, [[ },]])
end
table.insert(result, [[ "nodes": []])
table.insert(result, [[ {]])
table.insert(result, [[ "address": "127.0.0.1",]])
table.insert(result, [[ "port": 20401,]])
table.insert(result, [[ "windows": []])
table.insert(result, [[ {]])
generateWindow(result, arg["fullScreen"], arg["msaa"], arg["border"], arg["monitor"],
arg["tags"], arg["stereo"], arg["pos"], arg["size"], arg["res"], viewport)
table.insert(result, [[ }]])
table.insert(result, [[ ] ]])
table.insert(result, [[ }]])
table.insert(result, [[ ],]])
table.insert(result, [[ "users": []])
generateUser(result)
table.insert(result, [[ ] ]])
table.insert(result, [[}]])
return table.concat(result, "\n")
end
function check(type_str, arg, param, subparam_type)
local t = type(arg[param])
assert(t == type_str or t == "nil", param .. " must be a " .. type_str .. " or nil")
if type_str == "table" and subparam_type and arg[param] then
for k, v in pairs(arg[param]) do
assert(
type(v) == subparam_type,
param .. "[" .. k .. "] must be a " .. subparam_type
)
end
end
end
function generateSingleWindowConfig(arg, viewport)
check("table", arg, "res", "number")
check("table", arg, "tags", "string")
check("table", arg, "pos", "number")
check("boolean", arg, "shared")
check("boolean", arg, "fullScreen")
check("boolean", arg, "border")
check("boolean", arg, "vsync")
check("boolean", arg, "sgctDebug")
check("number", arg, "monitor")
check("number", arg, "msaa")
check("number", arg, "refreshRate")
check("string", arg, "stereo")
check("table", arg, "scene")
if arg["scene"] then
check("table", arg["scene"], "offset", "number")
check("table", arg["scene"], "orientation", "number")
check("number", arg["scene"], "scale")
end
if arg["shared"] then
local t = arg["tags"]
t[#t + 1] = "Spout"
end
return generateCluster(arg, viewport)
end
function normalizeArg(arg)
arg = arg or {}
check("number", arg, 1)
check("number", arg, 2)
if type(arg[1]) == "number" and type(arg[2]) == "number" then
arg["size"] = { arg[1], arg[2] }
arg[1] = nil
arg[2] = nil
end
end
function sgct.makeConfig(config)
local configFile = os.tmpname() .. ".json"
local file = io.open(configFile, "w+")
file:write(config)
io.close(file)
return configFile
end
function sgct.config.single(arg)
normalizeArg(arg)
check("table", arg, "size", "number")
check("table", arg, "fov", "number")
arg["vsync"] = arg["vsync"] or false
arg["tags"] = arg["tags"] or {}
arg["pos"] = arg["pos"] or { 50, 50 }
arg["size"] = arg["size"] or { 1280, 720 }
if (not arg["fov"]) then
local tanDefaultFov = math.tan(math.rad(40.0))
local tanHorizontalFov
local tanVerticalFov
local aspectRatio = arg["size"][1] / arg["size"][2]
if (aspectRatio > 1) then
tanHorizontalFov = tanDefaultFov
tanVerticalFov = tanDefaultFov / aspectRatio
else
tanHorizontalFov = tanDefaultFov * aspectRatio
tanVerticalFov = tanDefaultFov
end
arg["fov"] = {
down = math.deg(math.atan(tanVerticalFov)),
up = math.deg(math.atan(tanVerticalFov)),
left = math.deg(math.atan(tanHorizontalFov)),
right = math.deg(math.atan(tanHorizontalFov))
}
end
check("boolean", arg, "tracked")
sgctconfiginitializeString = "sgct.config.single"
arg["tracked"] = arg["tracked"] or true
local viewport = generateSingleViewportFOV(arg["fov"]["down"], arg["fov"]["up"],
arg["fov"]["left"], arg["fov"]["right"], arg["tracked"])
return sgct.makeConfig(generateSingleWindowConfig(arg, viewport))
end
function sgct.config.fisheye(arg)
normalizeArg(arg)
check("number", arg, "fov")
check("number", arg, "tilt")
check("string", arg, "quality")
check("table", arg, "background", "number")
check("table", arg, "crop", "number")
check("table", arg, "offset", "number")
sgctconfiginitializeString = "sgct.config.fisheye"
arg["vsync"] = arg["vsync"] or false
arg["tags"] = arg["tags"] or {}
arg["pos"] = arg["pos"] or { 50, 50 }
arg["size"] = arg["size"] or { 1024, 1024 }
arg["quality"] = arg["quality"] or "1k"
arg["tilt"] = arg["tilt"] or 90.0
arg["background"] = arg["background"] or { r = 0.0, g = 0.0, b = 0.0, a = 1.0 }
arg["tracked"] = arg["tracked"] or false
local viewport = generateFisheyeViewport(arg["fov"], arg["quality"], arg["tilt"],
arg["background"], arg["crop"], arg["offset"], arg["tracked"])
return sgct.makeConfig(generateSingleWindowConfig(arg, viewport))
end