local stb = require("stb") local nk = sg local utils = require("lua.utils") -- require("engine.nuklear") local ffi = require("ffi") local tinsert = table.insert -- -------------------------------------------------------------------------------------- table.unpack = unpack -- -------------------------------------------------------------------------------------- local function checkstring(val) local t = type(val) if t == "string" then return val elseif t == "number" then return tostring(val) elseif t == "cdata" then return ffi.string(val) else error("bad argument: string or number expected, got " .. t, 2) end end -- -------------------------------------------------------------------------------------- renderer = { ctx = nil, all_fonts = {}, cursor = { name = "arrow", rect = { position = {x=0,y=0}, size ={x=1,y=1} } }, } renderer.font = { ctx = nil, } -- -------------------------------------------------------------------------------------- local atlas = ffi.new("struct nk_font_atlas[1]") local fonts = {} -- -------------------------------------------------------------------------------------- local master_img_width = ffi.new("int[1]", 2048) local master_img_height = ffi.new("int[1]", 4096) -- -------------------------------------------------------------------------------------- local function adjust_glyph(font_handle, unicode, width) local font = ffi.cast("struct nk_font *", font_handle.ptr) local g = font.glyphs[unicode] g.xadvance = width end -- -------------------------------------------------------------------------------------- local function get_glyph_xadvance(font_handle, unicode) local font = ffi.cast("struct nk_font *", font_handle.ptr) local g = font.glyphs[unicode] return g.xadvance end -- -------------------------------------------------------------------------------------- local function find_glyph(font, codepoint) local glyph_ptr = font.glyphs while glyph_ptr ~= nil and glyph_ptr[0].codepoint ~= 0 do if tonumber(glyph_ptr[0].codepoint) == codepoint then return glyph_ptr end glyph_ptr = glyph_ptr + 1 end return nil end -- -------------------------------------------------------------------------------------- local function fix_tab_glyph(glyph, space) glyph[0].xadvance = space * 4 glyph[0].w, glyph[0].h = 0, 0 glyph[0].x0, glyph[0].y0, glyph[0].x1, glyph[0].y1 = 0, 0, 0, 0 glyph[0].u0, glyph[0].v0, glyph[0].u1, glyph[0].v1 = 0, 0, 0, 0 end -- -------------------------------------------------------------------------------------- local rune_ranges = ffi.new("nk_rune[9]", { 0x0009, 0x000A, -- tab 0x0020, 0x00FF, -- basic Latin + Latin-1 0x2500, 0x2BFF, -- box-drawing, arrows 0xE000, 0xF8FF, -- private use / Nerd Font symbols 0 }) local fa_rune_ranges = ffi.new("nk_rune[9]", { 0xE000, 0xF8FF, -- private use / Nerd Font symbols 0 }) -- -------------------------------------------------------------------------------------- local function font_loader( atlas, font_file, font_size) local config = nk.nk_font_config(font_size) -- Special case where the glyphs need to be default if(string.match(font_file, "icons.ttf$")) then config.range = nk.nk_font_default_glyph_ranges() elseif(string.match(font_file, "fontawesome")) then config.range = fa_rune_ranges else config.range = rune_ranges end -- config.merge_mode = nk.nk_false local newfont = nk.nk_font_atlas_add_from_file(atlas, font_file, font_size, config) -- local image = nk.nk_font_atlas_bake(atlas, master_img_width, master_img_height, nk.NK_FONT_ATLAS_RGBA32) return nil, newfont end -- -------------------------------------------------------------------------------------- local function font_atlas_img( image, debug ) local sg_img_desc = ffi.new("sg_image_desc[1]") sg_img_desc[0].width = master_img_width[0] sg_img_desc[0].height = master_img_height[0] sg_img_desc[0].pixel_format = sg.SG_PIXELFORMAT_RGBA8 sg_img_desc[0].sample_count = 1 sg_img_desc[0].data.subimage[0][0].ptr = image sg_img_desc[0].data.subimage[0][0].size = master_img_width[0] * master_img_height[0] * 4 local new_img = sg.sg_make_image(sg_img_desc) -- // create a sokol-nuklear image object which associates an sg_image with an sg_sampler local img_desc = ffi.new("snk_image_desc_t[1]") img_desc[0].image = new_img local snk_img = nk.snk_make_image(img_desc) local nk_hnd = nk.snk_nkhandle(snk_img) if(debug) then -- Dump the atlas to check it. stb.stbi_write_png( "data/fonts/atlas_font.png", master_img_width[0], master_img_height[0], 4, image, master_img_width[0] * 4) end return nk_hnd end -- -------------------------------------------------------------------------------------- -- Load font into a font pool - need to regen font atlas for this local function load_font(font_path, font_size) if(font_size == 0.0) then font_size = 16.0 end local ctx = renderer.ctx if(ctx == nil) then return nil end -- Reset image local image = nil local new_font = nil nk.nk_font_atlas_init_default(atlas) nk.nk_font_atlas_begin(atlas) -- image = nk.nk_font_atlas_bake(atlas, master_img_width, master_img_height, nk.NK_FONT_ATLAS_RGBA32) -- Reload previous fonts. for i, font in ipairs(fonts) do image, fonts.font = font_loader(atlas, font.path, font.size) local glyph = find_glyph(font.font, 9) local space = get_glyph_xadvance(font.font.handle.userdata, 32) if(glyph) then fix_tab_glyph(glyph, space) end -- atlas[0].config.range = rune_ranges -- nk.nk_font_default_glyph_ranges() end -- local cfg = ffi.new("struct nk_font_config[1]", {nk.nk_font_config(font_size)}) -- cfg[0].merge_mode = nk.nk_true -- cfg.coord_type = nk.NK_COORD_PIXEL image, new_font = font_loader(atlas, font_path, font_size) image = nk.nk_font_atlas_bake(atlas, master_img_width, master_img_height, nk.NK_FONT_ATLAS_RGBA32) local glyph = find_glyph(new_font, 9) local space = get_glyph_xadvance(new_font.handle.userdata, 32) if(glyph) then fix_tab_glyph(glyph, space) end local nk_img = font_atlas_img(image, true) nk.nk_font_atlas_end(atlas, nk_img, nil) nk.nk_font_atlas_cleanup(atlas) nk.nk_style_load_all_cursors(ctx, atlas[0].cursors) nk.nk_style_set_font(ctx, new_font.handle) local tab_size = get_glyph_xadvance(new_font.handle.userdata, 9) -- print(master_img_width[0], master_img_height[0], tab_size) local new_font_tbl = { tab_width = tab_size, font = new_font, path = font_path, size = font_size, cfg = nil } tinsert(fonts, new_font_tbl) return new_font_tbl end -- -------------------------------------------------------------------------------------- renderer.get_font = function( font_id ) return renderer.all_fonts[font_id] end -- -------------------------------------------------------------------------------------- renderer.font.load = function(path, size) local new_font = load_font(path, size) if(new_font == nil) then return nil end new_font.set_tab_width = function(self, width) if(width == self.tab_width) then return end adjust_glyph( self.font.handle.userdata, 9, width ) self.tab_width = width end new_font.get_tab_width = function(self, width) return self.tab_width end new_font.get_width = function(self, text) if(text==nil) then print(debug.traceback()) end local text = checkstring(text) return self.font.handle.width(self.font.handle.userdata, self.font.handle.height, text, #text) end new_font.get_height = function(self) return self.font.handle.height end new_font.get_size = function(self) return self.size end new_font.set_size = function(self, size) self.size = size end new_font.get_path = function(self) return self.path end new_font.get_handle = function(self) return self.font.handle end local font_id = #renderer.all_fonts+1 new_font.get_id = function(self) return font_id end tinsert(renderer.all_fonts, new_font) return setmetatable(new_font, { __index = new_font }) end -- -------------------------------------------------------------------------------------- renderer.show_debug = function(enable) end -- -------------------------------------------------------------------------------------- renderer.get_size = function() return nuklear_renderer.get_size() end -- -------------------------------------------------------------------------------------- -- Not really needed renderer.begin_frame = function() end -- -------------------------------------------------------------------------------------- -- Hrm.. needed? renderer.end_frame = function() end -- -------------------------------------------------------------------------------------- renderer.set_clip_rect = function(x, y, w, h) nuklear_renderer.set_clip_rect(x, y, w, h) end -- -------------------------------------------------------------------------------------- renderer.draw_rect = function(x, y, w, h, color) nuklear_renderer.draw_rect(x, y, w, h, color) end -- -------------------------------------------------------------------------------------- renderer.draw_text = function(font, text, x, y, color) return nuklear_renderer.draw_text(font, text, x, y, color) end -- -------------------------------------------------------------------------------------- renderer.draw_cursor = function() local icolor = nk.nk_rgba(0xff, 0xff, 0xff, 0xff) local r = nk.nk_rect(renderer.ctx.input.mouse.pos.x, renderer.ctx.input.mouse.pos.y, 12, 19) nk.nk_draw_image(renderer.canvas, r, renderer.ctx.style.cursor_active.img, icolor) end -- -------------------------------------------------------------------------------------- renderer.load_image = function(filename, no_ui) local img, img_info = nuklear_renderer.load_image(filename, no_ui) return img, img_info end -- -------------------------------------------------------------------------------------- renderer.load_image_buffer = function(name, buf, bufsize, no_ui) local img, img_info = nuklear_renderer.load_image_buffer(name, buf, bufsize, no_ui) return img, img_info end -- -------------------------------------------------------------------------------------- renderer.draw_image = function(image, x, y, w, h) return nuklear_renderer.draw_image(image, x, y, w, h) end -- -------------------------------------------------------------------------------------- renderer.load_model = function(filename) return threed_renderer.load_model(filename) end -- -------------------------------------------------------------------------------------- renderer.draw_model = function(model, x, y, w, h) return threed_renderer.draw_model(model, x, y, w, h) end -- -------------------------------------------------------------------------------------- renderer.set_cursor = function() nuklear_renderer.set_cursor() end -- -------------------------------------------------------------------------------------- renderer.find_glyph = find_glyph