Advertisement
CloneTrooper1019

"Roblox Character to Source Engine SMD File" script

Dec 20th, 2014
785
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 11.25 KB | None | 0 0
  1. ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  2. -- @CloneTrooper1019, 2014
  3. -- (WARNING: THIS IS IN BETA)
  4. --
  5. -- This script pulls a 3D Thumbnail from a roblox profile, and converts it into a StudioMDL file (.smd), which can be compiled into a Source Engine Game! It rigs the model too :)
  6. -- Steps to use:
  7. --
  8. -- 1: Go to your Roblox Studio Settings, and make sure your "Maximum Output Lines" is set to around 99999 or higher
  9. -- 2: Make sure your output window is visible.
  10. -- 3: Start a Roblox Server
  11. -- 4: If the hint tells you to enable the HttpService, go into the Explorer, select the HttpService and make sure that HttpEnabled is checked
  12. -- 5: When the hint above says "WRITING STUDIOMDL DATA", quickly right click the output and press "Clear Output"
  13. -- 6: Wait for it to finish processing.
  14. --
  15. -- The output will now display a full .smd file of the character you inputted.
  16. -- Most of you probably won't know what to do with it, and thats fine. This script is for advanced users :P
  17. -- You can look at these pages if you'd like to proceed further.
  18. --
  19. -- https://developer.valvesoftware.com/wiki/Crowbar
  20. -- https://developer.valvesoftware.com/wiki/QC
  21. --
  22. ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  23.  
  24. USER_ID = 2032622
  25.  
  26. ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  27.  
  28. h = game:GetService("HttpService")
  29.  
  30. bones = {
  31.     Torso1 =
  32.     {
  33.         Name = "Torso";
  34.         Offset = Vector3.new();
  35.         Link = 0;
  36.     },
  37.     LeftArm1 =
  38.     {
  39.         Name = "Left Shoulder";
  40.         Offset = Vector3.new(1,0.5,0);
  41.         Link = 1;
  42.     },
  43.     RightArm1 =
  44.     {
  45.         Name = "Right Shoulder";
  46.         Offset = Vector3.new(-1,0.5,0);
  47.         Link = 2;  
  48.     },
  49.     LeftLeg1 =
  50.     {
  51.         Name = "Left Hip";
  52.         Offset = Vector3.new(1,-1,0);
  53.         Link = 3;
  54.     },
  55.     RightLeg1 =
  56.     {
  57.         Name = "Right Hip";
  58.         Offset = Vector3.new(-1,-1,0);
  59.         Link = 4;
  60.     },
  61.     Head1 =
  62.     {
  63.         Name = "Neck";
  64.         Offset = Vector3.new(0,1,0);
  65.         Link = 5;
  66.     }
  67. }
  68.  
  69. tempFile =
  70. [[version 1
  71. nodes
  72. 0 "Torso" -1
  73. 1 "Left Shoulder" 0
  74. 2 "Right Shoulder" 0
  75. 3 "Left Hip" 0
  76. 4 "Right Hip" 0
  77. 5 "Neck" 0
  78. end
  79. skeleton
  80. time 0]]
  81.  
  82. function canGetAsync()
  83.     local can = pcall(function ()
  84.         return h:GetAsync("http://www.google.com")
  85.     end)
  86.     return can
  87. end
  88.  
  89. if not canGetAsync() then
  90.     local yieldHint = Instance.new("Hint",workspace)
  91.     while not game:findFirstChild("NetworkServer") do
  92.         yieldHint.Text = "["..script:GetFullName().."] Can't run compiler until a server is running. Read the script for more information."
  93.         wait()
  94.     end
  95.     while not canGetAsync() do
  96.         yieldHint.Text = "["..script:GetFullName().."] Please make sure that HttpService.HttpEnabled is checked!"
  97.         wait()
  98.     end
  99.     yieldHint:Destroy()
  100. end
  101.  
  102. function ridiculousJSONAsync(url,tag,decodeAgain)
  103.     local t = h:JSONDecode(h:GetAsync(url))
  104.     local async = h:GetAsync(t[tag])
  105.     return (decodeAgain and h:JSONDecode(async) or async)
  106. end
  107.  
  108. function parseOBJ(objFile,scale,origin)
  109.     -- Parses an OBJ File into a data array.
  110.     local scale = scale or 1
  111.     local origin = origin or Vector3.new()
  112.     local obj = {
  113.         Verts = {};
  114.         Norms = {};
  115.         Texs = {};
  116.         Faces = {};
  117.     }
  118.     local currentMtl = "";
  119.     local currentGroup = "root";
  120.     for line in objFile:gmatch("[^\r\n]+") do
  121.         if #line > 0 then
  122.             local info = {}
  123.             local tag = ""
  124.             local process = ""
  125.             local readChars = 0
  126.             for char in line:gmatch(".") do
  127.                 readChars = readChars + 1
  128.                 if char == " " then
  129.                     if tag == "" then
  130.                         tag = process
  131.                     else
  132.                         table.insert(info,tonumber(process) or process)
  133.                     end
  134.                     process = ""
  135.                 else
  136.                     process = process .. char
  137.                     if readChars == #line then
  138.                         table.insert(info,tonumber(process) or process)
  139.                     end
  140.                 end
  141.             end
  142.             if tag == "usemtl" then
  143.                 currentMtl = info[1]
  144.             elseif tag == "g" then
  145.                 local group = info[1]
  146.                 currentGroup = info[1]
  147.             elseif tag == "v" then
  148.                 local vec = Vector3.new(unpack(info))
  149.                 vec = (vec - origin) * scale
  150.                 table.insert(obj.Verts,{vec.X,vec.Y,vec.Z})
  151.             elseif tag == "vn" then
  152.                 local vec = Vector3.new(unpack(info))*scale
  153.                 table.insert(obj.Norms,{vec.X,vec.Y,vec.Z})
  154.             elseif tag == "vt" then
  155.                 table.insert(obj.Texs,info)
  156.             elseif tag == "f" then
  157.                 local face = {
  158.                     Material = currentMtl;
  159.                     Group = currentGroup;
  160.                     Coords = {};
  161.                 }
  162.                 for _,pair in pairs(info) do
  163.                     local triangle = {}
  164.                     local v,t,n
  165.                     if type(pair) == "number" then
  166.                         v = tonumber(pair)
  167.                     elseif string.find(pair,"//") then
  168.                         v,n = string.match(pair,"(%S+)//(%S+)")
  169.                     else
  170.                         v,t,n = string.match(pair,"(%S+)/(%S+)/(%S+)")
  171.                         if not v or not t or not n then
  172.                             v,t = string.match(pair,"(%S+)/(%S+)")
  173.                         end
  174.                     end
  175.                     triangle.Vert = tonumber(v)
  176.                     triangle.Tex = tonumber(t)
  177.                     triangle.Norm = tonumber(n)
  178.                     table.insert(face.Coords,triangle)
  179.                 end
  180.                 table.insert(obj.Faces,face)
  181.             end
  182.         end
  183.     end
  184.     return obj
  185. end
  186.  
  187. function parseMTL(mtlFile)
  188.     -- Parses an OBJ File into a data array.
  189.     local mtl = {}
  190.     local currentMtl = ""
  191.     local dump
  192.     for line in mtlFile:gmatch("[^\r\n]+") do
  193.         if #line > 0 then
  194.             local info = {}
  195.             local tag = ""
  196.             local process = ""
  197.             local readChars = 0
  198.             for char in line:gmatch(".") do
  199.                 readChars = readChars + 1
  200.                 if char == " " then
  201.                     if tag == "" then
  202.                         tag = process
  203.                     else
  204.                         table.insert(info,tonumber(process) or process)
  205.                     end
  206.                     process = ""
  207.                 else
  208.                     process = process .. char
  209.                     if readChars == #line then
  210.                         table.insert(info,tonumber(process) or process)
  211.                     end
  212.                 end
  213.             end
  214.             if tag == "newmtl" then
  215.                 if dump then
  216.                     table.insert(mtl,dump);
  217.                 end
  218.                 dump = {};
  219.                 dump.Material = info[1];
  220.             elseif tag == "map_d" then
  221.                 dump.HashTex = info[1];
  222.             end
  223.         end
  224.     end
  225.     return mtl;
  226. end
  227.  
  228. function NewFileWriter(initial)
  229.     local file = initial or ""
  230.     local writer = {}
  231.     function writer:Add(...)
  232.         for _,line in pairs{...} do
  233.             if file == "" then
  234.                 file = file .. line
  235.             else
  236.                 file = file .. "\n" .. line
  237.             end
  238.         end
  239.     end
  240.     function writer:Dump()
  241.         return file
  242.     end
  243.     return writer
  244. end
  245.  
  246.  
  247. function float(num)
  248.     -- Obj Files have some insanely low numbers sometimes.
  249.     -- I need to cap them to around 7 decimal places.
  250.     if math.floor(num) == num then
  251.         return tostring(num)
  252.     else
  253.         local omg = 10^99
  254.         local fix = math.floor(num*omg)/omg
  255.         local fl = tostring(math.floor(fix))
  256.         local str = tostring(fix)
  257.         while (#str - #fl) < 7 do
  258.             str = str .. "0"
  259.         end
  260.         return str
  261.     end
  262. end
  263.  
  264. function unwrap(this)
  265.     local str = ""
  266.     for _,v in pairs(this) do
  267.         if str ~= "" then
  268.             str = str .. " "
  269.         end
  270.         str = str .. float(v)
  271.     end
  272.     return str
  273. end
  274.  
  275. function calculateOrigin(obj,group)
  276.     local x,y,z = {},{},{}
  277.     local function avg(dump)
  278.         local a = 0
  279.         for _,v in pairs(dump) do
  280.             a = a + v
  281.         end
  282.         local total = #dump/2
  283.         a = a / #dump
  284.         return a
  285.     end
  286.     for _,face in pairs(obj.Faces) do
  287.         if face.Group == group then
  288.             for _,coord in pairs(face.Coords) do
  289.                 local vert = obj.Verts[coord.Vert]
  290.                 table.insert(x,vert[1])
  291.                 table.insert(y,vert[2])
  292.                 table.insert(z,vert[3])
  293.             end
  294.         end
  295.     end
  296.     return Vector3.new(avg(x),avg(y),avg(z))
  297. end
  298.  
  299. function dumpVector3(v3)
  300.     return float(v3.X).." "..float(v3.Y).." "..float(v3.Z)
  301. end
  302.  
  303. function WriteCharacterSMD(userId)
  304.     local data = ridiculousJSONAsync("http://rproxy.tk/avatar-thumbnail-3d/json?userId=" .. userId,"Url",true)
  305.     local objFile = ridiculousJSONAsync("http://rproxy.tk/thumbnail/resolve-hash/" .. data.obj,"Url")
  306.     local a = data.aabb.min;
  307.     local b = data.aabb.max;
  308.     local scale = 10
  309.     local origin do
  310.         a = Vector3.new(a.x,a.y,a.z)
  311.         b = Vector3.new(b.x,b.y,b.z)
  312.         origin = a:lerp(b,.5)
  313.     end
  314.     local obj = parseOBJ(objFile,scale,origin)
  315.     local file = NewFileWriter(tempFile)
  316.     local ignoreMtl do
  317.         local avatar = h:GetAsync("http://rproxy.tk/Asset/AvatarAccoutrements.ashx?userId=2032622")
  318.         local gearId = string.match(avatar,"?id=(%d+)&equipped=1")
  319.         if gearId then
  320.             local gearData = ridiculousJSONAsync("http://rproxy.tk/asset-thumbnail-3d/json?assetId=" .. gearId,"Url",true)
  321.             local hashTex = gearData.textures[1]
  322.             if hashTex then
  323.                 local mtlFile = ridiculousJSONAsync("http://rproxy.tk/thumbnail/resolve-hash/" .. data.mtl,"Url")
  324.                 local mtl = parseMTL(mtlFile)
  325.                 for _,v in pairs(mtl) do
  326.                     if v.HashTex == hashTex then
  327.                         ignoreMtl = v.Material
  328.                         break
  329.                     end
  330.                 end
  331.             end
  332.         end
  333.     end
  334.     local groups = {}
  335.     for _,face in pairs(obj.Faces) do
  336.         if not groups[face.Group] then
  337.             groups[face.Group] = true;
  338.         end
  339.     end
  340.     local isPlayerType = (groups["Player11"] ~= nil)
  341.     local fix = {}
  342.     for k in pairs(groups) do
  343.         table.insert(fix,k)
  344.     end
  345.     groups = fix
  346.     local function getLink(group)
  347.         if bones[group] then
  348.             return bones[group].Link
  349.         elseif string.find(group,"Player1") then
  350.             local num = tonumber(string.match(group,"Player1(%d+)"))
  351.             local ref = { [0] = "Head1",[1] = "Torso1",[2] = "LeftArm1",[3] = "RightArm1",[4] = "LeftLeg1",[5] = "RightLeg1" }
  352.             if num then
  353.                 local key = ref[#groups-num]
  354.                 if key then
  355.                     return bones[key].Link
  356.                 else
  357.                     return 5
  358.                 end
  359.             end
  360.         else
  361.             return 5
  362.         end
  363.     end
  364.     local function getRealName(name)
  365.         if not isPlayerType then
  366.             return name
  367.         else
  368.             local refs = {Head1 = 0, Torso1 = 1, LeftArm1 = 2, RightArm1 = 3, LeftLeg1 = 4, RightLeg1 = 5}
  369.             local ref = refs[name]
  370.             if ref then            
  371.                 return "Player1" .. #groups - refs[name]
  372.             else
  373.                 return name
  374.             end
  375.         end
  376.     end
  377.     local lines = {}
  378.     for name,data in pairs(bones) do
  379.         name = getRealName(name)
  380.         local o = (data.Offset * scale)
  381.         if name == "Torso1" or name == getRealName("Torso1") then
  382.             o = o + calculateOrigin(obj,name)
  383.         end
  384.         local bone = data.Link .." " .. dumpVector3(o) .. "0 0 0"
  385.         table.insert(lines,bone)
  386.     end
  387.     file:Add("end","triangles")
  388.     local h = Instance.new("Hint",workspace)
  389.     h.Archivable = false
  390.     for count,face in pairs(obj.Faces) do
  391.         if count % 50 == 0 then
  392.             h.Text = "PROCESSING STUDIOMDL DATA ("..count.."/"..#obj.Faces..")"
  393.             wait()
  394.         end
  395.         if face.Material  ~= ignoreMtl and face.Group ~= "Humanoidrootpart1" then
  396.             file:Add(face.Material)
  397.             local link = getLink(face.Group)
  398.             for _,coord in pairs(face.Coords) do
  399.                 local vert = obj.Verts[coord.Vert]
  400.                 local norm = obj.Norms[coord.Norm]
  401.                 local tex = obj.Texs[coord.Tex]
  402.                 file:Add(link .. " " .. unwrap(vert) .. "  " .. unwrap(norm) .. "  " .. unwrap(tex))
  403.             end
  404.         end
  405.     end
  406.     h.Text = "DONE. CHECK THE OUTPUT"
  407.     game:GetService("Debris"):AddItem(h,1)
  408.     file:Add("end")
  409.     print(file:Dump())
  410. end
  411.  
  412. WriteCharacterSMD(USER_ID)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement