mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
1525 lines
38 KiB
Lua
1525 lines
38 KiB
Lua
|
|
--[[
|
||
|
|
| This file was obtained through the combined efforts
|
||
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
||
|
|
|
|
||
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
||
|
|
|
|
||
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||
|
|
--]]
|
||
|
|
|
||
|
|
-- based on starfall
|
||
|
|
CreateClientConVar("pac_asset_browser_close_on_select", "1")
|
||
|
|
CreateClientConVar("pac_asset_browser_remember_layout", "1")
|
||
|
|
|
||
|
|
local function table_tolist(tbl, sort)
|
||
|
|
local list = {}
|
||
|
|
for key, val in pairs(tbl) do
|
||
|
|
table.insert(list, {key = key, val = val})
|
||
|
|
end
|
||
|
|
|
||
|
|
return list
|
||
|
|
end
|
||
|
|
|
||
|
|
local function table_sortedpairs(tbl, sort)
|
||
|
|
local list = table_tolist(tbl)
|
||
|
|
table.sort(list, sort)
|
||
|
|
local i = 0
|
||
|
|
return function()
|
||
|
|
i = i + 1
|
||
|
|
if list[i] then
|
||
|
|
return list[i].key, list[i].val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local file_Exists
|
||
|
|
do
|
||
|
|
local cache = {}
|
||
|
|
file_Exists = function(path, id)
|
||
|
|
local key = path .. id
|
||
|
|
|
||
|
|
if cache[key] == nil then
|
||
|
|
cache[key] = file.Exists(path, id)
|
||
|
|
end
|
||
|
|
|
||
|
|
return cache[key]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local get_material_keyvalues
|
||
|
|
do
|
||
|
|
local cache = {}
|
||
|
|
get_material_keyvalues = function(path)
|
||
|
|
if cache[path] == nil then
|
||
|
|
cache[path] = Material(path):GetKeyValues()
|
||
|
|
end
|
||
|
|
|
||
|
|
return cache[path]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local L = pace.LanguageString
|
||
|
|
|
||
|
|
local function install_click(icon, path, pattern, on_menu, pathid)
|
||
|
|
local old = icon.OnMouseReleased
|
||
|
|
icon.OnMouseReleased = function(_, code)
|
||
|
|
if code == MOUSE_LEFT then
|
||
|
|
pace.model_browser_callback(path, pathid)
|
||
|
|
elseif code == MOUSE_RIGHT then
|
||
|
|
local menu = DermaMenu()
|
||
|
|
menu:AddOption(L"copy path", function()
|
||
|
|
if pattern then
|
||
|
|
for _, pattern in ipairs(isstring(pattern) and {pattern} or pattern) do
|
||
|
|
local test = path:match(pattern)
|
||
|
|
if test then
|
||
|
|
path = test
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
SetClipboardText(path)
|
||
|
|
end)
|
||
|
|
if on_menu then on_menu(menu) end
|
||
|
|
menu:Open()
|
||
|
|
end
|
||
|
|
|
||
|
|
return old(_, code)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function get_unlit_mat(path)
|
||
|
|
if path:find("%.png$") then
|
||
|
|
return Material(path:match("materials/(.+)"))
|
||
|
|
elseif path:find("%.vmt$") then
|
||
|
|
local tex = Material(path:match("materials/(.+)%.vmt")):GetTexture("$basetexture")
|
||
|
|
if tex then
|
||
|
|
local mat = CreateMaterial(path .. "_pac_asset_browser", "UnlitGeneric")
|
||
|
|
mat:SetTexture("$basetexture", tex)
|
||
|
|
return mat
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return CreateMaterial(path .. "_pac_asset_browser", "UnlitGeneric", {["$basetexture"] = path:match("materials/(.+)%.vtf")})
|
||
|
|
end
|
||
|
|
|
||
|
|
local next_generate_icon = 0
|
||
|
|
local max_generating = 5
|
||
|
|
|
||
|
|
local function setup_paint(panel, generate_cb, draw_cb)
|
||
|
|
local old = panel.Paint
|
||
|
|
panel.Paint = function(self,w,h)
|
||
|
|
if not self.ready_to_draw then return end
|
||
|
|
|
||
|
|
if not self.setup_material then
|
||
|
|
generate_cb(self)
|
||
|
|
self.setup_material = true
|
||
|
|
end
|
||
|
|
|
||
|
|
draw_cb(self, w, h)
|
||
|
|
end
|
||
|
|
|
||
|
|
local old = panel.OnRemove
|
||
|
|
panel.OnRemove = function(...)
|
||
|
|
next_generate_icon = math.max(next_generate_icon - 1, 0)
|
||
|
|
|
||
|
|
if old then
|
||
|
|
old(...)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function create_texture_icon(path, pathid)
|
||
|
|
local icon = vgui.Create("DButton")
|
||
|
|
icon:SetTooltip(path)
|
||
|
|
icon:SetSize(128,128)
|
||
|
|
icon:SetWrap(true)
|
||
|
|
icon:SetText("")
|
||
|
|
|
||
|
|
install_click(icon, path, {"^materials/(.+)%.vtf$", "^materials/(.+%.png)$"}, nil, pathid)
|
||
|
|
|
||
|
|
setup_paint(
|
||
|
|
icon,
|
||
|
|
function(self)
|
||
|
|
self.mat = get_unlit_mat(path)
|
||
|
|
self.realwidth = self.mat:Width()
|
||
|
|
self.realheight = self.mat:Height()
|
||
|
|
end,
|
||
|
|
function(self, W, H)
|
||
|
|
if self.mat then
|
||
|
|
local w = math.min(W, self.realwidth)
|
||
|
|
local h = math.min(H, self.realheight)
|
||
|
|
|
||
|
|
surface.SetDrawColor(255,255,255,255)
|
||
|
|
surface.SetMaterial(self.mat)
|
||
|
|
surface.DrawTexturedRect(W/2 - w/2, H/2 - h/2, w, h)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
return icon
|
||
|
|
end
|
||
|
|
|
||
|
|
surface.CreateFont("pace_asset_browser_fixed_width", {
|
||
|
|
font = "dejavu sans mono",
|
||
|
|
})
|
||
|
|
|
||
|
|
local bad_materials = {}
|
||
|
|
|
||
|
|
local function create_material_icon(path, grid_panel)
|
||
|
|
|
||
|
|
if #pace.model_browser_browse_types_tbl == 1 then
|
||
|
|
if bad_materials[path] ~= nil then
|
||
|
|
local str = file.Read(path, "GAME")
|
||
|
|
if str then
|
||
|
|
local shader = str:match("^(.-){"):Trim():gsub("%p", ""):lower()
|
||
|
|
if not (shader == "vertexlitgeneric" or shader == "unlitgeneric" or shader == "eyerefract" or shader == "refract") then
|
||
|
|
bad_materials[path] = true
|
||
|
|
else
|
||
|
|
bad_materials[path] = false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if bad_materials[path] then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local mat_path = path:match("materials/(.+)%.vmt")
|
||
|
|
|
||
|
|
local icon = vgui.Create("DButton")
|
||
|
|
icon:SetTooltip(path)
|
||
|
|
icon:SetSize(128,128)
|
||
|
|
icon:SetWrap(true)
|
||
|
|
icon:SetText("")
|
||
|
|
|
||
|
|
setup_paint(
|
||
|
|
icon,
|
||
|
|
function(self)
|
||
|
|
self:SetupMaterial()
|
||
|
|
end,
|
||
|
|
function(self, w, h)
|
||
|
|
surface.SetDrawColor(0,0,0,240)
|
||
|
|
surface.DrawRect(0,0,w,h)
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
function icon:SetupMaterial()
|
||
|
|
local mat = Material(mat_path)
|
||
|
|
local shader = mat:GetShader():lower()
|
||
|
|
|
||
|
|
if shader == "vertexlitgeneric" then
|
||
|
|
local pnl = vgui.Create("DModelPanel", icon)
|
||
|
|
pnl:SetMouseInputEnabled(false)
|
||
|
|
pnl:Dock(FILL)
|
||
|
|
pnl:SetLookAt( Vector( 0, 0, 0 ) )
|
||
|
|
pnl:SetFOV(1)
|
||
|
|
pnl:SetModel("models/hunter/plates/plate1x1.mdl")
|
||
|
|
pnl:SetCamPos(Vector(1,0,1) * 1825)
|
||
|
|
pnl.mouseover = false
|
||
|
|
|
||
|
|
local m = Matrix()
|
||
|
|
m:Scale(Vector(1.37,0.99,0.01))
|
||
|
|
pnl.Entity:EnableMatrix("RenderMultiply", m)
|
||
|
|
|
||
|
|
--[[
|
||
|
|
|
||
|
|
local old = icon.OnCursorEntered
|
||
|
|
function icon:OnCursorEntered(...)
|
||
|
|
if pace.current_part:IsValid() and pace.current_part.Materialm then
|
||
|
|
pace.asset_browser_old_mat = pace.asset_browser_old_mat or pace.current_part.Materialm
|
||
|
|
pace.current_part.Materialm = mat
|
||
|
|
end
|
||
|
|
|
||
|
|
old(self, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
|
||
|
|
local old = icon.OnCursorExited
|
||
|
|
function icon:OnCursorExited(...)
|
||
|
|
if pace.current_part:IsValid() and pace.current_part.Materialm then
|
||
|
|
pace.current_part.Materialm = pace.asset_browser_old_mat
|
||
|
|
end
|
||
|
|
old(self, ...)
|
||
|
|
end
|
||
|
|
]]
|
||
|
|
|
||
|
|
local unlit_mat = get_unlit_mat(path)
|
||
|
|
|
||
|
|
function pnl:Paint( w, h )
|
||
|
|
local x, y = self:ScreenToLocal(gui.MouseX(), gui.MouseY())
|
||
|
|
|
||
|
|
if (x > w*8 or y > h*8) or (x < -w*4 or y < -h*4) then
|
||
|
|
surface.SetDrawColor(255,255,255,255)
|
||
|
|
surface.SetMaterial(unlit_mat)
|
||
|
|
surface.DrawTexturedRect(0,0,w,h)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
x = x / w
|
||
|
|
y = y / h
|
||
|
|
|
||
|
|
x = x * 50
|
||
|
|
y = y * 50
|
||
|
|
|
||
|
|
x = x - 25
|
||
|
|
y = y - 55
|
||
|
|
local light_pos = Vector(y, x, 30)
|
||
|
|
|
||
|
|
local pos_x, pos_y = self:LocalToScreen( 0, 0 )
|
||
|
|
cam.Start3D( self.vCamPos, Angle(45, 180, 0), self.fFOV, pos_x, pos_y, w, h, 5, self.FarZ )
|
||
|
|
|
||
|
|
render.SuppressEngineLighting( true )
|
||
|
|
|
||
|
|
|
||
|
|
render.SetColorModulation( 1, 1, 1 )
|
||
|
|
render.SetBlend(1)
|
||
|
|
render.SetLocalModelLights({{
|
||
|
|
color = Vector(1,1,1),
|
||
|
|
pos = self.Entity:GetPos() + light_pos,
|
||
|
|
}})
|
||
|
|
|
||
|
|
self:DrawModel()
|
||
|
|
|
||
|
|
render.SuppressEngineLighting( false )
|
||
|
|
cam.End3D()
|
||
|
|
end
|
||
|
|
|
||
|
|
pnl.PreDrawModel = function() render.ModelMaterialOverride(mat) end
|
||
|
|
pnl.PostDrawModel = function() render.ModelMaterialOverride() end
|
||
|
|
elseif shader == "lightmappedgeneric" or shader == "spritecard" then
|
||
|
|
local pnl = vgui.Create("DPanel", icon)
|
||
|
|
pnl:SetMouseInputEnabled(false)
|
||
|
|
pnl:Dock(FILL)
|
||
|
|
|
||
|
|
local mat = get_unlit_mat(path)
|
||
|
|
|
||
|
|
pnl.Paint = function(self,w,h)
|
||
|
|
surface.SetDrawColor(255,255,255,255)
|
||
|
|
surface.SetMaterial(mat)
|
||
|
|
surface.DrawTexturedRect(0,0,w,h)
|
||
|
|
end
|
||
|
|
|
||
|
|
else
|
||
|
|
local pnl = vgui.Create("DImage", icon)
|
||
|
|
pnl:SetMouseInputEnabled(false)
|
||
|
|
pnl:Dock(FILL)
|
||
|
|
pnl:SetImage(mat_path)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
install_click(icon, path, "^materials/(.+)%.vmt$", function(menu)
|
||
|
|
local function create_text_view(str)
|
||
|
|
local frame = vgui.Create("DFrame")
|
||
|
|
frame:SetTitle(path)
|
||
|
|
frame:SetSize(500, 500)
|
||
|
|
frame:Center()
|
||
|
|
frame:SetSizable(true)
|
||
|
|
|
||
|
|
local scroll = vgui.Create("DScrollPanel", frame)
|
||
|
|
scroll:Dock(FILL)
|
||
|
|
scroll:DockMargin( 0, 5, 5, 5 )
|
||
|
|
|
||
|
|
local text = vgui.Create("DTextEntry", scroll)
|
||
|
|
text:SetMultiline(true)
|
||
|
|
text:SetFont("pace_asset_browser_fixed_width")
|
||
|
|
|
||
|
|
text:SetText(str)
|
||
|
|
|
||
|
|
surface.SetFont(text:GetFont())
|
||
|
|
local _,h = surface.GetTextSize(str)
|
||
|
|
text:SetTall(h+50)
|
||
|
|
text:SetWide(frame:GetWide())
|
||
|
|
|
||
|
|
frame:MakePopup()
|
||
|
|
end
|
||
|
|
|
||
|
|
menu:AddOption("view .vmt", function()
|
||
|
|
create_text_view(file.Read(path, "GAME"):gsub("\t", " "))
|
||
|
|
end)
|
||
|
|
|
||
|
|
menu:AddOption("view keyvalues", function()
|
||
|
|
local tbl = {}
|
||
|
|
for k,v in pairs(get_material_keyvalues(mat_path)) do
|
||
|
|
table.insert(tbl, {k = k, v = v})
|
||
|
|
end
|
||
|
|
table.sort(tbl, function(a,b) return a.k < b.k end)
|
||
|
|
|
||
|
|
local str = ""
|
||
|
|
for _, v in ipairs(tbl) do
|
||
|
|
str = str .. v.k:sub(2) .. ":\n" .. tostring(v.v) .. "\n\n"
|
||
|
|
end
|
||
|
|
|
||
|
|
create_text_view(str)
|
||
|
|
end)
|
||
|
|
end)
|
||
|
|
|
||
|
|
grid_panel:Add(icon)
|
||
|
|
|
||
|
|
return icon
|
||
|
|
end
|
||
|
|
|
||
|
|
local function create_model_icon(path, pathid)
|
||
|
|
local icon = vgui.Create("SpawnIcon")
|
||
|
|
|
||
|
|
icon:SetSize(64, 64)
|
||
|
|
icon:SetModel(path)
|
||
|
|
icon:SetTooltip(path)
|
||
|
|
|
||
|
|
if path:StartWith("addons/") then
|
||
|
|
path = path:match("^addons/.-/(.+)") or path
|
||
|
|
end
|
||
|
|
|
||
|
|
install_click(icon, path, nil, nil, pathid)
|
||
|
|
|
||
|
|
return icon
|
||
|
|
end
|
||
|
|
|
||
|
|
local function update_title(info)
|
||
|
|
if info then
|
||
|
|
info = " - " .. info
|
||
|
|
pace.model_browser:SetTitle(pace.model_browser.title .. info)
|
||
|
|
else
|
||
|
|
pace.model_browser:SetTitle(pace.model_browser.title)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
do
|
||
|
|
local PANEL = {}
|
||
|
|
|
||
|
|
local BaseClass = baseclass.Get( "DPanel" )
|
||
|
|
|
||
|
|
function PANEL:Init()
|
||
|
|
self.zoom = pace.model_browser:GetCookieNumber("zoom", 100)
|
||
|
|
|
||
|
|
self.ContentContainers = {}
|
||
|
|
|
||
|
|
self:SetWide(102)
|
||
|
|
self:SetPaintBackground(false)
|
||
|
|
|
||
|
|
self.ZoomOut = vgui.Create("DButton", self)
|
||
|
|
self.ZoomOut:Dock(RIGHT)
|
||
|
|
self.ZoomOut:DockMargin(2, 2, 2, 2)
|
||
|
|
self.ZoomOut:SetText("-")
|
||
|
|
self.ZoomOut:SetWide(20)
|
||
|
|
self.ZoomOut.DoClick = function()
|
||
|
|
self:SetZoom(self.zoom - 10)
|
||
|
|
end
|
||
|
|
|
||
|
|
self.ZoomText = vgui.Create("DTextEntry", self)
|
||
|
|
self.ZoomText:Dock(RIGHT)
|
||
|
|
self.ZoomText:DockMargin(2, 2, 2, 2)
|
||
|
|
self.ZoomText:SetWide(50)
|
||
|
|
self:SetZoomText(self.zoom)
|
||
|
|
self.ZoomText.OnValueChange = function(value)
|
||
|
|
local new_zoom = tonumber(value:GetText())
|
||
|
|
if new_zoom then
|
||
|
|
self:SetZoom(new_zoom)
|
||
|
|
else
|
||
|
|
self:SetZoomText(self.zoom)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
self.ZoomIn = vgui.Create("DButton", self)
|
||
|
|
self.ZoomIn:Dock(RIGHT)
|
||
|
|
self.ZoomIn:DockMargin(2, 2, 2, 2)
|
||
|
|
self.ZoomIn:SetText("+")
|
||
|
|
self.ZoomIn:SetWide(20)
|
||
|
|
self.ZoomIn.DoClick = function()
|
||
|
|
self:SetZoom(self.zoom + 10)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:AddContentContainer(pnl)
|
||
|
|
if not table.HasValue(self.ContentContainers, pnl) then
|
||
|
|
table.insert(self.ContentContainers, pnl)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:VisibilityCheck()
|
||
|
|
local zoomUsable = false
|
||
|
|
for i,v in ipairs(self.ContentContainers) do
|
||
|
|
if v and v:IsVisible() then
|
||
|
|
zoomUsable = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self:SetVisible(zoomUsable)
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:SetZoom(num)
|
||
|
|
self.zoom = math.Clamp(num, 16, 512)
|
||
|
|
pace.model_browser:SetCookie("zoom", self.zoom)
|
||
|
|
|
||
|
|
self:SetZoomText(self.zoom)
|
||
|
|
|
||
|
|
local toDelete = {}
|
||
|
|
for i,v in ipairs(self.ContentContainers) do
|
||
|
|
if not (v and v:IsValid()) then
|
||
|
|
table.insert(toDelete, i)
|
||
|
|
elseif v:IsVisible() then
|
||
|
|
v:CalcZoom()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
// Clean up any panels that are invalid for what ever reason
|
||
|
|
for i,v in ipairs(toDelete) do
|
||
|
|
table.remove(self.ContentContainers, v)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:SetZoomText(num)
|
||
|
|
self.ZoomText:SetText(math.Round(num, 1) .. "%")
|
||
|
|
end
|
||
|
|
|
||
|
|
vgui.Register( "pac_AssetBrowser_ZoomControls", PANEL, "DPanel" )
|
||
|
|
end
|
||
|
|
|
||
|
|
do
|
||
|
|
local PANEL = {}
|
||
|
|
|
||
|
|
local BaseClass = baseclass.Get( "DScrollPanel" )
|
||
|
|
|
||
|
|
function PANEL:Init()
|
||
|
|
self:SetPaintBackground( false )
|
||
|
|
|
||
|
|
self.IconList = vgui.Create( "DPanel", self:GetCanvas())
|
||
|
|
self.IconList:Dock( TOP )
|
||
|
|
|
||
|
|
function self.IconList:PerformLayout()
|
||
|
|
if not self.invalidate then return end
|
||
|
|
self.invalidate = nil
|
||
|
|
local x, y = 0, 0
|
||
|
|
local max_width = self:GetWide()
|
||
|
|
local height = 0
|
||
|
|
|
||
|
|
local total_width
|
||
|
|
|
||
|
|
for _, child in ipairs(self:GetChildren()) do
|
||
|
|
height = math.max(height, child:GetTall())
|
||
|
|
|
||
|
|
if x + child:GetWide() > max_width then
|
||
|
|
total_width = x - max_width
|
||
|
|
x = 0
|
||
|
|
y = y + height
|
||
|
|
height = 0
|
||
|
|
end
|
||
|
|
|
||
|
|
child:SetPos(x, y)
|
||
|
|
|
||
|
|
x = x + child:GetWide()
|
||
|
|
end
|
||
|
|
|
||
|
|
if total_width then
|
||
|
|
for _, child in ipairs(self:GetChildren()) do
|
||
|
|
local x, y = child:GetPos()
|
||
|
|
child:SetPos(x - total_width/2, y)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
self:SetTall(y + height)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:Add(pnl)
|
||
|
|
pnl.ready_to_draw = true
|
||
|
|
pnl.original_size = {w=pnl:GetWide(),h=pnl:GetTall()}
|
||
|
|
if self.ZoomControls then
|
||
|
|
pnl:SetSize(pnl.original_size.w * self.ZoomControls.zoom * 0.01, pnl.original_size.h * self.ZoomControls.zoom * 0.01)
|
||
|
|
end
|
||
|
|
self.IconList:Add(pnl)
|
||
|
|
self.IconList.invalidate = true
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:SetZoomControls(pnl)
|
||
|
|
self.ZoomControls = pnl
|
||
|
|
pnl:AddContentContainer(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:CalcZoom()
|
||
|
|
if self.ZoomControls then
|
||
|
|
for i,v in ipairs(self.IconList:GetChildren()) do
|
||
|
|
v:SetSize(v.original_size.w * self.ZoomControls.zoom * 0.01, v.original_size.h * self.ZoomControls.zoom * 0.01)
|
||
|
|
end
|
||
|
|
self.IconList.invalidate = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:OnMouseWheeled(delta)
|
||
|
|
if input.IsControlDown() and self.ZoomControls then
|
||
|
|
self.ZoomControls:SetZoom(self.ZoomControls.zoom + delta * 4)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
return BaseClass.OnMouseWheeled(self, delta)
|
||
|
|
end
|
||
|
|
|
||
|
|
function PANEL:Clear()
|
||
|
|
for k,v in ipairs(self.IconList:GetChildren()) do
|
||
|
|
v:Remove()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
vgui.Register( "pac_AssetBrowser_ContentContainer", PANEL, "DScrollPanel" )
|
||
|
|
end
|
||
|
|
|
||
|
|
function pace.AssetBrowser(callback, browse_types_str, part_key)
|
||
|
|
browse_types_str = browse_types_str or "models;materials;textures;sound"
|
||
|
|
local browse_types = browse_types_str:Split(";")
|
||
|
|
|
||
|
|
if not pac.asset_browser_cache then
|
||
|
|
if file.Exists("pac3_cache/pac_asset_browser_index.txt", "DATA") then
|
||
|
|
pac.asset_browser_cache = util.JSONToTable(file.Read("pac3_cache/pac_asset_browser_index.txt", "DATA")) or {}
|
||
|
|
else
|
||
|
|
pac.asset_browser_cache = {}
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local texture_view = false
|
||
|
|
local material_view = table.HasValue(browse_types, "materials")
|
||
|
|
local sound_view = table.HasValue(browse_types, "sound")
|
||
|
|
|
||
|
|
if table.RemoveByValue(browse_types, "textures") then
|
||
|
|
texture_view = true
|
||
|
|
if not material_view then
|
||
|
|
table.insert(browse_types, "materials")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if pace.model_browser_browse_types ~= browse_types_str and pace.model_browser and pace.model_browser:IsValid() then
|
||
|
|
pace.model_browser:Remove()
|
||
|
|
end
|
||
|
|
|
||
|
|
local addModel
|
||
|
|
|
||
|
|
pace.model_browser_callback = function(...)
|
||
|
|
callback = callback or print
|
||
|
|
|
||
|
|
if callback(...) == false then return end
|
||
|
|
|
||
|
|
if GetConVar("pac_asset_browser_close_on_select"):GetBool() then
|
||
|
|
pace.model_browser:SetVisible(false)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
pace.model_browser_browse_types = browse_types_str
|
||
|
|
pace.model_browser_browse_types_tbl = browse_types
|
||
|
|
pace.model_browser_part_key = part_key
|
||
|
|
|
||
|
|
if pace.model_browser and pace.model_browser:IsValid() then
|
||
|
|
pace.model_browser:SetVisible(true)
|
||
|
|
pace.model_browser:MakePopup()
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local divider
|
||
|
|
|
||
|
|
local frame = vgui.Create("DFrame")
|
||
|
|
frame.title = L"asset browser" .. " - " .. (browse_types_str:gsub(";", " "))
|
||
|
|
|
||
|
|
if GetConVar("pac_asset_browser_remember_layout"):GetBool() then
|
||
|
|
frame:SetCookieName("pac_asset_browser")
|
||
|
|
end
|
||
|
|
|
||
|
|
local x = frame:GetCookieNumber("x", ScrW() - ScrW()/2.75)
|
||
|
|
local y = frame:GetCookieNumber("y", 0)
|
||
|
|
local w = frame:GetCookieNumber("w", ScrW()/2.75)
|
||
|
|
local h = frame:GetCookieNumber("h", ScrH())
|
||
|
|
|
||
|
|
x = math.Clamp(x, 0, ScrW())
|
||
|
|
y = math.Clamp(y, 0, ScrH())
|
||
|
|
|
||
|
|
w = math.Clamp(w, 50, ScrW())
|
||
|
|
h = math.Clamp(h, 50, ScrH())
|
||
|
|
|
||
|
|
frame:SetPos(x, y)
|
||
|
|
frame:SetSize(w, h)
|
||
|
|
|
||
|
|
frame:SetDeleteOnClose(false)
|
||
|
|
frame:SetSizable(true)
|
||
|
|
|
||
|
|
local last_x
|
||
|
|
local last_y
|
||
|
|
local last_w
|
||
|
|
local last_h
|
||
|
|
local div_x
|
||
|
|
local last_div_x
|
||
|
|
|
||
|
|
local old_think = frame.Think
|
||
|
|
frame.Think = function(...)
|
||
|
|
local x,y = frame:GetPos()
|
||
|
|
local w,h = frame:GetSize()
|
||
|
|
|
||
|
|
div_x = divider:GetLeftWidth()
|
||
|
|
|
||
|
|
if x ~= last_x then frame:SetCookie("x", x) last_x = x end
|
||
|
|
if y ~= last_y then frame:SetCookie("y", y) last_y = y end
|
||
|
|
if w ~= last_w then frame:SetCookie("w", w) last_w = w end
|
||
|
|
if h ~= last_h then frame:SetCookie("h", h) last_h = h end
|
||
|
|
if div_x ~= last_div_x then frame:SetCookie("div", div_x) last_div_x = div_x end
|
||
|
|
|
||
|
|
return old_think(...)
|
||
|
|
end
|
||
|
|
pace.model_browser = frame
|
||
|
|
update_title()
|
||
|
|
|
||
|
|
function frame:OnClose()
|
||
|
|
self:SetVisible(false)
|
||
|
|
end
|
||
|
|
|
||
|
|
local menu_bar = vgui.Create("DMenuBar", frame)
|
||
|
|
menu_bar:Dock(TOP)
|
||
|
|
local file_menu = menu_bar:AddMenu(L"file")
|
||
|
|
file_menu:AddOption(L"clear search cache", function()
|
||
|
|
Derma_Query(
|
||
|
|
L"Are you sure you want to clear? A good time to clear is when there is a big TF2 update or you've decided to permanently unmount some games to avoid them showing up in the search results.",
|
||
|
|
L"clear search cache",
|
||
|
|
|
||
|
|
L"clear", function()
|
||
|
|
file.Delete("pac3_cache/pac_asset_browser_index.txt")
|
||
|
|
pac.asset_browser_cache = {}
|
||
|
|
end,
|
||
|
|
|
||
|
|
L"cancel", function()
|
||
|
|
|
||
|
|
end
|
||
|
|
)
|
||
|
|
end):SetImage(pace.MiscIcons.clear)
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
local options_menu = menu_bar:AddMenu(L"options")
|
||
|
|
options_menu:SetDeleteSelf(false)
|
||
|
|
options_menu:AddCVar(L"close browser on select", "pac_asset_browser_close_on_select", "1", "0")
|
||
|
|
options_menu:AddCVar(L"remember layout", "pac_asset_browser_remember_layout", "1", "0")
|
||
|
|
|
||
|
|
|
||
|
|
local zoom_controls = vgui.Create("pac_AssetBrowser_ZoomControls", menu_bar)
|
||
|
|
zoom_controls:Dock(RIGHT)
|
||
|
|
|
||
|
|
|
||
|
|
--[[
|
||
|
|
local tool_bar = vgui.Create("DPanel", frame)
|
||
|
|
tool_bar:Dock(TOP)
|
||
|
|
|
||
|
|
local browse_back = vgui.Create("DImageButton", tool_bar)
|
||
|
|
browse_back:SetImage("icon16/arrow_left.png")
|
||
|
|
browse_back:SizeToContents()
|
||
|
|
browse_back.DoClick = function()
|
||
|
|
|
||
|
|
end
|
||
|
|
]]
|
||
|
|
local left_panel = vgui.Create("DPanel", frame)
|
||
|
|
left_panel:Dock(LEFT)
|
||
|
|
left_panel:SetSize(190, 10)
|
||
|
|
left_panel:DockMargin(0, 0, 4, 0)
|
||
|
|
left_panel.Paint = function () end
|
||
|
|
|
||
|
|
local tree = vgui.Create("DTree", left_panel)
|
||
|
|
tree:Dock(FILL)
|
||
|
|
tree:DockMargin(0, 0, 0, 0)
|
||
|
|
tree:SetBackgroundColor(Color(240, 240, 240))
|
||
|
|
frame.tree = tree
|
||
|
|
tree.OnNodeSelected = function (self, node)
|
||
|
|
if not IsValid(node.propPanel) then return end
|
||
|
|
|
||
|
|
if IsValid(frame.PropPanel.selected) then
|
||
|
|
frame.PropPanel.selected:SetVisible(false)
|
||
|
|
frame.PropPanel.selected = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
frame.PropPanel.selected = node.propPanel
|
||
|
|
|
||
|
|
frame.dir = node.dir
|
||
|
|
frame.pathid = node.pathid or node.GetPathID and node:GetPathID() or "GAME"
|
||
|
|
|
||
|
|
frame.PropPanel.selected:Dock(FILL)
|
||
|
|
frame.PropPanel.selected:SetVisible(true)
|
||
|
|
zoom_controls:VisibilityCheck()
|
||
|
|
|
||
|
|
divider:SetRight(frame.PropPanel.selected)
|
||
|
|
|
||
|
|
if node.dir then
|
||
|
|
local pathid = frame.pathid or "GAME"
|
||
|
|
if pathid == "GAME" then pathid = "all" end
|
||
|
|
|
||
|
|
update_title("browsing " .. pathid .. "/" .. node.dir .. "/*")
|
||
|
|
else
|
||
|
|
update_title()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--local root_node = tree:AddNode("content", "icon16/folder_database.png")
|
||
|
|
--root_node:SetExpanded(true)
|
||
|
|
|
||
|
|
local root_node = tree
|
||
|
|
|
||
|
|
frame.PropPanel = vgui.Create("DPanel", frame)
|
||
|
|
frame.PropPanel:Dock(FILL)
|
||
|
|
|
||
|
|
function frame.PropPanel:Paint (w, h)
|
||
|
|
draw.RoundedBox(0, 0, 0, w, h, Color(240, 240, 240))
|
||
|
|
end
|
||
|
|
|
||
|
|
divider = vgui.Create("DHorizontalDivider", frame)
|
||
|
|
divider:Dock(FILL)
|
||
|
|
divider:SetLeftWidth(frame:GetCookieNumber("div", 140))
|
||
|
|
divider:SetLeftMin(0)
|
||
|
|
divider:SetRightMin(0)
|
||
|
|
|
||
|
|
divider:SetLeft(left_panel)
|
||
|
|
divider:SetRight(frame.PropPanel)
|
||
|
|
|
||
|
|
|
||
|
|
local sound_name_list = vgui.Create("DListView", frame.PropPanel)
|
||
|
|
sound_name_list:AddColumn(L"name")
|
||
|
|
sound_name_list:Dock(FILL)
|
||
|
|
sound_name_list:SetMultiSelect(false)
|
||
|
|
sound_name_list:SetVisible(false)
|
||
|
|
|
||
|
|
local sound_list = vgui.Create("DListView", frame.PropPanel)
|
||
|
|
sound_list:AddColumn(L"path")
|
||
|
|
sound_list:AddColumn(L"byte size")
|
||
|
|
sound_list:Dock(FILL)
|
||
|
|
sound_list:SetMultiSelect(false)
|
||
|
|
sound_list:SetVisible(false)
|
||
|
|
|
||
|
|
local function AddGeneric(self, sound, ...)
|
||
|
|
local line = self:AddLine(sound, ...)
|
||
|
|
local play = vgui.Create("DImageButton", line)
|
||
|
|
play:SetImage("icon16/control_play.png")
|
||
|
|
play:SizeToContents()
|
||
|
|
play:Dock(LEFT)
|
||
|
|
|
||
|
|
function play.Start()
|
||
|
|
for _, v in pairs(self:GetLines()) do
|
||
|
|
v.play:Stop()
|
||
|
|
end
|
||
|
|
|
||
|
|
play:SetImage("icon16/control_stop.png")
|
||
|
|
|
||
|
|
local snd = CreateSound(pac.LocalPlayer, sound)
|
||
|
|
snd:Play()
|
||
|
|
pace.asset_browser_snd = snd
|
||
|
|
|
||
|
|
timer.Create("pac_asset_browser_play", SoundDuration(sound), 1, function()
|
||
|
|
if play:IsValid() then
|
||
|
|
play:Stop()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
function play.Stop()
|
||
|
|
play:SetImage("icon16/control_play.png")
|
||
|
|
|
||
|
|
if pace.asset_browser_snd then
|
||
|
|
pace.asset_browser_snd:Stop()
|
||
|
|
timer.Remove("pac_asset_browser_play")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
line.OnMousePressed = function(_, code)
|
||
|
|
self:ClearSelection()
|
||
|
|
self:SelectItem(line)
|
||
|
|
|
||
|
|
if code == MOUSE_RIGHT then
|
||
|
|
play:Start()
|
||
|
|
else
|
||
|
|
pace.model_browser_callback(sound, "GAME")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local label = line.Columns[1]
|
||
|
|
label:SetTextInset(play:GetWide() + 5, 0)
|
||
|
|
|
||
|
|
play.DoClick = function()
|
||
|
|
if timer.Exists("pac_asset_browser_play") and self:GetLines()[self:GetSelectedLine()] == line then
|
||
|
|
play:Stop()
|
||
|
|
return
|
||
|
|
end
|
||
|
|
self:ClearSelection()
|
||
|
|
self:SelectItem(line)
|
||
|
|
|
||
|
|
play:Start()
|
||
|
|
end
|
||
|
|
|
||
|
|
line.play = play
|
||
|
|
end
|
||
|
|
|
||
|
|
function sound_name_list:AddSound(name)
|
||
|
|
AddGeneric(self, name)
|
||
|
|
end
|
||
|
|
|
||
|
|
function sound_list:AddSound(path, pathid)
|
||
|
|
local sound_path = path:match("sound/(.+)")
|
||
|
|
|
||
|
|
AddGeneric(self, sound_path, file.Size(path, pathid))
|
||
|
|
end
|
||
|
|
|
||
|
|
local select_me
|
||
|
|
|
||
|
|
if texture_view or material_view then
|
||
|
|
local node = root_node:AddNode("materials", "icon16/folder_database.png")
|
||
|
|
node.dir = "materials"
|
||
|
|
|
||
|
|
local viewPanel = vgui.Create("pac_AssetBrowser_ContentContainer", frame.PropPanel)
|
||
|
|
viewPanel:DockMargin(5, 0, 0, 0)
|
||
|
|
viewPanel:SetVisible(false)
|
||
|
|
viewPanel:SetZoomControls(zoom_controls)
|
||
|
|
|
||
|
|
node.propPanel = viewPanel
|
||
|
|
|
||
|
|
for list_name, materials in pairs(pace.Materials) do
|
||
|
|
local list = node:AddNode(list_name)
|
||
|
|
list.dir = "materials"
|
||
|
|
list.propPanel = viewPanel
|
||
|
|
|
||
|
|
list.OnNodeSelected = function()
|
||
|
|
if viewPanel and viewPanel.currentNode and viewPanel.currentNode == list then return end
|
||
|
|
|
||
|
|
viewPanel:Clear(true)
|
||
|
|
viewPanel.currentNode = list
|
||
|
|
|
||
|
|
if material_view then
|
||
|
|
for _, material_name in ipairs(materials) do
|
||
|
|
local path = "materials/" .. material_name .. ".vmt"
|
||
|
|
if file_Exists(path, "GAME") then
|
||
|
|
create_material_icon(path, viewPanel)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if texture_view then
|
||
|
|
local done = {}
|
||
|
|
local textures = {}
|
||
|
|
|
||
|
|
for _, material_name in ipairs(materials) do
|
||
|
|
for k, v in pairs(get_material_keyvalues(material_name)) do
|
||
|
|
if type(v) == "ITexture" then
|
||
|
|
local name = v:GetName()
|
||
|
|
if not done[name] then
|
||
|
|
done[name] = true
|
||
|
|
table.insert(textures, "materials/" .. name .. ".vtf")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
for _, path in ipairs(textures) do
|
||
|
|
viewPanel:Add(create_texture_icon(path))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
tree:OnNodeSelected(list)
|
||
|
|
viewPanel.currentNode = list
|
||
|
|
end
|
||
|
|
|
||
|
|
if #browse_types == 1 and list_name == "materials" and (texture_view or material_view) then
|
||
|
|
select_me = list
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if table.HasValue(browse_types, "models") then
|
||
|
|
|
||
|
|
local spawnlists = root_node:AddNode("spawnlists")
|
||
|
|
spawnlists.info = {}
|
||
|
|
spawnlists.info.id = 0
|
||
|
|
spawnlists.dir = "models"
|
||
|
|
|
||
|
|
local has_game = {}
|
||
|
|
|
||
|
|
has_game[""] = true
|
||
|
|
|
||
|
|
for k, v in pairs(engine.GetGames()) do
|
||
|
|
if v.mounted then
|
||
|
|
has_game[v.folder] = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function fillNavBar(propTable, parentNode)
|
||
|
|
for k, v in table_sortedpairs(propTable, function(a, b) return a.key < b.key end) do
|
||
|
|
if v.parentid == parentNode.info.id and has_game[v.needsapp] then
|
||
|
|
local node = parentNode:AddNode(v.name, v.icon)
|
||
|
|
node:SetExpanded(true)
|
||
|
|
node.info = v
|
||
|
|
node.dir = "models"
|
||
|
|
|
||
|
|
node.propPanel = vgui.Create(vgui.GetControlTable("ContentContainer") and "ContentContainer" or "pac_AssetBrowser_ContentContainer", frame.PropPanel)
|
||
|
|
node.propPanel:DockMargin(5, 0, 0, 0)
|
||
|
|
node.propPanel:SetVisible(false)
|
||
|
|
|
||
|
|
parentNode.propPanel = node.propPanel
|
||
|
|
|
||
|
|
node.OnNodeSelected = function()
|
||
|
|
if not node.setup then
|
||
|
|
node.setup = true
|
||
|
|
for i, object in table_sortedpairs(v.contents, function(a, b) return a.key < b.key end) do
|
||
|
|
if object.type == "model" then
|
||
|
|
node.propPanel:Add(create_model_icon(object.model))
|
||
|
|
elseif object.type == "header" then
|
||
|
|
if not object.text or not isstring(object.text) then return end
|
||
|
|
|
||
|
|
local label = vgui.Create("ContentHeader", node.propPanel)
|
||
|
|
label:SetText(object.text)
|
||
|
|
|
||
|
|
node.propPanel:Add(label)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
tree:OnNodeSelected(node)
|
||
|
|
end
|
||
|
|
|
||
|
|
if #browse_types == 1 and v.name == "Construction Props" then
|
||
|
|
select_me = node
|
||
|
|
end
|
||
|
|
|
||
|
|
fillNavBar(propTable, node)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
fillNavBar(spawnmenu.GetPropTable(), spawnlists)
|
||
|
|
end
|
||
|
|
|
||
|
|
if sound_view then
|
||
|
|
local node = root_node:AddNode("game sounds", "icon16/sound.png")
|
||
|
|
node.dir = "sound names"
|
||
|
|
node.propPanel = sound_name_list
|
||
|
|
|
||
|
|
node.OnNodeSelected = function()
|
||
|
|
local categories = {}
|
||
|
|
|
||
|
|
for _, sound_name in ipairs(sound.GetTable()) do
|
||
|
|
local category = sound_name:match("^(.-)%.") or sound_name:match("^(.-)_") or sound_name:match("^(.-)%u")
|
||
|
|
if not category or category == nil then category = "misc" end
|
||
|
|
|
||
|
|
categories[category] = categories[category] or {}
|
||
|
|
table.insert(categories[category], sound_name)
|
||
|
|
end
|
||
|
|
|
||
|
|
local sorted = {}
|
||
|
|
|
||
|
|
for name, sounds in pairs(categories) do
|
||
|
|
table.sort(sounds, function(a, b) return a < b end)
|
||
|
|
table.insert(sorted, {name = name, sounds = sounds})
|
||
|
|
end
|
||
|
|
|
||
|
|
table.sort(sorted, function(a, b) return a.name < b.name end)
|
||
|
|
|
||
|
|
for _, data in ipairs(sorted) do
|
||
|
|
local category_name, sounds = data.name, data.sounds
|
||
|
|
|
||
|
|
local node = node:AddNode(category_name, "icon16/sound.png")
|
||
|
|
node.dir = "sound names"
|
||
|
|
node.propPanel = sound_name_list
|
||
|
|
|
||
|
|
node.OnNodeSelected = function()
|
||
|
|
sound_name_list:Clear()
|
||
|
|
for _, sound_name in ipairs(sounds) do
|
||
|
|
sound_name_list:AddSound(sound_name)
|
||
|
|
end
|
||
|
|
|
||
|
|
tree:OnNodeSelected(node)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
do -- mounted
|
||
|
|
local function addBrowseContent(viewPanel, node, name, icon, path, pathid)
|
||
|
|
local function on_select(self, node)
|
||
|
|
if viewPanel and viewPanel.currentNode and viewPanel.currentNode == node then return end
|
||
|
|
|
||
|
|
node.dir = self.dir
|
||
|
|
sound_list:Clear()
|
||
|
|
viewPanel:Clear(true)
|
||
|
|
viewPanel.currentNode = node
|
||
|
|
|
||
|
|
local searchString = node:GetFolder()
|
||
|
|
|
||
|
|
if searchString == "" and #browse_types == 1 then
|
||
|
|
local count = 0
|
||
|
|
local function find_recursive(path, pathid)
|
||
|
|
if count >= 500 then return end
|
||
|
|
local files_, folders_ = file.Find(path .. "/*", pathid)
|
||
|
|
if files_ then
|
||
|
|
for i,v in ipairs(files_) do
|
||
|
|
count = count + 1
|
||
|
|
|
||
|
|
local path = path .. "/" .. v
|
||
|
|
|
||
|
|
path = path:gsub("^.-(" .. browse_types[1] .. "/.+)$", "%1")
|
||
|
|
|
||
|
|
if browse_types[1] == "models" then
|
||
|
|
if not IsUselessModel(path) then
|
||
|
|
viewPanel:Add(create_model_icon(path, pathid))
|
||
|
|
end
|
||
|
|
elseif browse_types[1] == "materials" then
|
||
|
|
if path:find("%.vmt$") then
|
||
|
|
if material_view then
|
||
|
|
create_material_icon(path, viewPanel)
|
||
|
|
end
|
||
|
|
elseif texture_view then
|
||
|
|
viewPanel:Add(create_texture_icon(path, pathid))
|
||
|
|
end
|
||
|
|
elseif browse_types[1] == "sound" then
|
||
|
|
sound_list:AddSound(path, pathid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
for i,v in ipairs(folders_) do
|
||
|
|
find_recursive(path .. "/" .. v, pathid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
find_recursive(path .. browse_types[1], node:GetPathID())
|
||
|
|
else
|
||
|
|
local files, folders = file.Find(searchString .. "/*", node:GetPathID())
|
||
|
|
|
||
|
|
if files then
|
||
|
|
--[[
|
||
|
|
for _, dir in pairs(folders) do
|
||
|
|
|
||
|
|
local SPAWNICON = vgui.GetControlTable("SpawnIcon")
|
||
|
|
local icon = vgui.Create("DButton", viewPanel)
|
||
|
|
icon:SetSize(64,64)
|
||
|
|
icon:SetText(dir)
|
||
|
|
icon:SetWrap(true)
|
||
|
|
|
||
|
|
icon.Paint = SPAWNICON.Paint
|
||
|
|
icon.PaintOver = SPAWNICON.PaintOver
|
||
|
|
|
||
|
|
icon.DoClick = function()
|
||
|
|
for _, child in pairs(node.ChildNodes:GetChildren()) do
|
||
|
|
if child:GetFolder() == ((node:GetFolder() == "" and dir) or (node:GetFolder() .. "/" .. dir)) then
|
||
|
|
prev_node = tree.Tree:GetSelectedItem()
|
||
|
|
tree.Tree:SetSelectedItem(child)
|
||
|
|
node:SetExpanded(true)
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
viewPanel:Add(icon)
|
||
|
|
end
|
||
|
|
]]
|
||
|
|
if self.dir == "models" then
|
||
|
|
for k, v in pairs(files) do
|
||
|
|
local path = node:GetFolder() .. "/" .. v
|
||
|
|
|
||
|
|
if not path:StartWith("models/pac3_cache/") then
|
||
|
|
if not IsUselessModel(path) then
|
||
|
|
viewPanel:Add(create_model_icon(path, pathid))
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif self.dir == "materials" then
|
||
|
|
for k, v in pairs(files) do
|
||
|
|
local path = node:GetFolder() .. "/" .. v
|
||
|
|
|
||
|
|
if v:find("%.vmt$") then
|
||
|
|
if material_view then
|
||
|
|
create_material_icon(path, viewPanel)
|
||
|
|
end
|
||
|
|
elseif texture_view then
|
||
|
|
viewPanel:Add(create_texture_icon(path, pathid))
|
||
|
|
end
|
||
|
|
|
||
|
|
end
|
||
|
|
elseif self.dir == "sound" then
|
||
|
|
for k, v in pairs(files) do
|
||
|
|
local path = node:GetFolder() .. "/" .. v
|
||
|
|
sound_list:AddSound(path, pathid)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if self.dir == "sound" then
|
||
|
|
node.propPanel = sound_list
|
||
|
|
else
|
||
|
|
node.propPanel = viewPanel
|
||
|
|
end
|
||
|
|
|
||
|
|
tree:OnNodeSelected(node)
|
||
|
|
viewPanel.currentNode = node
|
||
|
|
end
|
||
|
|
|
||
|
|
node = node:AddNode(name, icon)
|
||
|
|
node:SetFolder("")
|
||
|
|
node:SetPathID(pathid)
|
||
|
|
node.viewPanel = viewPanel
|
||
|
|
|
||
|
|
for _, dir in ipairs(browse_types) do
|
||
|
|
local files, folders = file.Find(path .. dir .. "/*", pathid)
|
||
|
|
if files and (files[1] or folders[1]) then
|
||
|
|
local parent = node
|
||
|
|
|
||
|
|
local node = node:AddFolder(dir, path .. dir, pathid, false)
|
||
|
|
node.dir = dir
|
||
|
|
node.OnNodeSelected = on_select
|
||
|
|
|
||
|
|
if not select_me and #browse_types == 1 and name == "all" and browse_types[1] == dir and dir ~= "models" then
|
||
|
|
select_me = node
|
||
|
|
end
|
||
|
|
|
||
|
|
if not select_me and #browse_types == 3 and name == "all" and dir == "materials" then
|
||
|
|
select_me = node
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
node.OnNodeSelected = on_select
|
||
|
|
end
|
||
|
|
|
||
|
|
local viewPanel = vgui.Create("pac_AssetBrowser_ContentContainer", frame.PropPanel)
|
||
|
|
viewPanel:DockMargin(5, 0, 0, 0)
|
||
|
|
viewPanel:SetVisible(false)
|
||
|
|
viewPanel:SetZoomControls(zoom_controls)
|
||
|
|
|
||
|
|
do
|
||
|
|
local special = {
|
||
|
|
{
|
||
|
|
title = "all",
|
||
|
|
folder = "GAME",
|
||
|
|
icon = "games/16/all.png",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title = "downloaded",
|
||
|
|
folder = "DOWNLOAD",
|
||
|
|
icon = "materials/icon16/server_go.png",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title = "workshop",
|
||
|
|
folder = "WORKSHOP",
|
||
|
|
icon = "materials/icon16/plugin.png",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title = "thirdparty",
|
||
|
|
folder = "THIRDPARTY",
|
||
|
|
icon = "materials/icon16/folder_brick.png",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title = "mod",
|
||
|
|
folder = "MOD",
|
||
|
|
icon = "materials/icon16/folder_brick.png",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, info in ipairs(special) do
|
||
|
|
addBrowseContent(viewPanel, root_node, info.title, info.icon, "", info.folder)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
do
|
||
|
|
local games = engine.GetGames()
|
||
|
|
table.insert(games, {
|
||
|
|
title = "Garry's Mod",
|
||
|
|
folder = "garrysmod",
|
||
|
|
mounted = true
|
||
|
|
})
|
||
|
|
|
||
|
|
for _, game in table_sortedpairs(games, function(a, b) return a.val.title < b.val.title end) do
|
||
|
|
if game.mounted then
|
||
|
|
addBrowseContent(viewPanel, root_node, game.title, "games/16/" .. (game.icon or game.folder) .. ".png", "", game.folder)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local node = root_node:AddNode("addons")
|
||
|
|
|
||
|
|
for _, addon in table_sortedpairs(engine.GetAddons(), function(a, b) return a.val.title < b.val.title end) do
|
||
|
|
if addon.file:StartWith("addons/") then
|
||
|
|
local _, dirs = file.Find("*", addon.title)
|
||
|
|
|
||
|
|
if
|
||
|
|
table.HasValue(dirs, "materials") or
|
||
|
|
table.HasValue(dirs, "models") or
|
||
|
|
table.HasValue(dirs, "sound")
|
||
|
|
then
|
||
|
|
addBrowseContent(viewPanel, node, addon.title, "icon16/bricks.png", "", addon.title)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local _, folders = file.Find("addons/*", "MOD")
|
||
|
|
|
||
|
|
for _, path in ipairs(folders) do
|
||
|
|
if
|
||
|
|
file.IsDir("addons/" .. path .. "/materials", "MOD") or
|
||
|
|
file.IsDir("addons/" .. path .. "/sound", "MOD") or
|
||
|
|
file.IsDir("addons/" .. path .. "/models", "MOD")
|
||
|
|
then
|
||
|
|
addBrowseContent(viewPanel, node, path, "icon16/folder.png", "addons/" .. path .. "/", "MOD")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local model_view = vgui.Create("pac_AssetBrowser_ContentContainer", frame.PropPanel)
|
||
|
|
model_view:DockMargin(5, 0, 0, 0)
|
||
|
|
model_view:SetVisible(false)
|
||
|
|
model_view:SetZoomControls(zoom_controls)
|
||
|
|
|
||
|
|
local search = vgui.Create("DTextEntry", left_panel)
|
||
|
|
search:Dock(TOP)
|
||
|
|
search:SetTooltip("Press enter to search")
|
||
|
|
search.propPanel = model_view
|
||
|
|
search.model_view = model_view
|
||
|
|
search.delay_functions = {}
|
||
|
|
|
||
|
|
file_menu:AddOption(L"build search cache", function()
|
||
|
|
search:StartSearch("", "models/", {}, "GAME", function(path, pathid) end)
|
||
|
|
search:StartSearch("", "sound/", {}, "GAME", function(path, pathid) end)
|
||
|
|
search:StartSearch("", "materials/", {}, "GAME", function(path, pathid) end)
|
||
|
|
end)
|
||
|
|
|
||
|
|
local cancel = vgui.Create("DImageButton", search)
|
||
|
|
cancel:SetImage(pace.MiscIcons.clear)
|
||
|
|
cancel:SetSize(16, 16)
|
||
|
|
cancel.DoClick = function() search:Cancel() end
|
||
|
|
cancel:SetVisible(false)
|
||
|
|
|
||
|
|
do
|
||
|
|
local old = search.OnGetFocus
|
||
|
|
function search:OnGetFocus ()
|
||
|
|
if self:GetValue() == self.default_text then
|
||
|
|
self:SetValue("")
|
||
|
|
end
|
||
|
|
old(self)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
do
|
||
|
|
local old = search.OnLoseFocus
|
||
|
|
function search:OnLoseFocus ()
|
||
|
|
if self:GetValue() == "" then
|
||
|
|
self:SetValue(self.default_text)
|
||
|
|
end
|
||
|
|
old(self)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function find(path, pathid)
|
||
|
|
local key = path .. pathid
|
||
|
|
|
||
|
|
if pac.asset_browser_cache[key] then
|
||
|
|
return unpack(pac.asset_browser_cache[key])
|
||
|
|
end
|
||
|
|
|
||
|
|
local files, folders = file.Find(path, pathid)
|
||
|
|
|
||
|
|
pac.asset_browser_cache[key] = {files, folders}
|
||
|
|
|
||
|
|
return files, folders
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:PerformLayout()
|
||
|
|
cancel:SetPos(self:GetWide() - 16 - 2, 2)
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:StartSearch(search_text, folder, extensions, pathid, cb)
|
||
|
|
|
||
|
|
cancel:SetVisible(true)
|
||
|
|
|
||
|
|
local files, folders = find(folder .. "*", pathid)
|
||
|
|
|
||
|
|
self.searched = true
|
||
|
|
|
||
|
|
if files then
|
||
|
|
update_title(table.Count(self.delay_functions) .. " directories left - " .. folder .. "*")
|
||
|
|
|
||
|
|
for k, v in ipairs(files) do
|
||
|
|
local file = folder .. v
|
||
|
|
for _, ext in ipairs(extensions) do
|
||
|
|
if v:EndsWith(ext) and file:find(search_text, nil, true) then
|
||
|
|
local func = function() return cb(file, pathid) end
|
||
|
|
self.delay_functions[func] = func
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
for k, v in ipairs(folders) do
|
||
|
|
if v ~= "pac3_cache" then
|
||
|
|
local func = function()
|
||
|
|
self:StartSearch(search_text, folder .. v .. "/", extensions, pathid, cb)
|
||
|
|
end
|
||
|
|
self.delay_functions[func] = func
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:Stop()
|
||
|
|
cancel:SetVisible(false)
|
||
|
|
|
||
|
|
self.delay_functions = {}
|
||
|
|
self.searched = false
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:Cancel(why)
|
||
|
|
self:Stop()
|
||
|
|
|
||
|
|
if why then
|
||
|
|
update_title("search canceled: " .. why)
|
||
|
|
else
|
||
|
|
update_title("search canceled")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:Think()
|
||
|
|
if input.IsKeyDown(KEY_ESCAPE) then
|
||
|
|
self:Cancel()
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if input.IsControlDown() and input.IsKeyDown(KEY_F) then
|
||
|
|
self:RequestFocus()
|
||
|
|
end
|
||
|
|
|
||
|
|
local i = 0
|
||
|
|
for key, func in pairs(self.delay_functions) do
|
||
|
|
i = i + 1
|
||
|
|
local ok, reason = func()
|
||
|
|
|
||
|
|
if ok == false then
|
||
|
|
self:Cancel(reason)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
self.delay_functions[func] = nil
|
||
|
|
if i > 50 then break end
|
||
|
|
end
|
||
|
|
|
||
|
|
if i == 0 and self.searched then
|
||
|
|
self:Stop()
|
||
|
|
update_title()
|
||
|
|
file.Write("pac3_cache/pac_asset_browser_index.txt", util.TableToJSON(pac.asset_browser_cache))
|
||
|
|
end
|
||
|
|
|
||
|
|
if frame.dir then
|
||
|
|
if not self:IsEnabled() then
|
||
|
|
self:SetEnabled(true)
|
||
|
|
end
|
||
|
|
local change = false
|
||
|
|
if self:GetValue() == "" or self:GetValue() == self.default_text then
|
||
|
|
change = true
|
||
|
|
end
|
||
|
|
|
||
|
|
local pathid = frame.pathid or "GAME"
|
||
|
|
if pathid == "GAME" then pathid = "all" end
|
||
|
|
|
||
|
|
self.default_text = L("search " .. pathid .. "/" .. frame.dir .. "/*")
|
||
|
|
if change then
|
||
|
|
self:SetValue(self.default_text)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
self:SetValue("")
|
||
|
|
if self:IsEnabled() then
|
||
|
|
self:SetEnabled(false)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function search:OnEnter()
|
||
|
|
if self:GetValue() == "" then return end
|
||
|
|
|
||
|
|
local count = 0
|
||
|
|
local pathid = frame.pathid or "GAME"
|
||
|
|
local dir = frame.dir
|
||
|
|
|
||
|
|
if dir == "models" then
|
||
|
|
self.propPanel = self.model_view
|
||
|
|
self.propPanel:Clear()
|
||
|
|
self:StartSearch(self:GetValue(), "models/", {".mdl"}, pathid, function(path, pathid)
|
||
|
|
if count >= 500 then return false, "too many results (" .. count .. ")" end
|
||
|
|
count = count + 1
|
||
|
|
if not IsUselessModel(path) then
|
||
|
|
self.propPanel:Add(create_model_icon(path, pathid))
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
elseif dir == "sound" then
|
||
|
|
self.propPanel = sound_list
|
||
|
|
self.propPanel:Clear()
|
||
|
|
self:StartSearch(self:GetValue(), "sound/", {".wav", ".mp3", ".ogg"}, pathid, function(path, pathid)
|
||
|
|
if count >= 1500 then return false, "too many results (" .. count .. ")" end
|
||
|
|
count = count + 1
|
||
|
|
sound_list:AddSound(path, pathid)
|
||
|
|
end)
|
||
|
|
elseif dir == "materials" then
|
||
|
|
self.propPanel = self.model_view
|
||
|
|
self.propPanel:Clear()
|
||
|
|
|
||
|
|
self:StartSearch(self:GetValue(), "materials/", {".vmt", ".vtf", ".png"}, pathid, function(path, pathid)
|
||
|
|
if count >= 750 then return false, "too many results (" .. count .. ")" end
|
||
|
|
if path:EndsWith(".vmt") then
|
||
|
|
if material_view then
|
||
|
|
count = count + 1
|
||
|
|
create_material_icon(path, self.propPanel)
|
||
|
|
end
|
||
|
|
elseif texture_view then
|
||
|
|
self.propPanel:Add(create_texture_icon(path, pathid))
|
||
|
|
count = count + 1
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
elseif dir == "sound names" then
|
||
|
|
self.propPanel = sound_name_list
|
||
|
|
self.propPanel:Clear()
|
||
|
|
|
||
|
|
local search_text = self:GetValue()
|
||
|
|
for _, name in ipairs(sound.GetTable()) do
|
||
|
|
if count >= 1500 then update_title("too many results (" .. count .. ")") return end
|
||
|
|
if name:find(search_text, nil, true) then
|
||
|
|
count = count + 1
|
||
|
|
sound_name_list:AddSound(name)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
self.dir = dir
|
||
|
|
self.pathid = pathid
|
||
|
|
tree:OnNodeSelected(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
file_menu:AddSpacer()
|
||
|
|
file_menu:AddOption(L"exit", function() frame:Remove() end):SetImage(pace.MiscIcons.exit)
|
||
|
|
|
||
|
|
if select_me then
|
||
|
|
select_me:GetParentNode():SetExpanded(true)
|
||
|
|
select_me:SetExpanded(true)
|
||
|
|
tree:SetSelectedItem(select_me)
|
||
|
|
end
|
||
|
|
|
||
|
|
frame:MakePopup()
|
||
|
|
end
|
||
|
|
|
||
|
|
if pace.model_browser and pace.model_browser:IsValid() then
|
||
|
|
local visible = pace.model_browser:IsVisible()
|
||
|
|
pace.model_browser:Remove()
|
||
|
|
|
||
|
|
if visible then
|
||
|
|
pace.AssetBrowser(function(...) print(...) return false end)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
concommand.Add("pac_asset_browser", function(_, _, args)
|
||
|
|
pace.AssetBrowser(function(path) SetClipboardText(path) update_title("copied " .. path .. " to clipboard!") return false end, args[1] and table.concat(args, ";"))
|
||
|
|
pace.model_browser:SetSize(ScrW()/1.25, ScrH()/1.25)
|
||
|
|
pace.model_browser:Center()
|
||
|
|
end)
|
||
|
|
|
||
|
|
list.Set(
|
||
|
|
"DesktopWindows",
|
||
|
|
"PACAssetBrowser",
|
||
|
|
{
|
||
|
|
title = "Asset Browser",
|
||
|
|
icon = "icon16/images.png",
|
||
|
|
width = 960,
|
||
|
|
height = 700,
|
||
|
|
onewindow = true,
|
||
|
|
init = function(icn, pnl)
|
||
|
|
pnl:Remove()
|
||
|
|
RunConsoleCommand("pac_asset_browser")
|
||
|
|
end
|
||
|
|
}
|
||
|
|
)
|