Editor Script to set up large models with Illumination asset

Here is an editor script I use to set up large models (with 100+ textures) with illumination asset for 3D lighting. It will need to be slightly modified to fit for other models but still should be enough to use as a base:

local json = require "_Scripts.Modules.dkjson"

local M = {}

local DEFAULT_MATERIAL = "/illumination/materials/model.material"
local BASE_TEXTURE_DIR = "/_Assets/BetterCityModel/textures/"
local DATA_TEXTURE = "/illumination/textures/data.png"
local EMPTY_TEXTURE = "/illumination/textures/empty.png"

local function read_text(path)
    local f = io.open(path, "r")
    if not f then return nil end
    local d = f:read("*a")
    f:close()
    return d
end

local function write_text(path, txt)
    local f = io.open(path, "w")
    if not f then return false end
    f:write(txt)
    f:close()
    return true
end

local function load_gltf_material_order(gltf_path)
    local text = read_text("." .. gltf_path)
    if not text then
        print("Cannot read GLTF:", gltf_path)
        return {}
    end

    local ok, data = pcall(json.decode, text)
    if not ok then
        print("Failed to decode GLTF JSON")
        return {}
    end

    if not data.materials then
        print("GLTF has no materials[]:", gltf_path)
        return {}
    end

    local order = {}
    for _, mat in ipairs(data.materials) do
        if mat.name and mat.name ~= "" then
            table.insert(order, mat.name)
        end
    end

    return order
end

local function generate_material_block(name)
    -- texture paths using your illumination pipeline
    local diffuse = BASE_TEXTURE_DIR .. name .. "_baseColor.png"

    return string.format([[
    materials {
        name: "%s"
        material: "%s"

        textures {
            sampler: "DATA_TEXTURE"
            texture: "%s"
        }
        textures {
            sampler: "DIFFUSE_TEXTURE"
            texture: "%s"
        }
        textures {
            sampler: "LIGHT_TEXTURE"
            texture: "%s"
        }
        textures {
            sampler: "NORMAL_TEXTURE"
            texture: "%s"
        }
        textures {
            sampler: "SPECULAR_TEXTURE"
            texture: "%s"
        }
    }
    ]], name, DEFAULT_MATERIAL, DATA_TEXTURE, diffuse, EMPTY_TEXTURE, EMPTY_TEXTURE, EMPTY_TEXTURE)
end

local function update_model(model_path, material_order)
    local full = "." .. model_path
    local text = read_text(full)
    if not text then
        print("Cannot read model:", model_path)
        return
    end

    -- Remove ALL existing material blocks safely
    text = text:gsub("materials%s*{.-\n}", "")

    -- Generate new blocks in EXACT GLTF ORDER
    local blocks = {}
    for _, name in ipairs(material_order) do
        table.insert(blocks, generate_material_block(name))
    end

    text = text .. "\n" .. table.concat(blocks, "\n")

    if write_text(full, text) then
        print("Model updated:", model_path)
    else
        print("Failed to write model:", model_path)
    end
end

function M.get_commands()
    return {
        {
            label = "Apply Material And Textures",
            locations = { "Assets" },
            query = { selection = { type = "resource", cardinality = "one" } },

            active = function(opts)
                local path = editor.get(opts.selection, "path")
                return path and path:match("%.model$")
            end,

            run = function(opts)
                local model_path = editor.get(opts.selection, "path")
                local text = read_text("." .. model_path)
                if not text then return end

                -- extract mesh path
                local mesh = text:match('mesh:%s+"([^"]+)"')
                if not mesh then
                    editor.ui.show_dialog(editor.ui.dialog({
                        title = "Error",
                        content = editor.ui.label({ text = "No mesh: \"\" found in model." }),
                        buttons = { editor.ui.dialog_button({ text = "OK", default = true }) }
                    }))
                    return
                end

                -- extract material order from GLTF
                local order = load_gltf_material_order(mesh)
                if #order == 0 then
                    editor.ui.show_dialog(editor.ui.dialog({
                        title = "Error",
                        content = editor.ui.label({ text = "No materials[] found in GLTF: " .. mesh }),
                        buttons = { editor.ui.dialog_button({ text = "OK", default = true }) }
                    }))
                    return
                end

                update_model(model_path, order)

                editor.ui.show_dialog(editor.ui.dialog({
                    title = "Done",
                    content = editor.ui.label({
                        text = "Material textures applied.\nOrder preserved from: " .. mesh
                    }),
                    buttons = { editor.ui.dialog_button({ text = "OK", default = true }) }
                }))
            end
        }
    }
end

return M
3 Likes