babylon.scene.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. var BABYLON = BABYLON || {};
  2. (function () {
  3. BABYLON.Scene = function (engine) {
  4. this._engine = engine;
  5. this.autoClear = true;
  6. this.clearColor = new BABYLON.Color3(0.2, 0.2, 0.3);
  7. this.ambientColor = new BABYLON.Color3(0, 0, 0);
  8. engine.scenes.push(this);
  9. this._totalVertices = 0;
  10. this._activeVertices = 0;
  11. this._activeParticles = 0;
  12. this._lastFrameDuration = 0;
  13. this._evaluateActiveMeshesDuration = 0;
  14. this._renderTargetsDuration = 0;
  15. this._renderDuration = 0;
  16. this._toBeDisposed = [];
  17. this._onReadyCallbacks = [];
  18. this._pendingData = [];
  19. this._onBeforeRenderCallbacks = [];
  20. // Fog
  21. this.fogMode = BABYLON.Scene.FOGMODE_NONE;
  22. this.fogColor = new BABYLON.Color3(0.2, 0.2, 0.3);
  23. this.fogDensity = 0.1;
  24. this.fogStart = 0;
  25. this.fogEnd = 1000.0;
  26. // Lights
  27. this.lights = [];
  28. // Cameras
  29. this.cameras = [];
  30. this.activeCamera = null;
  31. // Meshes
  32. this.meshes = [];
  33. this._activeMeshes = [];
  34. // Materials
  35. this.materials = [];
  36. this.multiMaterials = [];
  37. this.defaultMaterial = new BABYLON.StandardMaterial("default material", this);
  38. // Textures
  39. this.textures = [];
  40. // Particles
  41. this.particleSystems = [];
  42. // Sprites
  43. this.spriteManagers = [];
  44. // Layers
  45. this.layers = [];
  46. // Collisions
  47. this.collisionsEnabled = true;
  48. this.gravity = new BABYLON.Vector3(0, 0, -9);
  49. // Animations
  50. this._activeAnimatables = [];
  51. };
  52. // Properties
  53. BABYLON.Scene.prototype.getEngine = function () {
  54. return this._engine;
  55. };
  56. BABYLON.Scene.prototype.getTotalVertices = function () {
  57. return this._totalVertices;
  58. };
  59. BABYLON.Scene.prototype.getActiveVertices = function () {
  60. return this._activeVertices;
  61. };
  62. BABYLON.Scene.prototype.getTotalVertices = function () {
  63. return this._totalVertices;
  64. };
  65. BABYLON.Scene.prototype.getActiveParticles = function () {
  66. return this._activeParticles;
  67. };
  68. // Stats
  69. BABYLON.Scene.prototype.getLastFrameDuration = function () {
  70. return this._lastFrameDuration;
  71. };
  72. BABYLON.Scene.prototype.getEvaluateActiveMeshesDuration = function () {
  73. return this._evaluateActiveMeshesDuration;
  74. };
  75. BABYLON.Scene.prototype.getRenderTargetsDuration = function () {
  76. return this._renderTargetsDuration;
  77. };
  78. BABYLON.Scene.prototype.getRenderDuration = function () {
  79. return this._renderDuration;
  80. };
  81. BABYLON.Scene.prototype.getParticlesDuration = function () {
  82. return this._particlesDuration;
  83. };
  84. BABYLON.Scene.prototype.getSpritesDuration = function () {
  85. return this._spritesDuration;
  86. };
  87. BABYLON.Scene.prototype.getAnimationRatio = function () {
  88. return this._animationRatio;
  89. };
  90. // Ready
  91. BABYLON.Scene.prototype.isReady = function () {
  92. for (var index = 0; index < this.materials.length; index++) {
  93. if (!this.materials[index].isReady()) {
  94. return false;
  95. }
  96. }
  97. return true;
  98. };
  99. BABYLON.Scene.prototype.executeWhenReady = function (func) {
  100. if (this.isReady()) {
  101. func();
  102. return;
  103. }
  104. if (this._pendingData.length === 0) {
  105. func();
  106. return;
  107. }
  108. this._onReadyCallbacks.push(func);
  109. };
  110. BABYLON.Scene.prototype.registerBeforeRender = function (func) {
  111. this._onBeforeRenderCallbacks.push(func);
  112. };
  113. BABYLON.Scene.prototype.unregisterBeforeRender = function (func) {
  114. var index = this._onBeforeRenderCallbacks.indexOf(func);
  115. if (index > -1) {
  116. this._onBeforeRenderCallbacks.splice(index, 1);
  117. }
  118. };
  119. BABYLON.Scene.prototype._addPendingData = function (data) {
  120. this._pendingData.push(data);
  121. };
  122. BABYLON.Scene.prototype._removePendingData = function (data) {
  123. var index = this._pendingData.indexOf(data);
  124. if (index !== -1) {
  125. this._pendingData.splice(index, 1);
  126. if (this._pendingData.length === 0) {
  127. this._onReadyCallbacks.forEach(function (func) {
  128. func();
  129. });
  130. this._onReadyCallbacks = [];
  131. }
  132. }
  133. };
  134. // Animations
  135. BABYLON.Scene.prototype.beginAnimation = function (target, from, to, loop, speedRatio) {
  136. if (speedRatio === undefined) {
  137. speedRatio = 1.0;
  138. }
  139. // Local animations
  140. if (target.animations) {
  141. this.stopAnimation(target);
  142. var animatable = new BABYLON._Animatable(target, from, to, loop, speedRatio);
  143. this._activeAnimatables.push(animatable);
  144. }
  145. // Children animations
  146. if (target.getAnimatables) {
  147. var animatables = target.getAnimatables();
  148. for (var index = 0; index < animatables.length; index++) {
  149. this.beginAnimation(animatables[index], from, to, loop, speedRatio);
  150. }
  151. }
  152. };
  153. BABYLON.Scene.prototype.stopAnimation = function (target) {
  154. for (var index = 0; index < this._activeAnimatables.length; index++) {
  155. if (this._activeAnimatables[index].target === target) {
  156. this._activeAnimatables.splice(index, 1);
  157. return;
  158. }
  159. }
  160. };
  161. BABYLON.Scene.prototype._animate = function () {
  162. for (var index = 0; index < this._activeAnimatables.length; index++) {
  163. if (!this._activeAnimatables[index]._animate()) {
  164. this._activeAnimatables.splice(index, 1);
  165. index--;
  166. }
  167. }
  168. };
  169. // Matrix
  170. BABYLON.Scene.prototype.getViewMatrix = function () {
  171. return this._viewMatrix;
  172. };
  173. BABYLON.Scene.prototype.getProjectionMatrix = function () {
  174. return this._projectionMatrix;
  175. };
  176. BABYLON.Scene.prototype.getTransformMatrix = function () {
  177. return this._transformMatrix;
  178. };
  179. BABYLON.Scene.prototype.setTransformMatrix = function (view, projection) {
  180. this._viewMatrix = view;
  181. this._projectionMatrix = projection;
  182. this._transformMatrix = this._viewMatrix.multiply(this._projectionMatrix);
  183. };
  184. // Methods
  185. BABYLON.Scene.prototype.activeCameraByID = function (id) {
  186. for (var index = 0; index < this.cameras.length; index++) {
  187. if (this.cameras[index].id == id) {
  188. this.activeCamera = this.cameras[index];
  189. return;
  190. }
  191. }
  192. };
  193. BABYLON.Scene.prototype.getMaterialByID = function (id) {
  194. for (var index = 0; index < this.materials.length; index++) {
  195. if (this.materials[index].id == id) {
  196. return this.materials[index];
  197. }
  198. }
  199. return null;
  200. };
  201. BABYLON.Scene.prototype.getMeshByID = function (id) {
  202. for (var index = 0; index < this.meshes.length; index++) {
  203. if (this.meshes[index].id == id) {
  204. return this.meshes[index];
  205. }
  206. }
  207. return null;
  208. };
  209. BABYLON.Scene.prototype.getLastMeshByID = function (id) {
  210. var result = null;
  211. for (var index = 0; index < this.meshes.length; index++) {
  212. if (this.meshes[index].id == id) {
  213. result = this.meshes[index];
  214. }
  215. }
  216. return result;
  217. };
  218. BABYLON.Scene.prototype.getMeshByName = function (name) {
  219. for (var index = 0; index < this.meshes.length; index++) {
  220. if (this.meshes[index].name == name) {
  221. return this.meshes[index];
  222. }
  223. }
  224. return null;
  225. };
  226. BABYLON.Scene.prototype.isActiveMesh = function (mesh) {
  227. return (this._activeMeshes.indexOf(mesh) !== -1);
  228. };
  229. BABYLON.Scene.prototype._evaluateActiveMeshes = function () {
  230. this._activeMeshes = [];
  231. this._opaqueSubMeshes = [];
  232. this._transparentSubMeshes = [];
  233. this._alphaTestSubMeshes = [];
  234. this._processedMaterials = [];
  235. this._renderTargets = [];
  236. this._activeParticleSystems = [];
  237. var frustumPlanes = BABYLON.Frustum.GetPlanes(this._transformMatrix);
  238. this._totalVertices = 0;
  239. this._activeVertices = 0;
  240. // meshes
  241. for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  242. var mesh = this.meshes[meshIndex];
  243. this._totalVertices += mesh.getTotalVertices();
  244. mesh.computeWorldMatrix();
  245. if (mesh.isEnabled() && mesh.isVisible && mesh.visibility > 0 && mesh.isInFrustrum(frustumPlanes)) {
  246. for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
  247. var subMesh = mesh.subMeshes[subIndex];
  248. if (mesh.subMeshes.length == 1 || subMesh.isInFrustrum(frustumPlanes)) {
  249. var material = subMesh.getMaterial();
  250. if (this._activeMeshes.indexOf(mesh) === -1) {
  251. this._activeMeshes.push(mesh);
  252. }
  253. if (material) {
  254. // Render targets
  255. if (material.getRenderTargetTextures) {
  256. if (this._processedMaterials.indexOf(material) === -1) {
  257. this._processedMaterials.push(material);
  258. this._renderTargets = this._renderTargets.concat(material.getRenderTargetTextures());
  259. }
  260. }
  261. // Dispatch
  262. if (material.needAlphaBlending() || mesh.visibility < 1.0) { // Transparent
  263. if (material.alpha > 0 || mesh.visibility < 1.0) {
  264. this._transparentSubMeshes.push(subMesh); // Opaque
  265. }
  266. } else if (material.needAlphaTesting()) { // Alpha test
  267. this._alphaTestSubMeshes.push(subMesh);
  268. } else {
  269. this._opaqueSubMeshes.push(subMesh);
  270. }
  271. }
  272. }
  273. }
  274. }
  275. }
  276. // Particle systems
  277. var beforeParticlesDate = new Date();
  278. for (var particleIndex = 0; particleIndex < this.particleSystems.length; particleIndex++) {
  279. var particleSystem = this.particleSystems[particleIndex];
  280. if (!particleSystem.emitter.position || (particleSystem.emitter && particleSystem.emitter.isEnabled())) {
  281. this._activeParticleSystems.push(particleSystem);
  282. particleSystem.animate();
  283. }
  284. }
  285. this._particlesDuration += new Date() - beforeParticlesDate;
  286. };
  287. BABYLON.Scene.prototype._localRender = function (opaqueSubMeshes, alphaTestSubMeshes, transparentSubMeshes, activeMeshes) {
  288. var engine = this._engine;
  289. // Opaque
  290. var subIndex;
  291. var submesh;
  292. for (subIndex = 0; subIndex < opaqueSubMeshes.length; subIndex++) {
  293. submesh = opaqueSubMeshes[subIndex];
  294. this._activeVertices += submesh.verticesCount;
  295. submesh.render();
  296. }
  297. // Alpha test
  298. engine.setAlphaTesting(true);
  299. for (subIndex = 0; subIndex < alphaTestSubMeshes.length; subIndex++) {
  300. submesh = alphaTestSubMeshes[subIndex];
  301. this._activeVertices += submesh.verticesCount;
  302. submesh.render();
  303. }
  304. engine.setAlphaTesting(false);
  305. if (!activeMeshes) {
  306. // Sprites
  307. var beforeSpritessDate = new Date();
  308. for (var index = 0; index < this.spriteManagers.length; index++) {
  309. var spriteManager = this.spriteManagers[index];
  310. spriteManager.render();
  311. }
  312. this._spritesDuration = new Date() - beforeSpritessDate;
  313. }
  314. // Transparent
  315. engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
  316. for (subIndex = 0; subIndex < transparentSubMeshes.length; subIndex++) {
  317. submesh = transparentSubMeshes[subIndex];
  318. this._activeVertices += submesh.verticesCount;
  319. submesh.render();
  320. }
  321. engine.setAlphaMode(BABYLON.Engine.ALPHA_DISABLE);
  322. // Particles
  323. var beforeParticlesDate = new Date();
  324. for (var particleIndex = 0; particleIndex < this._activeParticleSystems.length; particleIndex++) {
  325. var particleSystem = this._activeParticleSystems[particleIndex];
  326. if (!activeMeshes || activeMeshes.indexOf(particleSystem.emitter) !== -1) {
  327. this._activeParticles += particleSystem.render();
  328. }
  329. }
  330. this._particlesDuration += new Date() - beforeParticlesDate;
  331. };
  332. BABYLON.Scene.prototype.render = function () {
  333. var startDate = new Date();
  334. this._particlesDuration = 0;
  335. this._activeParticles = 0;
  336. var engine = this._engine;
  337. // Before render
  338. if (this.beforeRender) {
  339. this.beforeRender();
  340. }
  341. for (var callbackIndex = 0; callbackIndex < this._onBeforeRenderCallbacks.length; callbackIndex++) {
  342. this._onBeforeRenderCallbacks[callbackIndex]();
  343. }
  344. // Camera
  345. if (!this.activeCamera)
  346. throw new Error("Active camera not set");
  347. this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
  348. // Animations
  349. this._animationRatio = BABYLON.Tools.GetDeltaTime() * (60.0 / 1000.0);
  350. this._animate();
  351. // Meshes
  352. var beforeEvaluateActiveMeshesDate = new Date();
  353. this._evaluateActiveMeshes();
  354. this._evaluateActiveMeshesDuration = new Date() - beforeEvaluateActiveMeshesDate;
  355. // Render targets
  356. var beforeRenderTargetDate = new Date();
  357. for (var renderIndex = 0; renderIndex < this._renderTargets.length; renderIndex++) {
  358. var renderTarget = this._renderTargets[renderIndex];
  359. renderTarget.render();
  360. }
  361. if (this._renderTargets.length > 0) { // Restore back buffer
  362. engine.restoreDefaultFramebuffer();
  363. }
  364. this._renderTargetsDuration = new Date() - beforeRenderTargetDate;
  365. // Clear
  366. var beforeRenderDate = new Date();
  367. engine.clear(this.clearColor, this.autoClear, true);
  368. // Backgrounds
  369. if (this.layers.length) {
  370. engine.setDepthBuffer(false);
  371. var layerIndex;
  372. var layer;
  373. for (layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
  374. layer = this.layers[layerIndex];
  375. if (layer.isBackground) {
  376. layer.render();
  377. }
  378. }
  379. engine.setDepthBuffer(true);
  380. }
  381. // Render
  382. this._localRender(this._opaqueSubMeshes, this._alphaTestSubMeshes, this._transparentSubMeshes);
  383. // Foregrounds
  384. if (this.layers.length) {
  385. engine.setDepthBuffer(false);
  386. for (layerIndex = 0; layerIndex < this.layers.length; layerIndex++) {
  387. layer = this.layers[layerIndex];
  388. if (!layer.isBackground) {
  389. layer.render();
  390. }
  391. }
  392. engine.setDepthBuffer(true);
  393. }
  394. this._renderDuration = new Date() - beforeRenderDate;
  395. // Update camera
  396. this.activeCamera._update();
  397. // After render
  398. if (this.afterRender) {
  399. this.afterRender();
  400. }
  401. // Cleaning
  402. for (var index = 0; index < this._toBeDisposed.length; index++) {
  403. this._toBeDisposed[index].dispose();
  404. }
  405. this._toBeDisposed = [];
  406. this._lastFrameDuration = new Date() - startDate;
  407. };
  408. BABYLON.Scene.prototype.dispose = function () {
  409. this.beforeRender = null;
  410. this.afterRender = null;
  411. // Detach cameras
  412. var canvas = this._engine.getRenderingCanvas();
  413. var index;
  414. for (index = 0; index < this.cameras.length; index++) {
  415. this.cameras[index].detachControl(canvas);
  416. }
  417. // Release meshes
  418. while (this.meshes.length) {
  419. this.meshes[0].dispose(true);
  420. }
  421. // Release materials
  422. while (this.materials.length) {
  423. this.materials[0].dispose();
  424. }
  425. // Release particles
  426. while (this.particleSystems.length) {
  427. this.particleSystems[0].dispose();
  428. }
  429. // Release sprites
  430. while (this.spriteManagers.length) {
  431. this.spriteManagers[0].dispose();
  432. }
  433. // Release layers
  434. while (this.layers.length) {
  435. this.layers[0].dispose();
  436. }
  437. // Release textures
  438. while (this.textures.length) {
  439. this.textures[index].dispose();
  440. }
  441. // Remove from engine
  442. index = this._engine.scenes.indexOf(this);
  443. this._engine.scenes.splice(index, 1);
  444. this._engine.wipeCaches();
  445. };
  446. // Collisions
  447. BABYLON.Scene.prototype._getNewPosition = function (position, velocity, collider, maximumRetry) {
  448. var scaledPosition = position.divide(collider.radius);
  449. var scaledVelocity = velocity.divide(collider.radius);
  450. collider.retry = 0;
  451. collider.initialVelocity = scaledVelocity;
  452. collider.initialPosition = scaledPosition;
  453. var finalPosition = this._collideWithWorld(scaledPosition, scaledVelocity, collider, maximumRetry);
  454. finalPosition = finalPosition.multiply(collider.radius);
  455. return finalPosition;
  456. };
  457. BABYLON.Scene.prototype._collideWithWorld = function (position, velocity, collider, maximumRetry) {
  458. var closeDistance = BABYLON.Engine.collisionsEpsilon * 10.0;
  459. if (collider.retry >= maximumRetry) {
  460. return position;
  461. }
  462. collider._initialize(position, velocity, closeDistance);
  463. // Check all meshes
  464. for (var index = 0; index < this.meshes.length; index++) {
  465. var mesh = this.meshes[index];
  466. if (mesh.isEnabled() && mesh.checkCollisions) {
  467. mesh._checkCollision(collider);
  468. }
  469. }
  470. if (!collider.collisionFound) {
  471. return position.add(velocity);
  472. }
  473. if (velocity.x != 0 || velocity.y != 0 || velocity.z != 0) {
  474. var response = collider._getResponse(position, velocity);
  475. position = response.position;
  476. velocity = response.velocity;
  477. }
  478. if (velocity.length() <= closeDistance) {
  479. return position;
  480. }
  481. collider.retry++;
  482. return this._collideWithWorld(position, velocity, collider, maximumRetry);
  483. };
  484. // Picking
  485. BABYLON.Scene.prototype.createPickingRay = function (x, y, world) {
  486. var engine = this._engine;
  487. if (!this._viewMatrix) {
  488. if (!this.activeCamera)
  489. throw new Error("Active camera not set");
  490. this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
  491. }
  492. return BABYLON.Ray.CreateNew(x, y, engine.getRenderWidth(), engine.getRenderHeight(), world ? world : BABYLON.Matrix.Identity(), this._viewMatrix, this._projectionMatrix);
  493. };
  494. BABYLON.Scene.prototype.pick = function (x, y) {
  495. var distance = Number.MAX_VALUE;
  496. var pickedPoint = null;
  497. var pickedMesh = null;
  498. for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
  499. var mesh = this.meshes[meshIndex];
  500. if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
  501. continue;
  502. }
  503. var world = mesh.getWorldMatrix();
  504. var ray = this.createPickingRay(x, y, world);
  505. var result = mesh.intersects(ray);
  506. if (!result.hit)
  507. continue;
  508. if (result.distance >= distance)
  509. continue;
  510. distance = result.distance;
  511. pickedMesh = mesh;
  512. // Get picked point
  513. var worldOrigin = BABYLON.Vector3.TransformCoordinates(ray.origin, world);
  514. var direction = ray.direction.clone();
  515. direction.normalize();
  516. direction = direction.scale(result.distance);
  517. var worldDirection = BABYLON.Vector3.TransformNormal(direction, world);
  518. pickedPoint = worldOrigin.add(worldDirection);
  519. }
  520. return { hit: distance != Number.MAX_VALUE, distance: distance, pickedMesh: pickedMesh, pickedPoint: pickedPoint };
  521. };
  522. // Statics
  523. BABYLON.Scene.FOGMODE_NONE = 0;
  524. BABYLON.Scene.FOGMODE_EXP = 1;
  525. BABYLON.Scene.FOGMODE_EXP2 = 2;
  526. BABYLON.Scene.FOGMODE_LINEAR = 3;
  527. })();