diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/json.lua b/code/ryzom/client/data/gamedev/interfaces_v3/json.lua
new file mode 100644
index 000000000..66d473ec0
--- /dev/null
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/json.lua
@@ -0,0 +1,379 @@
+--
+-- json.lua
+--
+-- Copyright (c) 2015 rxi
+--
+-- This library is free software; you can redistribute it and/or modify it
+-- under the terms of the MIT license. See LICENSE for details.
+--
+
+local json = { _version = "0.1.0" }
+
+-------------------------------------------------------------------------------
+-- Encode
+-------------------------------------------------------------------------------
+
+local encode
+
+local escape_char_map = {
+ [ "\\" ] = "\\\\",
+ [ "\"" ] = "\\\"",
+ [ "\b" ] = "\\b",
+ [ "\f" ] = "\\f",
+ [ "\n" ] = "\\n",
+ [ "\r" ] = "\\r",
+ [ "\t" ] = "\\t",
+}
+
+local escape_char_map_inv = { [ "\\/" ] = "/" }
+for k, v in pairs(escape_char_map) do
+ escape_char_map_inv[v] = k
+end
+
+
+local function escape_char(c)
+ return escape_char_map[c] or string.format("\\u%04x", c:byte())
+end
+
+
+local function encode_nil(val)
+ return "null"
+end
+
+
+local function encode_table(val, stack)
+ local res = {}
+ stack = stack or {}
+
+ -- Circular reference?
+ if stack[val] then error("circular reference") end
+
+ stack[val] = true
+
+ if val[1] ~= nil or next(val) == nil then
+ -- Treat as array -- check keys are valid and it is not sparse
+ local n = 0
+ for k in pairs(val) do
+ if type(k) ~= "number" then
+ error("invalid table: mixed or invalid key types")
+ end
+ n = n + 1
+ end
+ if n ~= #val then
+ error("invalid table: sparse array")
+ end
+ -- Encode
+ for i, v in ipairs(val) do
+ table.insert(res, encode(v, stack))
+ end
+ stack[val] = nil
+ return "[" .. table.concat(res, ",") .. "]"
+
+ else
+ -- Treat as an object
+ for k, v in pairs(val) do
+ if type(k) ~= "string" then
+ error("invalid table: mixed or invalid key types")
+ end
+ table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
+ end
+ stack[val] = nil
+ return "{" .. table.concat(res, ",") .. "}"
+ end
+end
+
+
+local function encode_string(val)
+ return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
+end
+
+
+local function encode_number(val)
+ -- Check for NaN, -inf and inf
+ if val ~= val or val <= -math.huge or val >= math.huge then
+ error("unexpected number value '" .. tostring(val) .. "'")
+ end
+ return string.format("%.3f", val)
+end
+
+local type_func_map = {
+ [ "nil" ] = encode_nil,
+ [ "table" ] = encode_table,
+ [ "string" ] = encode_string,
+ [ "number" ] = encode_number,
+ [ "boolean" ] = tostring,
+}
+
+
+encode = function(val, stack)
+ local t = type(val)
+ local f = type_func_map[t]
+ if f then
+ return f(val, stack)
+ end
+ error("unexpected type '" .. t .. "'")
+end
+
+
+function json.encode(val)
+ return ( encode(val) )
+end
+
+
+-------------------------------------------------------------------------------
+-- Decode
+-------------------------------------------------------------------------------
+
+local parse
+
+local function create_set(...)
+ local res = {}
+ for i = 1, select("#", ...) do
+ res[ select(i, ...) ] = true
+ end
+ return res
+end
+
+local space_chars = create_set(" ", "\t", "\r", "\n")
+local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
+local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
+local literals = create_set("true", "false", "null")
+
+local literal_map = {
+ [ "true" ] = true,
+ [ "false" ] = false,
+ [ "null" ] = nil,
+}
+
+
+local function next_char(str, idx, set, negate)
+ for i = idx, #str do
+ if set[str:sub(i, i)] ~= negate then
+ return i
+ end
+ end
+ return #str + 1
+end
+
+
+local function decode_error(str, idx, msg)
+ local line_count = 1
+ local col_count = 1
+ for i = 1, idx - 1 do
+ col_count = col_count + 1
+ if str:sub(i, i) == "\n" then
+ line_count = line_count + 1
+ col_count = 1
+ end
+ end
+ error( string.format("%s at line %d col %d", msg, line_count, col_count) )
+end
+
+
+local function codepoint_to_utf8(n)
+ -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
+ local f = math.floor
+ if n <= 0x7f then
+ return string.char(n)
+ elseif n <= 0x7ff then
+ return string.char(f(n / 64) + 192, n % 64 + 128)
+ elseif n <= 0xffff then
+ return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
+ elseif n <= 0x10ffff then
+ return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
+ f(n % 4096 / 64) + 128, n % 64 + 128)
+ end
+ error( string.format("invalid unicode codepoint '%x'", n) )
+end
+
+
+local function parse_unicode_escape(s)
+ local n1 = tonumber( s:sub(3, 6), 16 )
+ local n2 = tonumber( s:sub(9, 12), 16 )
+ -- Surrogate pair?
+ if n2 then
+ return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
+ else
+ return codepoint_to_utf8(n1)
+ end
+end
+
+
+local function parse_string(str, i)
+ local has_unicode_escape = false
+ local has_surrogate_escape = false
+ local has_escape = false
+ local last
+ for j = i + 1, #str do
+ local x = str:byte(j)
+
+ if x < 32 then
+ decode_error(str, j, "control character in string")
+ end
+
+ if last == 92 then -- "\\" (escape char)
+ if x == 117 then -- "u" (unicode escape sequence)
+ local hex = str:sub(j + 1, j + 5)
+ if not hex:find("%x%x%x%x") then
+ decode_error(str, j, "invalid unicode escape in string")
+ end
+ if hex:find("^[dD][89aAbB]") then
+ has_surrogate_escape = true
+ else
+ has_unicode_escape = true
+ end
+ else
+ local c = string.char(x)
+ if not escape_chars[c] then
+ decode_error(str, j, "invalid escape char '" .. c .. "' in string")
+ end
+ has_escape = true
+ end
+ last = nil
+
+ elseif x == 34 then -- '"' (end of string)
+ local s = str:sub(i + 1, j - 1)
+ if has_surrogate_escape then
+ s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
+ end
+ if has_unicode_escape then
+ s = s:gsub("\\u....", parse_unicode_escape)
+ end
+ if has_escape then
+ s = s:gsub("\\.", escape_char_map_inv)
+ end
+ return s, j + 1
+
+ else
+ last = x
+ end
+ end
+ decode_error(str, i, "expected closing quote for string")
+end
+
+
+local function parse_number(str, i)
+ local x = next_char(str, i, delim_chars)
+ local s = str:sub(i, x - 1)
+ local n = tonumber(s)
+ if not n then
+ decode_error(str, i, "invalid number '" .. s .. "'")
+ end
+ return n, x
+end
+
+
+local function parse_literal(str, i)
+ local x = next_char(str, i, delim_chars)
+ local word = str:sub(i, x - 1)
+ if not literals[word] then
+ decode_error(str, i, "invalid literal '" .. word .. "'")
+ end
+ return literal_map[word], x
+end
+
+
+local function parse_array(str, i)
+ local res = {}
+ local n = 1
+ i = i + 1
+ while 1 do
+ local x
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of array?
+ if str:sub(i, i) == "]" then
+ i = i + 1
+ break
+ end
+ -- Read token
+ x, i = parse(str, i)
+ res[n] = x
+ n = n + 1
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "]" then break end
+ if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
+ end
+ return res, i
+end
+
+
+local function parse_object(str, i)
+ local res = {}
+ i = i + 1
+ while 1 do
+ local key, val
+ i = next_char(str, i, space_chars, true)
+ -- Empty / end of object?
+ if str:sub(i, i) == "}" then
+ i = i + 1
+ break
+ end
+ -- Read key
+ if str:sub(i, i) ~= '"' then
+ decode_error(str, i, "expected string for key")
+ end
+ key, i = parse(str, i)
+ -- Read ':' delimiter
+ i = next_char(str, i, space_chars, true)
+ if str:sub(i, i) ~= ":" then
+ decode_error(str, i, "expected ':' after key")
+ end
+ i = next_char(str, i + 1, space_chars, true)
+ -- Read value
+ val, i = parse(str, i)
+ -- Set
+ res[key] = val
+ -- Next token
+ i = next_char(str, i, space_chars, true)
+ local chr = str:sub(i, i)
+ i = i + 1
+ if chr == "}" then break end
+ if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
+ end
+ return res, i
+end
+
+
+local char_func_map = {
+ [ '"' ] = parse_string,
+ [ "0" ] = parse_number,
+ [ "1" ] = parse_number,
+ [ "2" ] = parse_number,
+ [ "3" ] = parse_number,
+ [ "4" ] = parse_number,
+ [ "5" ] = parse_number,
+ [ "6" ] = parse_number,
+ [ "7" ] = parse_number,
+ [ "8" ] = parse_number,
+ [ "9" ] = parse_number,
+ [ "-" ] = parse_number,
+ [ "t" ] = parse_literal,
+ [ "f" ] = parse_literal,
+ [ "n" ] = parse_literal,
+ [ "[" ] = parse_array,
+ [ "{" ] = parse_object,
+}
+
+
+parse = function(str, idx)
+ local chr = str:sub(idx, idx)
+ local f = char_func_map[chr]
+ if f then
+ return f(str, idx)
+ end
+ decode_error(str, idx, "unexpected character '" .. chr .. "'")
+end
+
+
+function json.decode(str)
+ if type(str) ~= "string" then
+ error("expected argument of type string, got " .. type(str))
+ end
+ return ( parse(str, next_char(str, 1, space_chars, true)) )
+end
+
+
+return json
diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/sceneedit.lua b/code/ryzom/client/data/gamedev/interfaces_v3/sceneedit.lua
new file mode 100644
index 000000000..8f907808a
--- /dev/null
+++ b/code/ryzom/client/data/gamedev/interfaces_v3/sceneedit.lua
@@ -0,0 +1,592 @@
+json = dofile("user/json.lua")
+
+--- Parse interface of ark_scene_editor_edit_menu ---
+local script = [[
+
+
+]]
+parseInterfaceFromString(script)
+
+
+if SceneEditor == nil then
+ SceneEditor = {
+ Shapes = {},
+ Groups = {},
+ LastEditedGroup = nil,
+ HaveUpdate = nil
+
+ };
+end
+
+
+function debug(text)
+ local message = ucstring()
+ message:fromUtf8(tostring(text))
+ displaySystemInfo(message, "SYS")
+end
+
+
+function SceneEditor:init(scene_id, form_url, translations, icons_url)
+ self.sceneId = scene_id
+ self.baseUrl = form_url
+ self.iconsUrl = icons_url
+ self.T = translations
+end
+
+function SceneEditor:reset()
+ self.Shapes = {}
+ self.Groups = {}
+ self.LastEditedGroup = nil
+ self.HaveUpdate = nil
+ runAH(nil, "remove_shapes", "")
+ self:get_html("Reseted")
+end
+
+function SceneEditor:show_menu()
+ if (rightClick) then
+ SceneEditor:launch_menu()
+ end
+end
+
+
+function SceneEditor:launch_menu(id)
+ -- SelectedInstanceId can be set by client application
+ if id ~= nil then
+ SelectedInstanceId = id
+ end
+ local menu = getUI("ui:interface:ark_scene_editor_edit_menu")
+ menu:setMinW(85)
+ menu:updateCoords()
+ menu = menu:getRootMenu()
+ menu:reset()
+ menu:addLine(ucstring("-- SHAPE EDITION --"), "", "", "shape_header")
+ menu:addLine(ucstring("Move"), "", "", "shape_move")
+ menu:addSubMenu(1)
+ local subMenu = menu:getSubMenu(1)
+ subMenu:addIconLine(ucstring("Axe X"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:move_x()')", "shape_move_x", "ark_move_x.tga")
+ subMenu:addIconLine(ucstring("Axe Y"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:move_y()')", "shape_move_y", "ark_move_y.tga")
+ subMenu:addIconLine(ucstring("Axe Z"), "lua", "x, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:move_z()')", "shape_move_z", "ark_move_z.tga")
+ subMenu:addIconLine(ucstring("Axes X & Y"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:move_xy()')", "shape_move_xy", "ark_move_xy.tga")
+ subMenu:addIconLine(ucstring("Axes X & Y Snap to ground"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:move_xysnap()')", "shape_move_xy_snap", "ark_move_xysnap.tga")
+ subMenu:addSeparator()
+ subMenu:addIconLine(ucstring("Move to player"), "lua", "SceneEditor:move_player()", "shape_move_player", "ark_move_player.tga")
+
+ menu:addLine(ucstring("Rotate"), "", "", "shape_rotate")
+ menu:addSubMenu(2)
+ subMenu = menu:getSubMenu(2)
+ subMenu:addIconLine(ucstring("Axe X"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:rotate(SelectedInstanceId, \"x\")')", "shape_rotate_x", "ark_rotate_x.tga")
+ subMenu:addIconLine(ucstring("Axe Y"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:rotate(SelectedInstanceId, \"y\")')", "shape_rotate_y", "ark_rotate_y.tga")
+ subMenu:addIconLine(ucstring("Axe Z"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:rotate(SelectedInstanceId, \"z\")')", "shape_rotate_z", "ark_rotate_z.tga")
+
+ menu:addLine(ucstring("Scale"), "", "", "shape_scale")
+ menu:addSubMenu(3)
+ subMenu = menu:getSubMenu(3)
+ subMenu:addIconLine(ucstring("Axe X"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:scale(SelectedInstanceId, \"x\")')", "shape_scale_x", "ark_scale_x.tga")
+ subMenu:addIconLine(ucstring("Axe Y"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:scale(SelectedInstanceId, \"y\")')", "shape_scale_y", "ark_scale_y.tga")
+ subMenu:addIconLine(ucstring("Axe Z"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:scale(SelectedInstanceId, \"z\")')", "shape_scale_z", "ark_scale_z.tga")
+
+ menu:addLine(ucstring("-- COLLISION EDITION --"), "", "", "col_header")
+ menu:addLine(ucstring("Move"), "", "", "col_move")
+ menu:addSubMenu(5)
+ subMenu = menu:getSubMenu(5)
+ subMenu:addIconLine(ucstring("Axe X"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_move_x()')", "col_move_x", "ark_move_x.tga")
+ subMenu:addIconLine(ucstring("Axe Y"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_move_y()')", "col_move_y", "ark_move_y.tga")
+ subMenu:addIconLine(ucstring("Axe Z"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_move_z()')", "col_move_z", "ark_move_xy.tga")
+ subMenu:addIconLine(ucstring("Axe X & Y"), "lua", "setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_move_xy()')", "col_move_xy", "ark_move_xy.tga")
+ subMenu:addSeparator()
+ subMenu:addIconLine(ucstring("Move to Shape"), "lua", "SceneEditor:col_move_to_shape()", "col_move_to_shape", "ark_move_player.tga")
+
+ menu:addIconLine(ucstring("Rotate"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_rotate(SelectedInstanceId, \"x\")')", "col_rotate_x", "ark_rotate_x.tga")
+
+ menu:addLine(ucstring("Scale"), "", "", "col_scale")
+ menu:addSubMenu(7)
+ subMenu = menu:getSubMenu(7)
+ subMenu:addIconLine(ucstring("Axe X"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_scale(SelectedInstanceId, \"x\")')", "col_scale_x", "ark_scale_x.tga")
+ subMenu:addIconLine(ucstring("Axe Y"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_scale(SelectedInstanceId, \"y\")')", "col_scale_y", "ark_scale_y.tga")
+ subMenu:addIconLine(ucstring("Axe Z"), "lua", "ARK_SHAPE_LATEST_X, ARK_SHAPE_LATEST_Y = getMousePos(); setOnDraw(getUI('ui:interface:ark_scene_editor'), 'SceneEditor:col_scale(SelectedInstanceId, \"z\")')", "col_scale_z", "ark_scale_z.tga")
+
+ launchContextMenuInGame("ui:interface:ark_scene_editor_edit_menu")
+end
+
+
+function arcc_tools_check_rclick()
+ root = getUI("ui:interface")
+ local rx, ry = getMousePos()
+ i_id = getShapeIdAt(rx, ry)
+ if i_id >= 0 then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ end
+end
+
+function SceneEditor:move(id, axe)
+ local d, mx, my = getMouseDown()
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ SceneEditor:set_modified(id)
+ self:get_html("Moved")
+ else
+ local x,y,z = getGroundAtMouse()
+ if axe == "x" then moveShape(id, tostring(x), "+0", "+0") end
+ if axe == "y" then moveShape(id, "+0", tostring(y), "+0") end
+ if axe == "z" then
+ mx, my = getMousePos()
+ moveShape(id, "+0", "+0", "+"..tostring((my-ARK_SHAPE_LATEST_Y)/100))
+ ARK_SHAPE_LATEST_Y = my
+ end
+ if axe == "xysnap" then moveShape(id, tostring(x), tostring(y), tostring(z)) end
+ if axe == "xy" then moveShape(id, tostring(x), tostring(y), "+0") end
+ if axe == "player" then
+ x, y, z = getPlayerPos()
+ moveShape(id, tostring(x), tostring(y), tostring(z))
+ SceneEditor:set_modified(id)
+ self:get_html("Moved to player")
+ end
+
+ end
+end
+
+function SceneEditor:rotate(id, axe)
+ local d, mx, my = getMouseDown()
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ SceneEditor:set_modified(id)
+ self:get_html("Rotate")
+ else
+ mx, my = getMousePos()
+ if axe == "x" then rotateShape(id, "+"..tostring((my-ARK_SHAPE_LATEST_Y)/100), "+0", "+0") end
+ if axe == "y" then rotateShape(id, "+0", "+"..tostring((my-ARK_SHAPE_LATEST_Y)/100), "+0") end
+ if axe == "z" then rotateShape(id, "+0", "+0", "+"..tostring((mx-ARK_SHAPE_LATEST_X)/100)) end
+ ARK_SHAPE_LATEST_X = mx
+ ARK_SHAPE_LATEST_Y = my
+ end
+end
+
+function SceneEditor:scale(id, axe)
+ local d, mx, my = getMouseDown()
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ SceneEditor:set_modified(id)
+ self:get_html("Rotate")
+ else
+ mx, my = getMousePos()
+ local setup = {}
+ if axe == "x" then setup["scale x"]="+"..tostring((mx-ARK_SHAPE_LATEST_X)/100) end
+ if axe == "y" then setup["scale y"]="+"..tostring((mx-ARK_SHAPE_LATEST_X)/100) end
+ if axe == "z" then setup["scale z"]="+"..tostring((my-ARK_SHAPE_LATEST_Y)/100) end
+ setupShape(id, setup)
+ ARK_SHAPE_LATEST_X = mx
+ ARK_SHAPE_LATEST_Y = my
+ end
+end
+
+
+function SceneEditor:move_x()
+ self:move(SelectedInstanceId, "x")
+end
+
+function SceneEditor:move_y()
+ self:move(SelectedInstanceId, "y")
+end
+
+function SceneEditor:move_xy()
+ self:move(SelectedInstanceId, "xy")
+end
+
+function SceneEditor:move_xysnap()
+ self:move(SelectedInstanceId, "xysnap")
+end
+
+function SceneEditor:move_z()
+ self:move(SelectedInstanceId, "z")
+end
+
+function SceneEditor:move_player()
+ self:move(SelectedInstanceId, "player")
+end
+
+
+function SceneEditor:col_move(id, axe)
+ local d, mx, my = getMouseDown()
+
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ self:set_modified(id)
+ self:get_html("Updated")
+ else
+ local x,y,z = getGroundAtMouse()
+ local setup = {}
+ if axe == "x" then setup["col pos x"]=tostring(x) end
+ if axe == "y" then setup["col pos y"]=tostring(y) end
+ if axe == "z" then
+ mx, my = getMousePos()
+ setup["col pos z"]="+"..tostring((my-ARK_SHAPE_LATEST_Y)/100)
+ ARK_SHAPE_LATEST_X = mx
+ ARK_SHAPE_LATEST_Y = my
+ end
+ if axe == "xy" then setup["col pos x"]=tostring(x); setup["col pos y"]=tostring(y) end
+ if axe == "shape" then
+ x, y, z = getShapePos(id)
+ setup["col pos x"]=tostring(x)
+ setup["col pos y"]=tostring(y)
+ self:set_modified(id)
+ setupShape(id, setup)
+ self:get_html("Updated")
+ else
+ setupShape(id, setup)
+ end
+ end
+end
+
+function SceneEditor:col_rotate(id, axe)
+ local d, mx, my = getMouseDown()
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ SceneEditor:set_modified(id)
+ self:get_html("Rotate")
+ else
+ mx, my = getMousePos()
+ local setup = {}
+ setup["col orientation"]="+"..tostring((mx-ARK_SHAPE_LATEST_X)/100)
+ setupShape(id, setup)
+ ARK_SHAPE_LATEST_X = mx
+ ARK_SHAPE_LATEST_Y = my
+ end
+end
+
+
+
+function SceneEditor:col_scale(id, axe)
+ local d, mx, my = getMouseDown()
+ if d then
+ setOnDraw(getUI("ui:interface:ark_scene_editor"), "")
+ SceneEditor:set_modified(id)
+ self:get_html("Rotate")
+ else
+ mx, my = getMousePos()
+ local setup = {}
+ if axe == "x" then setup["col size x"]="+"..tostring((mx-ARK_SHAPE_LATEST_X)/100) end
+ if axe == "y" then setup["col size y"]="+"..tostring((mx-ARK_SHAPE_LATEST_X)/100) end
+ if axe == "z" then setup["col size z"]="+"..tostring((my-ARK_SHAPE_LATEST_Y)/100) end
+ setupShape(id, setup)
+ ARK_SHAPE_LATEST_X = mx
+ ARK_SHAPE_LATEST_Y = my
+ end
+end
+
+
+function SceneEditor:set_modified(id)
+ self.Groups[self.Shapes[id].group].props.modified=true
+ self.Shapes[id].modified = "modified"
+ self.HaveUpdate = true
+end
+
+
+function SceneEditor:col_move_x()
+ self:col_move(SelectedInstanceId, "x")
+end
+
+function SceneEditor:col_move_y()
+ self:col_move(SelectedInstanceId, "y")
+end
+function SceneEditor:col_move_z()
+ self:col_move(SelectedInstanceId, "z")
+end
+
+
+function SceneEditor:col_move_xy()
+ self:col_move(SelectedInstanceId, "xy")
+end
+
+
+function SceneEditor:col_move_to_shape()
+ self:col_move(SelectedInstanceId, "shape")
+end
+
+
+function SceneEditor:setup_shape(shape_id, setup)
+ final_setup = self.Shapes[new_shape.id].setup
+ if final_setup == nil then
+ final_setup = {}
+ end
+ for k,v in pairs(setup) do
+ final_setup[k] = v
+ end
+ self.Shapes[new_shape.id].setup = final_setup
+ setupShape(shape_id, setup)
+end
+
+function SceneEditor:add(shape)
+ if self.LastEditedGroup == nil then
+ self:get_html(''..self.T["no_selected_group"]..'', '000000')
+ end
+ local new_shape = {}
+ new_shape.file = shape
+ new_shape.group = self.LastEditedGroup
+ self.Groups[new_shape.group].props.modified=true
+ new_shape.db_id = self.Groups[new_shape.group].props.count + 1
+ new_shape.modified = "added"
+ new_shape_id = addShape(shape, 0, 0, 0, "user", 1, true, "", "SceneEditor:show_menu()")
+ table.insert(self.Groups[new_shape.group], new_shape_id)
+ self.Groups[new_shape.group].props.count = self.Groups[new_shape.group].props.count + 1
+ self.Shapes[new_shape_id] = new_shape
+ self:get_html("Added")
+end
+
+
+function SceneEditor:removeShape(shape_id)
+ deleteShape(shape_id)
+ local group = self.Shapes[shape_id].group
+ for k,g_shape_id in pairs(self.Groups[group]) do
+ if shape_id == g_shape_id then
+ self.Groups[group][k] = nil
+ end
+ end
+ self:set_modified(shape_id)
+ self.Shapes[shape_id] = nil
+ self:get_html("Removed")
+end
+
+function SceneEditor:addGroup(name, count, show, edit)
+ if name == nil then
+ return
+ end
+ if self.Groups[name] == nil then
+ self.Groups[name] = {}
+ self.Groups[name].props = {}
+ self.Groups[name].props.count = count
+ self.Groups[name].props.show = show
+ self.Groups[name].props.edit = edit
+ self.Groups[name].props.modified = false
+ else
+ self.Groups[name].props.show = show
+ self.Groups[name].props.edit = edit
+ end
+end
+
+function SceneEditor:editGroup(group)
+ if self.LastEditedGroup then
+ self:removeGroup(self.LastEditedGroup)
+ self:addGroup(self.LastEditedGroup, 0, true, false)
+ end
+ self:removeGroup(group);
+ self:addGroup(group, 0, true, true)
+ self.LastEditedGroup = group
+end
+
+function SceneEditor:addFromDb(group, db_id, json_shape)
+ shape = json.decode(json_shape)
+ shape.db_id = db_id
+
+ shape.group = group
+ shape.modified = ""
+ if hide then
+ shape_id = addShape(shape.file, shape.pos[1], shape.pos[2], shape.pos[3], "user", 1, false, "", "")
+ else
+ shape_id = addShape(shape.file, shape.pos[1], shape.pos[2], shape.pos[3], "user", 1, true, "", "SceneEditor:show_menu()")
+ end
+ rotateShape(shape_id, tostring(shape.rot[1]), tostring(shape.rot[2]), tostring(shape.rot[3]))
+ setupShape(shape_id, shape.setup)
+ self.Shapes[shape_id] = shape
+ table.insert(self.Groups[group], shape_id)
+ if db_id > self.Groups[group].props.count then
+ self.Groups[group].props.count = db_id
+ end
+end
+
+
+function SceneEditor:removeGroup(group)
+ if self.Groups[group] == nil then
+ return
+ end
+
+ for k,shape_id in pairs(self.Groups[group]) do
+ if k ~= "props" then
+ self.Shapes[shape_id] = nil
+ deleteShape(shape_id)
+ end
+ end
+ self.Groups[group] = nil
+ if self.LastEditedGroup == group then
+ self.LastEditedGroup = nil
+ local ui = getUI("ui:interface:ark_list_of_shapes")
+ if ui then
+ ui.active=false
+ end
+ end
+ self:get_html("Group Removed")
+end
+
+function SceneEditor:enc64(data)
+ local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+ return ((data:gsub('.', function(x)
+ local r,b='',x:byte()
+ for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
+ return r;
+ end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
+ if (#x < 6) then return '' end
+ local c=0
+ for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
+ return b:sub(c+1,c+1)
+ end)..({ '', '==', '=' })[#data%3+1])
+end
+
+function SceneEditor:get_vector(x, y, z)
+ local vector = {}
+ table.insert(vector, x)
+ table.insert(vector, y)
+ table.insert(vector, z)
+
+ return vector
+end
+
+function SceneEditor:get_random_color()
+ local r = math.random(44, 66);
+ local g = math.random(44, 66);
+ local b = math.random(44, 66);
+ return '#'..tostring(r)..tostring(g)..tostring(b)
+
+end
+
+function pairsByKeys(t, f)
+ local a = {}
+ for n in pairs(t) do table.insert(a, n) end
+ table.sort(a, f)
+ local i = 0 -- iterator variable
+ local iter = function () -- iterator function
+ i = i + 1
+ if a[i] == nil then
+ return nil
+ else
+ return a[i], t[a[i]]
+ end
+ end
+ return iter
+end
+
+function SceneEditor:show_shape_list()
+ local ui = getUI("ui:interface:ark_list_of_shapes")
+ local need_setup = ui == nil
+ if need_setup then
+ WebBrowser:openWindow("ark_list_of_shapes", self.baseUrl..'_ListShapes')
+ ui = getUI("ui:interface:ark_list_of_shapes")
+ ui.pop_min_w = 400
+ ui.w = 400
+ getUI("ui:interface:ark_list_of_shapes:browser:header_opened:browse_redo").active=false
+ getUI("ui:interface:ark_list_of_shapes:browser:header_opened:browse_undo").active=false
+ getUI("ui:interface:ark_list_of_shapes:browser:header_opened:browse_refresh").active=false
+ getUI("ui:interface:ark_list_of_shapes:browser:header_opened:browse_home").active=false
+ else
+ ui.active = true
+ end
+end
+
+
+function SceneEditor:get_html_section(message, color)
+ return '
'
+end
+
+function SceneEditor:get_html(message, message_bg)
+ local new_group = ' '
+ local show_hide_cols = ' '
+ local reset_scene = ''
+
+ local html = ''..self.T["sceno_editor"]..''..self:get_html_section(message..' | '..new_group..show_hide_cols..reset_scene, (message_bg or SceneEditor:get_random_color()))
+
+ html = html .. ''
+ ui = getUI("ui:interface:ark_scene_editor:browser:content:html", false)
+ if ui then
+ ui:renderHtml(html)
+ end
+end
+
|