babylon.tools.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. module BABYLON {
  2. declare var FilesTextures; //ANY
  3. export interface IAnimatable {
  4. animations: Array<Animation>;
  5. }
  6. export interface ISize {
  7. width: number;
  8. height: number;
  9. }
  10. // FPS
  11. var fpsRange = 60;
  12. var previousFramesDuration = [];
  13. var fps = 60;
  14. var deltaTime = 0;
  15. var cloneValue = (source, destinationObject) => {
  16. if (!source)
  17. return null;
  18. if (source instanceof Mesh) {
  19. return null;
  20. }
  21. if (source instanceof SubMesh) {
  22. return source.clone(destinationObject);
  23. } else if (source.clone) {
  24. return source.clone();
  25. }
  26. return null;
  27. };
  28. export class Tools {
  29. public static BaseUrl = "";
  30. private static _ScreenshotCanvas: HTMLCanvasElement;
  31. public static GetFilename(path: string): string {
  32. var index = path.lastIndexOf("/");
  33. if (index < 0)
  34. return path;
  35. return path.substring(index + 1);
  36. }
  37. public static GetDOMTextContent(element: HTMLElement): string {
  38. var result = "";
  39. var child = element.firstChild;
  40. while (child) {
  41. if (child.nodeType == 3) {
  42. result += child.textContent;
  43. }
  44. child = child.nextSibling;
  45. }
  46. return result;
  47. }
  48. public static ToDegrees(angle: number): number {
  49. return angle * 180 / Math.PI;
  50. }
  51. public static ToRadians(angle: number): number {
  52. return angle * Math.PI / 180;
  53. }
  54. public static ExtractMinAndMax(positions: number[], start: number, count: number): { minimum: Vector3; maximum: Vector3 } {
  55. var minimum = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  56. var maximum = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  57. for (var index = start; index < start + count; index++) {
  58. var current = new Vector3(positions[index * 3], positions[index * 3 + 1], positions[index * 3 + 2]);
  59. minimum = BABYLON.Vector3.Minimize(current, minimum);
  60. maximum = BABYLON.Vector3.Maximize(current, maximum);
  61. }
  62. return {
  63. minimum: minimum,
  64. maximum: maximum
  65. };
  66. }
  67. public static MakeArray(obj, allowsNullUndefined?: boolean): Array<any> {
  68. if (allowsNullUndefined !== true && (obj === undefined || obj == null))
  69. return undefined;
  70. return Array.isArray(obj) ? obj : [obj];
  71. }
  72. // Misc.
  73. public static GetPointerPrefix(): string {
  74. var eventPrefix = "pointer";
  75. // Check if hand.js is referenced or if the browser natively supports pointer events
  76. if (!navigator.pointerEnabled) {
  77. eventPrefix = "mouse";
  78. }
  79. return eventPrefix;
  80. }
  81. public static QueueNewFrame(func): void {
  82. if (window.requestAnimationFrame)
  83. window.requestAnimationFrame(func);
  84. else if (window.msRequestAnimationFrame)
  85. window.msRequestAnimationFrame(func);
  86. else if (window.webkitRequestAnimationFrame)
  87. window.webkitRequestAnimationFrame(func);
  88. else if (window.mozRequestAnimationFrame)
  89. window.mozRequestAnimationFrame(func);
  90. else if (window.oRequestAnimationFrame)
  91. window.oRequestAnimationFrame(func);
  92. else {
  93. window.setTimeout(func, 16);
  94. }
  95. }
  96. public static RequestFullscreen(element): void {
  97. if (element.requestFullscreen)
  98. element.requestFullscreen();
  99. else if (element.msRequestFullscreen)
  100. element.msRequestFullscreen();
  101. else if (element.webkitRequestFullscreen)
  102. element.webkitRequestFullscreen();
  103. else if (element.mozRequestFullScreen)
  104. element.mozRequestFullScreen();
  105. }
  106. public static ExitFullscreen(): void {
  107. if (document.exitFullscreen) {
  108. document.exitFullscreen();
  109. }
  110. else if (document.mozCancelFullScreen) {
  111. document.mozCancelFullScreen();
  112. }
  113. else if (document.webkitCancelFullScreen) {
  114. document.webkitCancelFullScreen();
  115. }
  116. else if (document.msCancelFullScreen) {
  117. document.msCancelFullScreen();
  118. }
  119. }
  120. // External files
  121. public static LoadImage(url: string, onload, onerror, database): HTMLImageElement {
  122. var img = new Image();
  123. img.crossOrigin = 'anonymous';
  124. img.onload = () => {
  125. onload(img);
  126. };
  127. img.onerror = err => {
  128. onerror(img, err);
  129. };
  130. var noIndexedDB = () => {
  131. img.src = url;
  132. };
  133. var loadFromIndexedDB = () => {
  134. database.loadImageFromDB(url, img);
  135. };
  136. //ANY database to do!
  137. if (database && database.enableTexturesOffline) { //ANY } && BABYLON.Database.isUASupportingBlobStorage) {
  138. database.openAsync(loadFromIndexedDB, noIndexedDB);
  139. }
  140. else {
  141. if (url.indexOf("file:") === -1) {
  142. noIndexedDB();
  143. }
  144. else {
  145. try {
  146. var textureName = url.substring(5);
  147. var blobURL;
  148. try {
  149. blobURL = URL.createObjectURL(FilesTextures[textureName], { oneTimeOnly: true });
  150. }
  151. catch (ex) {
  152. // Chrome doesn't support oneTimeOnly parameter
  153. blobURL = URL.createObjectURL(FilesTextures[textureName]);
  154. }
  155. img.src = blobURL;
  156. }
  157. catch (e) {
  158. console.log("Error while trying to load texture: " + textureName);
  159. img.src = null;
  160. }
  161. }
  162. }
  163. return img;
  164. }
  165. //ANY
  166. public static LoadFile(url: string, callback: (data: any) => void, progressCallBack?: () => void, database?, useArrayBuffer?: boolean): void {
  167. var noIndexedDB = () => {
  168. var request = new XMLHttpRequest();
  169. var loadUrl = Tools.BaseUrl + url;
  170. request.open('GET', loadUrl, true);
  171. if (useArrayBuffer) {
  172. request.responseType = "arraybuffer";
  173. }
  174. request.onprogress = progressCallBack;
  175. request.onreadystatechange = () => {
  176. if (request.readyState == 4) {
  177. if (request.status == 200) {
  178. callback(!useArrayBuffer ? request.responseText : request.response);
  179. } else { // Failed
  180. throw new Error("Error status: " + request.status + " - Unable to load " + loadUrl);
  181. }
  182. }
  183. };
  184. request.send(null);
  185. };
  186. var loadFromIndexedDB = () => {
  187. database.loadSceneFromDB(url, callback, progressCallBack, noIndexedDB);
  188. };
  189. // Caching only scenes files
  190. if (database && url.indexOf(".babylon") !== -1 && (database.enableSceneOffline)) {
  191. database.openAsync(loadFromIndexedDB, noIndexedDB);
  192. }
  193. else {
  194. noIndexedDB();
  195. }
  196. }
  197. public static ReadFile(fileToLoad, callback, progressCallBack): void {
  198. var reader = new FileReader();
  199. reader.onload = e => {
  200. callback(e.target.result);
  201. };
  202. reader.onprogress = progressCallBack;
  203. // Asynchronous read
  204. reader.readAsText(fileToLoad);
  205. }
  206. // Misc.
  207. public static WithinEpsilon(a: number, b: number): boolean {
  208. var num = a - b;
  209. return -1.401298E-45 <= num && num <= 1.401298E-45;
  210. }
  211. public static DeepCopy(source, destination, doNotCopyList?: string[], mustCopyList?: string[]): void {
  212. for (var prop in source) {
  213. if (prop[0] === "_" && (!mustCopyList || mustCopyList.indexOf(prop) === -1)) {
  214. continue;
  215. }
  216. if (doNotCopyList && doNotCopyList.indexOf(prop) !== -1) {
  217. continue;
  218. }
  219. var sourceValue = source[prop];
  220. var typeOfSourceValue = typeof sourceValue;
  221. if (typeOfSourceValue == "function") {
  222. continue;
  223. }
  224. if (typeOfSourceValue == "object") {
  225. if (sourceValue instanceof Array) {
  226. destination[prop] = [];
  227. if (sourceValue.length > 0) {
  228. if (typeof sourceValue[0] == "object") {
  229. for (var index = 0; index < sourceValue.length; index++) {
  230. var clonedValue = cloneValue(sourceValue[index], destination);
  231. if (destination[prop].indexOf(clonedValue) === -1) { // Test if auto inject was not done
  232. destination[prop].push(clonedValue);
  233. }
  234. }
  235. } else {
  236. destination[prop] = sourceValue.slice(0);
  237. }
  238. }
  239. } else {
  240. destination[prop] = cloneValue(sourceValue, destination);
  241. }
  242. } else {
  243. destination[prop] = sourceValue;
  244. }
  245. }
  246. }
  247. public static IsEmpty(obj): boolean {
  248. for (var i in obj) {
  249. return false;
  250. }
  251. return true;
  252. }
  253. public static GetFps(): number {
  254. return fps;
  255. }
  256. public static GetDeltaTime(): number {
  257. return deltaTime;
  258. }
  259. public static _MeasureFps(): void {
  260. previousFramesDuration.push((new Date).getTime());
  261. var length = previousFramesDuration.length;
  262. if (length >= 2) {
  263. deltaTime = previousFramesDuration[length - 1] - previousFramesDuration[length - 2];
  264. }
  265. if (length >= fpsRange) {
  266. if (length > fpsRange) {
  267. previousFramesDuration.splice(0, 1);
  268. length = previousFramesDuration.length;
  269. }
  270. var sum = 0;
  271. for (var id = 0; id < length - 1; id++) {
  272. sum += previousFramesDuration[id + 1] - previousFramesDuration[id];
  273. }
  274. fps = 1000.0 / (sum / (length - 1));
  275. }
  276. }
  277. public static CreateScreenshot(engine: Engine, camera: Camera, size: any): void {
  278. var width: number;
  279. var height: number;
  280. //If a zoom value is specified
  281. if (size.zoom) {
  282. width = Math.round(engine.getRenderWidth() * size.zoom);
  283. height = Math.round(width / engine.getAspectRatio(camera));
  284. size = { width: width, height: height };
  285. }
  286. else if (size.width && size.height) {
  287. width = size.width;
  288. height = size.height;
  289. }
  290. //If passing only width, computing height to keep display canvas ratio.
  291. else if (size.width && !size.height) {
  292. width = size.width;
  293. height = Math.round(width / engine.getAspectRatio(camera));
  294. size = { width: width, height: height };
  295. }
  296. //If passing only height, computing width to keep display canvas ratio.
  297. else if (size.height && !size.width) {
  298. height = size.height;
  299. width = Math.round(height * engine.getAspectRatio(camera));
  300. size = { width: width, height: height };
  301. }
  302. //Assuming here that "size" parameter is a number
  303. else if (!isNaN(size)) {
  304. height = size;
  305. width = size;
  306. }
  307. else {
  308. console.error("Invalid 'size' parameter !");
  309. }
  310. var texture = new RenderTargetTexture("screenShot", size, engine.scenes[0]);
  311. texture.renderList = engine.scenes[0].meshes;
  312. texture.onAfterRender = function () {
  313. // Read the contents of the framebuffer
  314. var numberOfChannelsByLine = width * 4;
  315. var halfHeight = height / 2;
  316. //Reading datas from WebGL
  317. var data = engine.ReadPixels(0, 0, width, height);
  318. //To flip image on Y axis.
  319. for (var i = 0; i < halfHeight; i++) {
  320. for (var j = 0; j < numberOfChannelsByLine; j++) {
  321. var currentCell = j + i * numberOfChannelsByLine;
  322. var targetLine = height - i - 1;
  323. var targetCell = j + targetLine * numberOfChannelsByLine;;
  324. var temp = data[currentCell];
  325. data[currentCell] = data[targetCell];
  326. data[targetCell] = temp;
  327. }
  328. }
  329. // Create a 2D canvas to store the result
  330. if (!Tools._ScreenshotCanvas) {
  331. Tools._ScreenshotCanvas = document.createElement('canvas');
  332. }
  333. Tools._ScreenshotCanvas.width = width;
  334. Tools._ScreenshotCanvas.height = height;
  335. var context = Tools._ScreenshotCanvas.getContext('2d');
  336. // Copy the pixels to a 2D canvas
  337. var imageData = context.createImageData(width, height);
  338. imageData.data.set(data);
  339. context.putImageData(imageData, 0, 0);
  340. var base64Image = Tools._ScreenshotCanvas.toDataURL();
  341. //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
  342. if (("download" in document.createElement("a"))) {
  343. var a = window.document.createElement("a");
  344. a.href = base64Image;
  345. var date = new Date();
  346. var stringDate = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate() + "-" + date.getHours() + ":" + date.getMinutes();
  347. a.setAttribute("download", "screenshot-" + stringDate);
  348. window.document.body.appendChild(a);
  349. a.addEventListener("click", function () {
  350. a.parentElement.removeChild(a);
  351. });
  352. a.click();
  353. //Or opening a new tab with the image if it is not possible to automatically start download.
  354. } else {
  355. var newWindow = window.open("");
  356. var img = newWindow.document.createElement("img");
  357. img.src = base64Image;
  358. newWindow.document.body.appendChild(img);
  359. }
  360. };
  361. texture.render();
  362. texture.dispose();
  363. }
  364. }
  365. }