babylon.collisionWorker.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. module BABYLON {
  2. export class CollisionCache {
  3. private _meshes: { [n: number]: SerializedMesh; } = {};
  4. private _geometries: { [s: number]: SerializedGeometry; } = {};
  5. public getMeshes(): { [n: number]: SerializedMesh; } {
  6. return this._meshes;
  7. }
  8. public getGeometries(): { [s: number]: SerializedGeometry; } {
  9. return this._geometries;
  10. }
  11. public getMesh(id: any): SerializedMesh {
  12. return this._meshes[id];
  13. }
  14. public addMesh(mesh: SerializedMesh) {
  15. this._meshes[mesh.uniqueId] = mesh;
  16. }
  17. public getGeometry(id: string): SerializedGeometry {
  18. return this._geometries[id];
  19. }
  20. public addGeometry(geometry: SerializedGeometry) {
  21. this._geometries[geometry.id] = geometry;
  22. }
  23. }
  24. export class CollideWorker {
  25. private collisionsScalingMatrix = BABYLON.Matrix.Zero();
  26. private collisionTranformationMatrix = BABYLON.Matrix.Zero();
  27. constructor(public collider: BABYLON.Collider, private _collisionCache: CollisionCache, private finalPosition: BABYLON.Vector3) {
  28. }
  29. public collideWithWorld(position: BABYLON.Vector3, velocity: BABYLON.Vector3, maximumRetry: number, excludedMeshUniqueId?: number) {
  30. //TODO CollisionsEpsilon should be defined here and not in the engine.
  31. var closeDistance = /*BABYLON.Engine.CollisionsEpsilon * 10.0*/ 0.01;
  32. //is initializing here correct? A quick look - looks like it is fine.
  33. if (this.collider.retry >= maximumRetry) {
  34. this.finalPosition.copyFrom(position);
  35. return;
  36. }
  37. this.collider._initialize(position, velocity, closeDistance);
  38. // Check all meshes
  39. var meshes = this._collisionCache.getMeshes();
  40. for (var uniqueId in meshes) {
  41. if (meshes.hasOwnProperty(uniqueId) && parseInt(uniqueId) != excludedMeshUniqueId) {
  42. var mesh: SerializedMesh = meshes[uniqueId];
  43. if (mesh.checkCollisions)
  44. this.checkCollision(mesh);
  45. }
  46. }
  47. if (!this.collider.collisionFound) {
  48. position.addToRef(velocity, this.finalPosition);
  49. return;
  50. }
  51. if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
  52. this.collider._getResponse(position, velocity);
  53. }
  54. if (velocity.length() <= closeDistance) {
  55. this.finalPosition.copyFrom(position);
  56. return;
  57. }
  58. this.collider.retry++;
  59. this.collideWithWorld(position, velocity, maximumRetry, excludedMeshUniqueId);
  60. }
  61. private checkCollision(mesh: SerializedMesh) {
  62. if (!this.collider._canDoCollision(BABYLON.Vector3.FromArray(mesh.sphereCenter), mesh.sphereRadius, BABYLON.Vector3.FromArray(mesh.boxMinimum), BABYLON.Vector3.FromArray(mesh.boxMaximum))) {
  63. return;
  64. };
  65. // Transformation matrix
  66. BABYLON.Matrix.ScalingToRef(1.0 / this.collider.radius.x, 1.0 / this.collider.radius.y, 1.0 / this.collider.radius.z, this.collisionsScalingMatrix);
  67. var worldFromCache = BABYLON.Matrix.FromArray(mesh.worldMatrixFromCache);
  68. worldFromCache.multiplyToRef(this.collisionsScalingMatrix, this.collisionTranformationMatrix);
  69. this.processCollisionsForSubMeshes(this.collisionTranformationMatrix, mesh);
  70. //return colTransMat;
  71. }
  72. private processCollisionsForSubMeshes(transformMatrix: BABYLON.Matrix, mesh: SerializedMesh): void {
  73. var len: number;
  74. var subMeshes;
  75. // No Octrees for now
  76. //if (this._submeshesOctree && this.useOctreeForCollisions) {
  77. // var radius = collider.velocityWorldLength + Math.max(collider.radius.x, collider.radius.y, collider.radius.z);
  78. // var intersections = this._submeshesOctree.intersects(collider.basePointWorld, radius);
  79. // len = intersections.length;
  80. // subMeshes = intersections.data;
  81. //} else {
  82. subMeshes = mesh.subMeshes;
  83. len = subMeshes.length;
  84. //}
  85. if (!mesh.geometryId) {
  86. console.log("no mesh geometry id");
  87. return;
  88. }
  89. var meshGeometry = this._collisionCache.getGeometry(mesh.geometryId);
  90. if (!meshGeometry) {
  91. console.log("couldn't find geometry", mesh.geometryId);
  92. return;
  93. }
  94. for (var index = 0; index < len; index++) {
  95. var subMesh = subMeshes[index];
  96. // Bounding test
  97. if (len > 1 && !this.checkSubmeshCollision(subMesh))
  98. continue;
  99. //Unneeded
  100. //subMesh['getMesh'] = function () {
  101. // return mesh.uniqueId;
  102. //}
  103. this.collideForSubMesh(subMesh, transformMatrix, meshGeometry);
  104. }
  105. }
  106. private collideForSubMesh(subMesh: SerializedSubMesh, transformMatrix: BABYLON.Matrix, meshGeometry: SerializedGeometry): void {
  107. var positionsArray = [];
  108. for (var i = 0; i < meshGeometry.positions.length; i = i + 3) {
  109. var p = BABYLON.Vector3.FromArray([meshGeometry.positions[i], meshGeometry.positions[i + 1], meshGeometry.positions[i + 2]]);
  110. positionsArray.push(p);
  111. }
  112. subMesh['_lastColliderTransformMatrix'] = transformMatrix.clone();
  113. //The following two arrays should be initialized CORRECTLY to save some calculation time.
  114. subMesh['_lastColliderWorldVertices'] = [];
  115. subMesh['_trianglePlanes'] = [];
  116. var start = subMesh.verticesStart;
  117. var end = (subMesh.verticesStart + subMesh.verticesCount);
  118. for (var i = start; i < end; i++) {
  119. subMesh['_lastColliderWorldVertices'].push(BABYLON.Vector3.TransformCoordinates(positionsArray[i], transformMatrix));
  120. }
  121. //}
  122. // Collide
  123. this.collider._collide(subMesh['_trianglePlanes'] = [], subMesh['_lastColliderWorldVertices'], <any> meshGeometry.indices, subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, subMesh.hasMaterial);
  124. }
  125. //TODO - this! :-)
  126. private checkSubmeshCollision(subMesh: SerializedSubMesh) : boolean {
  127. return this.collider._canDoCollision(BABYLON.Vector3.FromArray(subMesh.sphereCenter), subMesh.sphereRadius, BABYLON.Vector3.FromArray(subMesh.boxMinimum), BABYLON.Vector3.FromArray(subMesh.boxMaximum));
  128. }
  129. }
  130. export interface ICollisionDetector {
  131. onInit(payload: InitPayload): void;
  132. onUpdate(payload: UpdatePayload): void;
  133. onCollision(payload: CollidePayload): void;
  134. }
  135. export class CollisionDetectorTransferable implements ICollisionDetector {
  136. private _collisionCache: CollisionCache;
  137. public onInit(payload: InitPayload) {
  138. this._collisionCache = new CollisionCache();
  139. var reply: WorkerReply = {
  140. error: WorkerReplyType.SUCCESS,
  141. taskType: WorkerTaskType.INIT
  142. }
  143. postMessage(reply, undefined);
  144. }
  145. public onUpdate(payload: UpdatePayload) {
  146. for (var id in payload.updatedGeometries) {
  147. if (payload.updatedGeometries.hasOwnProperty(id)) {
  148. this._collisionCache.addGeometry(payload.updatedGeometries[id]);
  149. }
  150. }
  151. for (var uniqueId in payload.updatedMeshes) {
  152. if (payload.updatedMeshes.hasOwnProperty(uniqueId)) {
  153. this._collisionCache.addMesh(payload.updatedMeshes[uniqueId]);
  154. }
  155. }
  156. var replay: WorkerReply = {
  157. error: WorkerReplyType.SUCCESS,
  158. taskType: WorkerTaskType.UPDATE
  159. }
  160. postMessage(replay, undefined);
  161. }
  162. public onCollision(payload: CollidePayload) {
  163. var finalPosition = BABYLON.Vector3.Zero();
  164. //create a new collider
  165. var collider = new BABYLON.Collider();
  166. collider.radius = BABYLON.Vector3.FromArray(payload.collider.radius);
  167. var colliderWorker = new CollideWorker(collider, this._collisionCache, finalPosition);
  168. colliderWorker.collideWithWorld(BABYLON.Vector3.FromArray(payload.collider.position), BABYLON.Vector3.FromArray(payload.collider.velocity), payload.maximumRetry, payload.excludedMeshUniqueId);
  169. var replyPayload: CollisionReplyPayload = {
  170. collidedMeshUniqueId: <any> collider.collidedMesh,
  171. collisionId: payload.collisionId,
  172. newPosition: finalPosition.asArray()
  173. }
  174. var reply: WorkerReply = {
  175. error: WorkerReplyType.SUCCESS,
  176. taskType: WorkerTaskType.COLLIDE,
  177. payload: replyPayload
  178. }
  179. postMessage(reply, undefined);
  180. }
  181. }
  182. //TypeScript doesn't know WorkerGlobalScope
  183. declare class WorkerGlobalScope { }
  184. //check if we are in a web worker, as this code should NOT run on the main UI thread
  185. if (self && self instanceof WorkerGlobalScope) {
  186. //Window hack to allow including babylonjs native code. the <any> is for typescript.
  187. window = <any> {};
  188. var collisionDetector: ICollisionDetector = new CollisionDetectorTransferable();
  189. var onNewMessage = function (event: MessageEvent) {
  190. var message = <BabylonMessage> event.data;
  191. switch (message.taskType) {
  192. case WorkerTaskType.INIT:
  193. collisionDetector.onInit(<InitPayload> message.payload);
  194. break;
  195. case WorkerTaskType.COLLIDE:
  196. collisionDetector.onCollision(<CollidePayload> message.payload);
  197. break;
  198. case WorkerTaskType.UPDATE:
  199. collisionDetector.onUpdate(<UpdatePayload> message.payload);
  200. break;
  201. }
  202. }
  203. self.onmessage = onNewMessage;
  204. }
  205. }