Skip to content

Instantly share code, notes, and snippets.

@CheezusChrust
Last active September 18, 2023 21:37
Show Gist options
  • Save CheezusChrust/7aabdec7020e0034f2de8e7104bfbbb7 to your computer and use it in GitHub Desktop.
Save CheezusChrust/7aabdec7020e0034f2de8e7104bfbbb7 to your computer and use it in GitHub Desktop.
--@name Mesh Loader
--@author Cheezus
--@shared
local debugPrints = true -- Shows info about tri count and loading progress
if CLIENT then
local globalScale = Vector(0.1) -- Scale all meshes added by this amount, or add a scale vector to individual meshes
local offsetPos = Vector(0, 0, 0) -- Offsets are relative to chip/base
local offsetAngle = Angle(0, 0, 0)
local disableTexFiltering = false -- Disable texture filtering (smoothing) for all meshes
-- color and scale parameters can be added optionally for individual meshes, can leave them out if they aren't used
local meshes = {
{
mdl = "https://f001.backblazeb2.com/file/cheezus-sharex/ShareX/2023/02/15/88014032/gun.obj",
texture = "https://i.imgur.com/lLPN47x.jpeg",
},
{
mdl = "https://f001.backblazeb2.com/file/cheezus-sharex/ShareX/2023/02/15/00154639/barrel.obj",
texture = "sprops/sprops_grid_12x12",
},
}
-- For colorful printing
local W, R, G = Color(255, 255, 255), Color(255, 0, 0), Color(0, 255, 0)
local base = chip()
timer.create("requestBase", 1, 0, function()
net.start("requestBase")
net.send()
end)
net.receive("sendBase", function()
local ent = net.readEntity()
timer.stop("requestBase")
base = isValid(ent) and ent or chip()
for _, v in ipairs(meshes) do
if isValid(v.holo) then
v.holo:setPos(base:localToWorld(offsetPos))
v.holo:setAngles(base:localToWorldAngles(offsetAngle))
end
end
end)
local alreadyDownloadedMats = {}
local buildThread = coroutine.wrap(function()
for k, v in ipairs(meshes) do
local holo = hologram.create(base:localToWorld(offsetPos), base:localToWorldAngles(offsetAngle), "models/Combine_Helicopter/helicopter_bomb01.mdl")
holo:setParent(base)
v.holo = holo
if not alreadyDownloadedMats[v.texture] then
local mat = material.create("VertexLitGeneric")
if not v.texture or v.texture == "" then
v.texture = "hunter/myplastic"
end
if v.texture:sub(1, 4) == "http" then
if hasPermission("material.urlcreate", v.texture) then
mat:setTextureURL("$basetexture", v.texture, function(_, _, width, height, layout)
if not width or not height then
mat:setTexture("$basetexture", "hunter/myplastic")
print(true, W, "Failed to create texture for mesh ", G, k, W, ": ", R, "invalid texture URL")
return
end
local xScale = 1024 / width
local yScale = 1024 / height
layout(0, 0, width * xScale, height * yScale)
alreadyDownloadedMats[v.texture] = k
end)
else
mat:setTexture("$basetexture", "hunter/myplastic")
print(true, W, "Failed to create texture for mesh ", G, k, W, ": ", R, "missing permission material.urlcreate OR url not whitelisted")
end
else
mat:setTexture("$basetexture", v.texture)
end
v.mat = mat
else
v.mat = meshes[alreadyDownloadedMats[v.texture]].mat
print(nil, W, "Reused material for mesh ", G, k)
end
local success = false
local triangles = mesh.trianglesLeft()
try(function()
_, v.finalMesh = next(mesh.createFromObj(v.meshData, true))
success = true
end, function(err)
print(true, W, "Failed to build mesh ", G, k, W, ": ", R, err.message)
end)
if success then
print(nil, W, "Built mesh " .. k, W, ": ", G, triangles - mesh.trianglesLeft(), W, " tris")
end
end
return true
end)
local triangles = mesh.trianglesLeft()
local function doneBuilding()
local triCount = triangles - mesh.trianglesLeft()
setName("Mesh Loader - " .. triCount .. " tris")
print(false, W, "Finished, using ", G, triCount, W, " tris")
for _, v in ipairs(meshes) do
if v.finalMesh then
v.holo:setMesh(v.finalMesh)
v.holo:setMeshMaterial(v.mat)
v.holo:setRenderBounds(Vector(-200), Vector(200))
v.holo:setScale((v.scale or Vector(1)) * globalScale)
v.holo:setColor(v.color or Color(255))
if disableTexFiltering then
v.holo:setFilterMag(0)
v.holo:setFilterMin(0)
end
end
end
end
local function buildMeshes()
setName("Mesh Loader - Building meshes...")
hook.add("think", "build", function()
while chip():getQuotaAverage() < chip():getQuotaMax() / 4 do
if buildThread() then
doneBuilding()
hook.remove("think", "build")
return
end
end
end)
end
local failed
local alreadyDownloaded = {}
local download = coroutine.create(function()
for k, v in ipairs(meshes) do
if not alreadyDownloaded[v.mdl] then
http.get(v.mdl, function(data)
if data[1] == "<" or data[1] == "{" then
failed = true
return
end
v.meshData = data
alreadyDownloaded[v.mdl] = k
print(nil, W, "Downloaded mesh data for mesh ", G, k, W, ", size: ", G, math.round(#data / 1048576, 3) .. "mb")
end, function(err)
print(true, W, "HTTP error - ", R, err)
failed = true
end)
else
v.meshData = meshes[alreadyDownloaded[v.mdl]].meshData
print(nil, W, "Reused mesh data for mesh ", G, k)
end
coroutine.yield()
end
end)
timer.create("downloadMeshes", 0.1, 0, function()
if coroutine.status(download) ~= "dead" then
if http.canRequest() then
coroutine.resume(download)
end
elseif failed then
timer.remove("downloadMeshes")
print(true, W, "Failed to get mesh - ", R, "URL did not return OBJ format")
else
local loaded = true
for _, v in ipairs(meshes) do
if not v.meshData then loaded = false end
end
if loaded then
buildMeshes()
timer.remove("downloadMeshes")
end
end
end)
else
wire.adjustPorts({
Base = "entity"
})
net.receive("requestBase", function(_, ply)
local base = wire.ports.Base
if not isValid(base) then return end
net.start("sendBase")
net.writeEntity(base)
net.send(ply)
end)
end
local oldPrint = print
local col = SERVER and Color(3, 169, 244) or Color(222, 169, 9)
local prefix = SERVER and "[Server] " or "[Client] "
function print(warn, ...)
if #{...} == 0 then
oldPrint(col, prefix, Color(255, 255, 255), warn)
return
end
if (debugPrints and not warn) or warn then
oldPrint(col, prefix, ...)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment