SMD_Loader

Run Settings
LanguageLua
Language Version
Run Command
--!strict local AssetService = game:GetService("AssetService") local CollectionService = game:GetService("CollectionService") local HTTP = game:GetService("HttpService") local Compression if not script:FindFirstChild("Compression") and HTTP.HttpEnabled then Compression = loadstring(HTTP:GetAsync("https://pastebin.com/raw/Lxk41Jq0",true))() else require(script.Compression) end local SMD_DATA_ATTRIB = "SMD_Data" local SMD_TAG = "SMD_MeshPart" local main = {} main.Compression = Compression main.Util = {} export type DataBlock = "version" | "nodes" | "skeleton" | "triangles" export type SMD_Model = { Data: {number} -- This array stores the Vertices in this order [VERT_X,VERT_Y,VERT_Z,NORMAL_X,NORMAL_Y,NORMAL_Z,U,V] } -- this function converts a string to base64 local function ToBase64(data: string): string 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 -- this function converts base64 to string local function FromBase64(data: string): string local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' data = string.gsub(data, '[^'..b..'=]', '') return (data:gsub('.', function(x) if (x == '=') then return '' end local r,f='',(b:find(x)::number-1) for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end return r; end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) if (#x ~= 8) then return '' end local c=0 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end return string.char(c) end)) end local function GetLine(str: string,find: string,start: number?): number -- TOTALLY scuffed implementation and can prob be optimized, but idrc so uhh yeah start = start or 1 local lines = string.split(str,"\n") for i = start :: number,#lines do local line = lines[i] if line == find or string.match(line,find) then return i end end return -1 end local function GetDataBlockContent(source: string, block: DataBlock): {string} -- Oh boy this has so much potentional to break local lines = string.split(source,"\n") local startLine = GetLine(source,block) + 1 local endLine = GetLine(source,"end",startLine) - 1 local contentLines: {string} = {} local chunkSize = math.round((endLine - startLine) / 5) for chunk = startLine,endLine,chunkSize do local chunkStart = chunk local chunkEnd = math.min(endLine,chunk + chunkSize) print("Reading chunk " .. math.round(chunk / chunkSize)) for line = chunkStart,chunkEnd do task.spawn(function() local relLineNumber = 1 + (line - startLine) contentLines[relLineNumber] = lines[line] end) end task.wait() end return contentLines end local function GetTriangles(source: string): {{number}} -- I hate myself for this local verts: {{number}} = {} local dataBlock = GetDataBlockContent(source,"triangles") for i = 1,#dataBlock do local vertRaw = dataBlock[i] local nums = string.split(vertRaw," ") if #nums < 2 then continue end local data: {number} = {} local index = 1 for i,strNum in pairs(nums) do local num = tonumber(strNum) if num then table.insert(data,index,num) index += 1 end end table.insert(verts,data) end print(verts[1]) return verts end function main.Load(source: string): SMD_Model local startTime = time() local model: SMD_Model = { Data = {} } local data = GetTriangles(source) for i,triangle in pairs(data) do -- Vertex Position model.Data[#model.Data + 1] = triangle[3] model.Data[#model.Data + 1] = triangle[4] model.Data[#model.Data + 1] = triangle[2] -- Vertex Normal model.Data[#model.Data + 1] = triangle[6] model.Data[#model.Data + 1] = triangle[7] model.Data[#model.Data + 1] = triangle[5] -- Vertex UV model.Data[#model.Data + 1] = triangle[8] model.Data[#model.Data + 1] = -triangle[9] end if #model.Data % 3 ~= 0 then error("Why isn't your model triangles? For what reason man.") end print(string.format("Model loaded after %s seconds",tostring(time() - startTime))) return model end function main.LoadFromURI(uri: string): SMD_Model local uriSplit = string.split(uri,"/") local fileName = uriSplit[#uriSplit] print("Loading " .. fileName) return main.Util.ModelFromData(HTTP:GetAsync(uri,true)) end function main.MeshFromModel(model: SMD_Model,textureId: number?): MeshPart local editableMesh = AssetService:CreateEditableMesh() local colorId = editableMesh:AddColor(Color3.new(1,1,1),1) local vertexIds: {number} = {} local normalIds: {number} = {} local uvIds: {number} = {} local vertIndex = 1 for i = 1,#model.Data,8 do local vertex = model.Data[i] table.insert(vertexIds,vertIndex,editableMesh:AddVertex(Vector3.new(model.Data[i+0],model.Data[i+1],model.Data[i+2]))) table.insert(normalIds,vertIndex,editableMesh:AddNormal(Vector3.new(model.Data[i+3],model.Data[i+4],model.Data[i+5]))) table.insert(uvIds,vertIndex,editableMesh:AddUV(Vector2.new(model.Data[i+6],model.Data[i+7]))) vertIndex += 1 end for i = 1,#vertexIds,3 do local faceId = editableMesh:AddTriangle(vertexIds[i],vertexIds[i+1],vertexIds[i+2]) editableMesh:SetFaceNormals(faceId,{normalIds[i],normalIds[i+1],normalIds[i+2]}) editableMesh:SetFaceUVs(faceId,{uvIds[i],uvIds[i+1],uvIds[i+2]}) end print(editableMesh:RemoveUnused()) local meshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh)) if textureId then meshPart.TextureContent = Content.fromAssetId(textureId) end meshPart:SetAttribute(SMD_DATA_ATTRIB,main.Util.ModelToString(model)) CollectionService:AddTag(meshPart,SMD_TAG) --editableMesh:Destroy() -- To prevent memory leaks -- Message from future Zen : this was a stupid f-ing bug return meshPart end function main.ReloadAllMeshParts() for _,meshPart in CollectionService:GetTagged(SMD_TAG) do if not meshPart:IsA("MeshPart") then continue end main.ReloadMeshPart(meshPart) end end function main.ReloadMeshPart(meshPart: MeshPart) if not meshPart:GetAttribute(SMD_DATA_ATTRIB) then return end local data = meshPart:GetAttribute(SMD_DATA_ATTRIB) :: string local tempMeshPart = main.MeshFromModel(main.Util.ModelFromData(data)) meshPart:ApplyMesh(tempMeshPart) tempMeshPart:Destroy() end function main.Util.ModelToString(model: SMD_Model): string local vertices = table.concat(model.Data,",") local compressed = ToBase64(Compression.Zlib.Compress(vertices,{ level = 9, strategy = "huffman_only" })) return compressed end function main.Util.ModelFromData(data: string): SMD_Model local model: SMD_Model = { Data = {} } local rawData = string.split(main.Util.DecodeData(data),",") for i = 1,#rawData do model.Data[i] = tonumber(rawData[i]) :: number end return model end function main.Util.DecodeData(data: string) return Compression.Zlib.Decompress(FromBase64(data)) end return main
Editor Settings
Theme
Key bindings
Full width
Lines