index.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. (function () {
  2. var jsEditor;
  3. var run = function () {
  4. var blockEditorChange = false;
  5. jsEditor.onKeyDown(function (evt) {
  6. });
  7. jsEditor.onKeyUp(function (evt) {
  8. if (blockEditorChange) {
  9. return;
  10. }
  11. document.getElementById("currentScript").innerHTML = "Custom";
  12. document.getElementById('safemodeToggle').checked = true;
  13. });
  14. var snippetUrl = "https://babylonjs-api.azurewebsites.net/api/snippet";
  15. var currentSnippetToken;
  16. var engine;
  17. var fpsLabel = document.getElementById("fpsLabel");
  18. var scripts;
  19. var zipCode;
  20. BABYLON.Engine.ShadersRepository = "/src/Shaders/";
  21. switch (BABYLON.Engine.Version) {
  22. case "2.5":
  23. document.getElementById("currentVersion").innerHTML = "Version: " + BABYLON.Engine.Version;
  24. break;
  25. default:
  26. document.getElementById("currentVersion").innerHTML = "Version: Latest";
  27. break;
  28. }
  29. var loadScript = function (scriptURL, title) {
  30. var xhr = new XMLHttpRequest();
  31. xhr.open('GET', scriptURL, true);
  32. xhr.onreadystatechange = function () {
  33. if (xhr.readyState === 4) {
  34. if (xhr.status === 200) {
  35. blockEditorChange = true;
  36. jsEditor.setValue(xhr.responseText);
  37. jsEditor.setPosition({ lineNumber: 0, column: 0 });
  38. blockEditorChange = false;
  39. compileAndRun();
  40. document.getElementById("currentScript").innerHTML = title;
  41. currentSnippetToken = null;
  42. }
  43. }
  44. };
  45. xhr.send(null);
  46. };
  47. var loadScriptFromIndex = function (index) {
  48. if (index === 0) {
  49. index = 1;
  50. }
  51. var script = scripts[index - 1].trim();
  52. loadScript("scripts/" + script + ".js", script);
  53. }
  54. var onScriptClick = function (evt) {
  55. loadScriptFromIndex(evt.target.scriptLinkIndex);
  56. }
  57. var loadScriptsList = function () {
  58. var xhr = new XMLHttpRequest();
  59. xhr.open('GET', 'scripts/scripts.txt', true);
  60. xhr.onreadystatechange = function () {
  61. if (xhr.readyState === 4) {
  62. if (xhr.status === 200) {
  63. scripts = xhr.responseText.split("\n");
  64. var ul = document.getElementById("scriptsList");
  65. var index;
  66. for (index = 0; index < scripts.length; index++) {
  67. var li = document.createElement("li");
  68. var a = document.createElement("a");
  69. li.class = "scriptsListEntry";
  70. a.href = "#";
  71. a.innerHTML = (index + 1) + " - " + scripts[index];
  72. a.scriptLinkIndex = index + 1;
  73. a.onclick = onScriptClick;
  74. li.appendChild(a);
  75. ul.appendChild(li);
  76. }
  77. if (!location.hash) {
  78. // Query string
  79. var queryString = window.location.search;
  80. if (queryString) {
  81. var query = queryString.replace("?", "");
  82. index = parseInt(query);
  83. if (!isNaN(index)) {
  84. loadScriptFromIndex(index);
  85. } else {
  86. loadScript("scripts/" + query + ".js", query);
  87. }
  88. } else {
  89. loadScript("scripts/basic scene.js", "Basic scene");
  90. }
  91. }
  92. }
  93. }
  94. };
  95. xhr.send(null);
  96. }
  97. var createNewScript = function () {
  98. location.hash = "";
  99. currentSnippetToken = null;
  100. jsEditor.setValue('// You have to create a function called createScene. This function must return a BABYLON.Scene object\r\n// You can reference the following variables: scene, canvas\r\n// You must at least define a camera\r\n// More info here: https://doc.babylonjs.com/generals/The_Playground_Tutorial\r\n\r\nvar createScene = function() {\r\n\tvar scene = new BABYLON.Scene(engine);\r\n\tvar camera = new BABYLON.ArcRotateCamera("Camera", 0, Math.PI / 2, 12, BABYLON.Vector3.Zero(), scene);\r\n\tcamera.attachControl(canvas, true);\r\n\r\n\r\n\r\n\treturn scene;\r\n};');
  101. jsEditor.setPosition({ lineNumber: 11, column: 0 });
  102. jsEditor.focus();
  103. compileAndRun();
  104. }
  105. var clear = function () {
  106. location.hash = "";
  107. currentSnippetToken = null;
  108. jsEditor.setValue('');
  109. jsEditor.setPosition({ lineNumber: 0, column: 0 });
  110. jsEditor.focus();
  111. }
  112. var showError = function (errorMessage, errorEvent) {
  113. var errorContent =
  114. '<div class="alert alert-error"><button type="button" class="close" data-dismiss="alert">&times;</button><h4>Compilation error</h4>'
  115. if (errorEvent) {
  116. var regEx = /\(.+:(\d+):(\d+)\)\n/g;
  117. var match = regEx.exec(errorEvent.stack);
  118. if (match) {
  119. errorContent += "Line ";
  120. var lineNumber = match[1];
  121. var columnNumber = match[2];
  122. errorContent += lineNumber + ':' + columnNumber + ' - ';
  123. }
  124. }
  125. errorContent += errorMessage + '</div>';
  126. document.getElementById("errorZone").innerHTML = errorContent;
  127. }
  128. compileAndRun = function () {
  129. try {
  130. if (!BABYLON.Engine.isSupported()) {
  131. showError("Your browser does not support WebGL", null);
  132. return;
  133. }
  134. if (engine) {
  135. engine.dispose();
  136. engine = null;
  137. }
  138. var canvas = document.getElementById("renderCanvas");
  139. engine = new BABYLON.Engine(canvas, true, {preserveDrawingBuffer: true, stencil: true});
  140. document.getElementById("errorZone").innerHTML = "";
  141. document.getElementById("statusBar").innerHTML = "Loading assets...Please wait";
  142. engine.runRenderLoop(function () {
  143. if (engine.scenes.length === 0) {
  144. return;
  145. }
  146. if (canvas.width !== canvas.clientWidth) {
  147. engine.resize();
  148. }
  149. var scene = engine.scenes[0];
  150. if (scene.activeCamera || scene.activeCameras.length > 0) {
  151. scene.render();
  152. }
  153. fpsLabel.innerHTML = engine.getFps().toFixed() + " fps";
  154. });
  155. var code = jsEditor.getValue();
  156. var scene;
  157. if (code.indexOf("createScene") !== -1) { // createScene
  158. eval(code);
  159. scene = createScene();
  160. if (!scene) {
  161. showError("createScene function must return a scene.", null);
  162. return;
  163. }
  164. zipCode = code + "\r\n\r\nvar scene = createScene();";
  165. } else if (code.indexOf("CreateScene") !== -1) { // CreateScene
  166. eval(code);
  167. scene = CreateScene();
  168. if (!scene) {
  169. showError("CreateScene function must return a scene.", null);
  170. return;
  171. }
  172. zipCode = code + "\r\n\r\nvar scene = CreateScene();";
  173. } else if (code.indexOf("createscene") !== -1) { // createscene
  174. eval(code);
  175. scene = createscene();
  176. if (!scene) {
  177. showError("createscene function must return a scene.", null);
  178. return;
  179. }
  180. zipCode = code + "\r\n\r\nvar scene = createscene();";
  181. } else { // Direct code
  182. scene = new BABYLON.Scene(engine);
  183. eval("runScript = function(scene, canvas) {" + code + "}");
  184. runScript(scene, canvas);
  185. zipCode = "var scene = new BABYLON.Scene(engine);\r\n\r\n" + code;
  186. }
  187. if (engine.scenes.length === 0) {
  188. showError("You must at least create a scene.", null);
  189. return;
  190. }
  191. if (engine.scenes[0].activeCamera == null) {
  192. showError("You must at least create a camera.", null);
  193. return;
  194. }
  195. engine.scenes[0].executeWhenReady(function () {
  196. document.getElementById("statusBar").innerHTML = "";
  197. });
  198. } catch (e) {
  199. showError(e.message, e);
  200. }
  201. };
  202. window.addEventListener("resize",
  203. function () {
  204. if (engine) {
  205. engine.resize();
  206. }
  207. });
  208. // Load scripts list
  209. loadScriptsList();
  210. // Zip
  211. var addContentToZip = function (zip, name, url, replace, buffer, then) {
  212. var xhr = new XMLHttpRequest();
  213. xhr.open('GET', url, true);
  214. if (buffer) {
  215. xhr.responseType = "arraybuffer";
  216. }
  217. xhr.onreadystatechange = function () {
  218. if (xhr.readyState === 4) {
  219. if (xhr.status === 200) {
  220. var text;
  221. if (!buffer) {
  222. if (replace) {
  223. var splits = replace.split("\r\n");
  224. for (var index = 0; index < splits.length; index++) {
  225. splits[index] = " " + splits[index];
  226. }
  227. replace = splits.join("\r\n");
  228. text = xhr.responseText.replace("####INJECT####", replace);
  229. } else {
  230. text = xhr.responseText;
  231. }
  232. }
  233. zip.file(name, buffer ? xhr.response : text);
  234. then();
  235. }
  236. }
  237. };
  238. xhr.send(null);
  239. }
  240. var addTexturesToZip = function (zip, index, textures, folder, then) {
  241. if (index === textures.length) {
  242. then();
  243. return;
  244. }
  245. if (textures[index].isRenderTarget || textures[index] instanceof BABYLON.DynamicTexture) {
  246. addTexturesToZip(zip, index + 1, textures, folder, then);
  247. return;
  248. }
  249. if (textures[index].isCube) {
  250. if (textures[index]._extensions) {
  251. for (var i = 0; i < 6; i++) {
  252. textures.push({ name: textures[index].name + textures[index]._extensions[i] });
  253. }
  254. }
  255. else {
  256. textures.push({ name: textures[index].name });
  257. }
  258. addTexturesToZip(zip, index + 1, textures, folder, then);
  259. return;
  260. }
  261. if (folder == null) {
  262. folder = zip.folder("textures");
  263. }
  264. var url;
  265. if (textures[index].video) {
  266. url = textures[index].video.currentSrc;
  267. } else {
  268. url = textures[index].name;
  269. }
  270. var name = url.substr(url.lastIndexOf("/") + 1);
  271. addContentToZip(folder,
  272. name,
  273. url,
  274. null,
  275. true,
  276. function () {
  277. addTexturesToZip(zip, index + 1, textures, folder, then);
  278. });
  279. }
  280. var addImportedFilesToZip = function (zip, index, importedFiles, folder, then) {
  281. if (index === importedFiles.length) {
  282. then();
  283. return;
  284. }
  285. if (!folder) {
  286. folder = zip.folder("scenes");
  287. }
  288. var url = importedFiles[index];
  289. var name = url.substr(url.lastIndexOf("/") + 1);
  290. addContentToZip(folder,
  291. name,
  292. url,
  293. null,
  294. true,
  295. function () {
  296. addImportedFilesToZip(zip, index + 1, importedFiles, folder, then);
  297. });
  298. }
  299. var getZip = function () {
  300. if (engine.scenes.length === 0) {
  301. return;
  302. }
  303. var zip = new JSZip();
  304. var scene = engine.scenes[0];
  305. var textures = scene.textures;
  306. var importedFiles = scene.importedMeshesFiles;
  307. document.getElementById("statusBar").innerHTML = "Creating archive...Please wait";
  308. if (zipCode.indexOf("textures/worldHeightMap.jpg") !== -1) {
  309. textures.push({ name: "textures/worldHeightMap.jpg" });
  310. }
  311. addContentToZip(zip,
  312. "index.html",
  313. "zipContent/index.html",
  314. zipCode,
  315. false,
  316. function () {
  317. addTexturesToZip(zip,
  318. 0,
  319. textures,
  320. null,
  321. function () {
  322. addImportedFilesToZip(zip,
  323. 0,
  324. importedFiles,
  325. null,
  326. function () {
  327. var blob = zip.generate({ type: "blob" });
  328. saveAs(blob, "sample.zip");
  329. document.getElementById("statusBar").innerHTML = "";
  330. });
  331. });
  332. });
  333. }
  334. // Versions
  335. setVersion = function (version) {
  336. switch (version) {
  337. case "2.5":
  338. location.href = "index2_5.html";
  339. break;
  340. default:
  341. location.href = "index.html";
  342. break;
  343. }
  344. }
  345. // Fonts
  346. setFontSize = function (size) {
  347. document.querySelector(".monaco-editor").style.fontSize = size + "px";
  348. document.getElementById("currentFontSize").innerHTML = "Font: " + size;
  349. };
  350. // Fullscreen
  351. var goFullscreen = function () {
  352. if (engine) {
  353. engine.switchFullscreen(true);
  354. }
  355. }
  356. var toggleEditor = function () {
  357. var editorButton = document.getElementById("editorButton");
  358. var scene = engine.scenes[0];
  359. if (editorButton.innerHTML === "-Editor") {
  360. editorButton.innerHTML = "+Editor";
  361. document.getElementById("jsEditor").style.display = "none";
  362. document.getElementById("canvasZone").style.flexBasis = "100%";
  363. } else {
  364. editorButton.innerHTML = "-Editor";
  365. document.getElementById("jsEditor").style.display = "block";
  366. document.getElementById("canvasZone").style.flexBasis = undefined;
  367. }
  368. engine.resize();
  369. if (scene.debugLayer.isVisible()) {
  370. scene.debugLayer.hide();
  371. scene.debugLayer.show();
  372. }
  373. }
  374. var toggleDebug = function () {
  375. var debugButton = document.getElementById("debugButton");
  376. var scene = engine.scenes[0];
  377. if (debugButton.innerHTML === "+Debug layer") {
  378. debugButton.innerHTML = "-Debug layer";
  379. scene.debugLayer.show();
  380. } else {
  381. debugButton.innerHTML = "+Debug layer";
  382. scene.debugLayer.hide();
  383. }
  384. }
  385. // UI
  386. document.getElementById("runButton").addEventListener("click", compileAndRun);
  387. document.getElementById("zipButton").addEventListener("click", getZip);
  388. document.getElementById("fullscreenButton").addEventListener("click", goFullscreen);
  389. document.getElementById("newButton").addEventListener("click", createNewScript);
  390. document.getElementById("clearButton").addEventListener("click", clear);
  391. document.getElementById("editorButton").addEventListener("click", toggleEditor);
  392. document.getElementById("debugButton").addEventListener("click", toggleDebug);
  393. //Navigation Overwrites
  394. var exitPrompt = function (e) {
  395. var safeToggle = document.getElementById("safemodeToggle");
  396. if (safeToggle.checked) {
  397. e = e || window.event;
  398. var message =
  399. 'This page is asking you to confirm that you want to leave - data you have entered may not be saved.';
  400. if (e) {
  401. e.returnValue = message;
  402. }
  403. return message;
  404. }
  405. };
  406. window.onbeforeunload = exitPrompt;
  407. // Snippet
  408. var save = function () {
  409. var xmlHttp = new XMLHttpRequest();
  410. xmlHttp.onreadystatechange = function () {
  411. if (xmlHttp.readyState === 4) {
  412. if (xmlHttp.status === 201) {
  413. var baseUrl = location.href.replace(location.hash, "").replace(location.search, "");
  414. var snippet = JSON.parse(xmlHttp.responseText);
  415. var newUrl = baseUrl + "#" + snippet.id;
  416. currentSnippetToken = snippet.id;
  417. if (snippet.version !== "0") {
  418. newUrl += "#" + snippet.version;
  419. }
  420. location.href = newUrl;
  421. compileAndRun();
  422. } else {
  423. showError("Unable to save your code. It may be too long.", null);
  424. }
  425. }
  426. }
  427. xmlHttp.open("POST", snippetUrl + (currentSnippetToken ? "/" + currentSnippetToken : ""), true);
  428. xmlHttp.setRequestHeader("Content-Type", "application/json");
  429. var payload = {
  430. code: jsEditor.getValue()
  431. };
  432. xmlHttp.send(JSON.stringify(payload));
  433. }
  434. document.getElementById("saveButton").addEventListener("click", save);
  435. document.getElementById("mainTitle").innerHTML = "Babylon.js v" + BABYLON.Engine.Version + " Playground";
  436. var previousHash = "";
  437. var cleanHash = function () {
  438. var splits = decodeURIComponent(location.hash.substr(1)).split("#");
  439. if (splits.length > 2) {
  440. splits.splice(2, splits.length - 2);
  441. }
  442. location.hash = splits.join("#");
  443. }
  444. var checkHash = function (firstTime) {
  445. if (location.hash) {
  446. if (previousHash !== location.hash) {
  447. cleanHash();
  448. previousHash = location.hash;
  449. try {
  450. var xmlHttp = new XMLHttpRequest();
  451. xmlHttp.onreadystatechange = function () {
  452. if (xmlHttp.readyState === 4) {
  453. if (xmlHttp.status === 200) {
  454. var snippet = JSON.parse(xmlHttp.responseText);
  455. blockEditorChange = true;
  456. jsEditor.setValue(snippet.code.toString());
  457. jsEditor.setPosition({ lineNumber: 0, column: 0 });
  458. blockEditorChange = false;
  459. compileAndRun();
  460. document.getElementById("currentScript").innerHTML = "Custom";
  461. } else if (firstTime) {
  462. location.href = location.href.replace(location.hash, "");
  463. if (scripts) {
  464. loadScriptFromIndex(0);
  465. }
  466. }
  467. }
  468. }
  469. var hash = location.hash.substr(1);
  470. currentSnippetToken = hash.split("#")[0];
  471. xmlHttp.open("GET", snippetUrl + "/" + hash.replace("#", "/"));
  472. xmlHttp.send();
  473. } catch (e) {
  474. }
  475. }
  476. }
  477. setTimeout(checkHash, 200);
  478. }
  479. checkHash(true);
  480. }
  481. // Monaco
  482. var xhr = new XMLHttpRequest();
  483. xhr.open('GET', "babylon.d.txt", true);
  484. xhr.onreadystatechange = function () {
  485. if (xhr.readyState === 4) {
  486. if (xhr.status === 200) {
  487. require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs' } });
  488. require(['vs/editor/editor.main'], function () {
  489. monaco.languages.typescript.javascriptDefaults.addExtraLib(xhr.responseText, 'babylon.d.ts');
  490. jsEditor = monaco.editor.create(document.getElementById('jsEditor'), {
  491. value: "",
  492. language: "javascript",
  493. lineNumbers: true,
  494. tabSize: "auto",
  495. insertSpaces: "auto",
  496. roundedSelection: true,
  497. scrollBeyondLastLine: false,
  498. automaticLayout: true,
  499. readOnly: false,
  500. theme: "vs",
  501. contextmenu: false
  502. });
  503. run();
  504. });
  505. }
  506. }
  507. };
  508. xhr.send(null);
  509. })();