sketchfab

download sketchfab models

当前为 2024-04-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name sketchfab
  3. // @version 1.0
  4. // @description download sketchfab models
  5. // @author Kvali
  6. // @include /^https?://(www\.)?sketchfab\.com/.*
  7. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.0.2/jszip-utils.min.js
  9. // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js
  10. // @run-at document-start
  11. // @grant unsafeWindow
  12. // @grant GM_download
  13. // @namespace https://gf.qytechs.cn/users/956968
  14. // @license MIT
  15. // ==/UserScript==
  16.  
  17. var zip = new JSZip();
  18. let folder = zip.folder('collection');
  19.  
  20. var button_dw = false;
  21. var func_drawGeometry = /(this\._stateCache\.drawGeometry\(this\._graphicContext,t\))/g;
  22. var fund_drawArrays = /t\.drawArrays\(t\.TRIANGLES,0,6\)/g;
  23. var func_renderInto1 = /x\.renderInto\(n,S,y/g;
  24. var func_renderInto2 = /g\.renderInto=function\(e,i,r/g;
  25. var func_getResourceImage = /getResourceImage:function\(e,t\){/g;
  26.  
  27. var func_test = /apply:function\(e\){var t=e instanceof r\.Geometry;/g
  28.  
  29. var addbtnfunc;
  30.  
  31. (function() {
  32. 'use strict';
  33. var window = unsafeWindow;
  34. console.log("[UserScript]init", window);
  35.  
  36. window.allmodel = [];
  37. var saveimagecache2 = {};
  38. var objects = {};
  39.  
  40. var saveimage_to_list = function(url,file_name)
  41. {
  42. if (!saveimagecache2[url])
  43. {
  44. var mdl = {
  45. name: file_name
  46. }
  47.  
  48. saveimagecache2[url] = mdl;
  49. }
  50. }
  51.  
  52. addbtnfunc = function() {
  53. var p = document.evaluate("//div[@class='titlebar']", document, null, 9, null).singleNodeValue;
  54. if(p && !button_dw) {
  55. console.log("[UserScript]add btn dwnld");
  56. var btn = document.createElement("a");
  57. btn.setAttribute("class", "control");
  58. btn.innerHTML = "DOWNLOAD";
  59. btn.style.backgroundColor = "#1caad9";
  60. btn.style.color = "white";
  61. btn.style.padding = "8px";
  62. btn.style.borderRadius = "4px";
  63. btn.style.cursor = "pointer";
  64. btn.style.transition = "background-color 0.2s ease-in-out";
  65. btn.addEventListener("mouseenter", function() {
  66. btn.style.backgroundColor = "#1c88bb";
  67. });
  68. btn.addEventListener("mouseleave", function() {
  69. btn.style.backgroundColor = "#1caad9";
  70. });
  71. btn.addEventListener("click", dodownload, false);
  72. p.appendChild(btn);
  73. button_dw = true;
  74. } else {
  75. console.log("[UserScript]try add btn later");
  76. setTimeout(addbtnfunc, 3000);
  77. }
  78. }
  79.  
  80. var dodownload = function() {
  81. console.log("[UserScript]download");
  82. var idx = 0;
  83. window.allmodel.forEach(function(obj)
  84. {
  85. var mdl = {
  86. name: "model_"+idx,
  87. obj:parseobj(obj)
  88. }
  89. console.log(mdl);
  90. dosavefile(mdl);
  91. idx++;
  92.  
  93. })
  94. PackAll();
  95. }
  96.  
  97. var PackAll = function ()
  98. {
  99. for (var obj in objects) {
  100. console.log("[UserScript]save file", obj);
  101. folder.file(obj, objects[obj], {binary:true});
  102. }
  103.  
  104. var file_name = document.getElementsByClassName('model-name__label')[0].textContent;
  105. folder.generateAsync({ type: "blob" }).then(content => saveAs(content, file_name + ".zip"));
  106. }
  107.  
  108. var parseobj = function(obj)
  109. {
  110. console.log("[UserScript]: obj", obj);
  111. var list = [];
  112. obj._primitives.forEach(function(p) {
  113. if(p && p.indices) {
  114. list.push({
  115. 'mode' : p.mode,
  116. 'indices' : p.indices._elements
  117. });
  118. }
  119. })
  120.  
  121. var attr = obj._attributes;
  122. return {
  123. vertex: attr.Vertex._elements,
  124. normal: attr.Normal ? attr.Normal._elements : [],
  125. uv: attr.TexCoord0 ? attr.TexCoord0._elements :
  126. attr.TexCoord1 ? attr.TexCoord1._elements :
  127. attr.TexCoord2 ? attr.TexCoord2._elements :
  128. attr.TexCoord2 ? attr.TexCoord2._elements :
  129. attr.TexCoord3 ? attr.TexCoord3._elements :
  130. attr.TexCoord4 ? attr.TexCoord4._elements :
  131. attr.TexCoord5 ? attr.TexCoord5._elements :
  132. attr.TexCoord6 ? attr.TexCoord6._elements :
  133. attr.TexCoord7 ? attr.TexCoord7._elements :
  134. attr.TexCoord8 ? attr.TexCoord8._elements : [],
  135. primitives: list,
  136. };
  137. }
  138.  
  139. var dosavefile = function(mdl)
  140. {
  141. var obj = mdl.obj;
  142.  
  143. var str = '';
  144. str += 'mtllib ' + mdl.name + '.mtl\n';
  145. str += 'o ' + mdl.name + '\n';
  146. for (var i = 0; i < obj.vertex.length; i += 3) {
  147. str += 'v ';
  148. for (var j = 0; j < 3; ++j) {
  149. str += obj.vertex[i + j] + ' ';
  150. }
  151. str += '\n';
  152. }
  153. for (i = 0; i < obj.normal.length; i += 3) {
  154. str += 'vn ';
  155. for (j = 0; j < 3; ++j) {
  156. str += obj.normal[i + j] + ' ';
  157. }
  158. str += '\n';
  159. }
  160.  
  161. for (i = 0; i < obj.uv.length; i += 2) {
  162. str += 'vt ';
  163. for (j = 0; j < 2; ++j) {
  164. str += obj.uv[i + j] + ' ';
  165. }
  166. str += '\n';
  167. }
  168.  
  169. str += 's on \n';
  170.  
  171. var vn = obj.normal.length != 0;
  172. var vt = obj.uv.length != 0;
  173.  
  174. for (i = 0; i < obj.primitives.length; ++i) {
  175. var primitive = obj.primitives[i];
  176. if (primitive.mode == 4 || primitive.mode == 5) {
  177. var strip = (primitive.mode == 5);
  178. for (j = 0; j + 2 < primitive.indices.length; !strip ? j += 3 : j++) {
  179. str += 'f ';
  180. var order = [ 0, 1, 2];
  181. if (strip && (j % 2 == 1)) {
  182. order = [ 0, 2, 1];
  183. }
  184. for (var k = 0; k < 3; ++k)
  185. {
  186. var faceNum = primitive.indices[j + order[k]] + 1;
  187. str += faceNum;
  188. if (vn || vt) {
  189. str += '/';
  190. if (vt) {
  191. str += faceNum;
  192. }
  193. if (vn) {
  194. str += '/' + faceNum;
  195. }
  196. }
  197. str += ' ';
  198. }
  199. str += '\n';
  200. }
  201. }
  202. else {
  203. console.log("[UserScript]dosavefile: unknown primitive mode", primitive);
  204. }
  205. }
  206.  
  207. str += '\n';
  208.  
  209. var objblob = new Blob([str], {type:'text/plain'});
  210.  
  211. objects[mdl.name+".obj"] = objblob;
  212. }
  213.  
  214. window.attachbody = function(obj)
  215. {
  216. if(obj._faked != true && ((obj.stateset && obj.stateset._name) || obj._name || (obj._parents && obj._parents[0]._name)) ) {
  217. obj._faked = true;
  218. if(obj._name == "composer layer" || obj._name == "Ground - Geometry") return;
  219. window.allmodel.push(obj)
  220. console.log(obj);
  221. }
  222.  
  223. }
  224.  
  225. window.hook_test = function(e, idx)
  226. {
  227. console.log("hooked index: "+idx);
  228. console.log(e);
  229. }
  230. window.drawhookcanvas = function(e, imagemodel)
  231. {
  232.  
  233. if((e.width == 128 && e.height == 128) || (e.width == 32 && e.height == 32) || (e.width == 64 && e.height == 64))
  234. {
  235. return e;
  236. }
  237. if(imagemodel)
  238. {
  239. var alpha = e.options.format;
  240. var filename_image = imagemodel.attributes.name;
  241. var uid = imagemodel.attributes.uid;
  242. var url_image = e.url;
  243. var max_size = 0;
  244. var obr = e;
  245. imagemodel.attributes.images.forEach(function(img)
  246. {
  247. var alpha_is_check = alpha == "A" ? img.options.format == alpha : true;
  248.  
  249. var d = img.width;
  250. while ( d % 2 == 0 )
  251. {
  252. d = d / 2;
  253. }
  254.  
  255. if(img.size > max_size && alpha_is_check && d == 1)
  256. {
  257. max_size = img.size;
  258. url_image = img.url;
  259. uid = img.uid;
  260. obr = img;
  261. }
  262. });
  263. if(!saveimagecache2[url_image])
  264. {
  265. console.log(e);
  266. saveimage_to_list(url_image, filename_image);
  267. }
  268. else
  269. {
  270.  
  271. }
  272.  
  273. return obr;
  274. }
  275. return e;
  276. }
  277.  
  278. window.drawhookimg = function(gl,t)
  279. {
  280. var url = t[5].currentSrc;
  281. var width = t[5].width;
  282. var height = t[5].height;
  283.  
  284. if(!saveimagecache2[url])
  285. {
  286.  
  287. return;
  288. }
  289. else
  290. {
  291.  
  292. }
  293.  
  294. var data = new Uint8Array(width * height * 4);
  295. gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data);
  296.  
  297. var halfHeight = height / 2 | 0;
  298. var bytesPerRow = width * 4;
  299.  
  300. var temp = new Uint8Array(width * 4);
  301. for (var y = 0; y < halfHeight; ++y)
  302. {
  303. var topOffset = y * bytesPerRow;
  304. var bottomOffset = (height - y - 1) * bytesPerRow;
  305.  
  306. temp.set(data.subarray(topOffset, topOffset + bytesPerRow));
  307.  
  308. data.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
  309.  
  310. data.set(temp, bottomOffset);
  311. }
  312.  
  313. var canvas = document.createElement('canvas');
  314. canvas.width = width;
  315. canvas.height = height;
  316. var context = canvas.getContext('2d');
  317.  
  318. var imageData = context.createImageData(width, height);
  319. imageData.data.set(data);
  320. context.putImageData(imageData, 0, 0);
  321.  
  322. var re = /(?:\.([^.]+))?$/;
  323. var ext = re.exec(saveimagecache2[url].name)[1];
  324. var name = saveimagecache2[url].name+".png";
  325.  
  326. if(ext == "png" || ext == "jpg" || ext == "jpeg")
  327. {
  328. var ret = saveimagecache2[url].name.replace('.'+ext,'');
  329. name = ret+".png";
  330. }
  331. console.log("saved texture to blob "+name);
  332. canvas.toBlob(function(blob){objects[name] = blob;},"image/png");
  333. }
  334.  
  335. })();
  336.  
  337. (() => {
  338. "use strict";
  339. const Event = class {
  340. constructor(script, target) {
  341. this.script = script;
  342. this.target = target;
  343.  
  344. this._cancel = false;
  345. this._replace = null;
  346. this._stop = false;
  347. }
  348.  
  349. preventDefault() {
  350. this._cancel = true;
  351. }
  352. stopPropagation() {
  353. this._stop = true;
  354. }
  355. replacePayload(payload) {
  356. this._replace = payload;
  357. }
  358. };
  359.  
  360. let callbacks = [];
  361. window.addBeforeScriptExecuteListener = (f) => {
  362. if (typeof f !== "function") {
  363. throw new Error("Event handler must be a function.");
  364. }
  365. callbacks.push(f);
  366. };
  367. window.removeBeforeScriptExecuteListener = (f) => {
  368. let i = callbacks.length;
  369. while (i--) {
  370. if (callbacks[i] === f) {
  371. callbacks.splice(i, 1);
  372. }
  373. }
  374. };
  375.  
  376. const dispatch = (script, target) => {
  377. if (script.tagName !== "SCRIPT") {
  378. return;
  379. }
  380.  
  381. const e = new Event(script, target);
  382.  
  383. if (typeof window.onbeforescriptexecute === "function") {
  384. try {
  385. window.onbeforescriptexecute(e);
  386. } catch (err) {
  387. console.error(err);
  388. }
  389. }
  390.  
  391. for (const func of callbacks) {
  392. if (e._stop) {
  393. break;
  394. }
  395. try {
  396. func(e);
  397. } catch (err) {
  398. console.error(err);
  399. }
  400. }
  401.  
  402. if (e._cancel) {
  403. script.textContent = "";
  404. script.remove();
  405. } else if (typeof e._replace === "string") {
  406. script.textContent = e._replace;
  407. }
  408. };
  409. const observer = new MutationObserver((mutations) => {
  410. for (const m of mutations) {
  411. for (const n of m.addedNodes) {
  412. dispatch(n, m.target);
  413. }
  414. }
  415. });
  416. observer.observe(document, {
  417. childList: true,
  418. subtree: true,
  419. });
  420. })();
  421.  
  422. (() => {
  423. "use strict";
  424.  
  425. window.onbeforescriptexecute = (e) => {
  426. var links_as_arr = Array.from(e.target.childNodes);
  427.  
  428. links_as_arr.forEach(function(srimgc)
  429. {
  430. if(srimgc instanceof HTMLScriptElement)
  431. {
  432. if (srimgc.src.indexOf("web/dist/") >= 0 || srimgc.src.indexOf("standaloneViewer") >= 0)
  433. {
  434. e.preventDefault();
  435. e.stopPropagation();
  436. var req = new XMLHttpRequest();
  437. req.open('GET', srimgc.src, false);
  438. req.send('');
  439. var jstext = req.responseText;
  440. var ret = func_renderInto1.exec(jstext);
  441.  
  442. if (ret)
  443. {
  444. var index = ret.index + ret[0].length;
  445. var head = jstext.slice(0, index);
  446. var tail = jstext.slice(index);
  447. jstext = head + ",i" + tail;
  448. console.log("[UserScript] Injection: patch_0 injected successful" + srimgc.src);
  449. }
  450.  
  451. ret = func_renderInto2.exec(jstext);
  452.  
  453. if (ret)
  454. {
  455. var index = ret.index + ret[0].length;
  456. var head = jstext.slice(0, index);
  457. var tail = jstext.slice(index);
  458. jstext = head + ",image_data" + tail;
  459. console.log("[UserScript] Injection: patch_1 injected successful" + srimgc.src);
  460. }
  461.  
  462. ret = fund_drawArrays.exec(jstext);
  463.  
  464. if (ret)
  465. {
  466. var index = ret.index + ret[0].length;
  467. var head = jstext.slice(0, index);
  468. var tail = jstext.slice(index);
  469. jstext = head + ",window.drawhookimg(t,image_data)" + tail;
  470. console.log("[UserScript] Injection: patch_2 injected successful" + srimgc.src);
  471. }
  472.  
  473. ret = func_getResourceImage.exec(jstext);
  474.  
  475. if (ret)
  476. {
  477. var index = ret.index + ret[0].length;
  478. var head = jstext.slice(0, index);
  479. var tail = jstext.slice(index);
  480. jstext = head + "e = window.drawhookcanvas(e,this._imageModel);" + tail;
  481. console.log("[UserScript] Injection: patch_3 injected successful " + srimgc.src);
  482. }
  483.  
  484. ret = func_drawGeometry.exec(jstext);
  485.  
  486. if (ret)
  487. {
  488. var index1 = ret.index + ret[1].length;
  489. var head1 = jstext.slice(0, index1);
  490. var tail1 = jstext.slice(index1);
  491. jstext = head1 + ";window.attachbody(t);" + tail1;
  492. console.log("[UserScript] Injection: patch_4 injected successful" + srimgc.src);
  493. setTimeout(addbtnfunc, 3000);
  494. }
  495.  
  496. var idx = 0;
  497.  
  498. var obj = document.createElement('script');
  499. obj.type = "text/javascript";
  500. obj.text = jstext;
  501. document.getElementsByTagName('head')[0].appendChild(obj);
  502. }
  503. }
  504. });
  505. };
  506. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址