babylon.collisionWorker.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. module BABYLON.CollisionWorker {
  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. var closeDistance = BABYLON.Engine.CollisionsEpsilon * 10.0;
  31. //is initializing here correct? A quick look - looks like it is fine.
  32. if (this.collider.retry >= maximumRetry) {
  33. this.finalPosition.copyFrom(position);
  34. return;
  35. }
  36. this.collider._initialize(position, velocity, closeDistance);
  37. // Check all meshes
  38. var meshes = this._collisionCache.getMeshes();
  39. for (var uniqueId in meshes) {
  40. if (meshes.hasOwnProperty(uniqueId) && parseInt(uniqueId) != excludedMeshUniqueId) {
  41. var mesh: SerializedMesh = meshes[uniqueId];
  42. if (mesh.checkCollisions)
  43. this.checkCollision(mesh);
  44. }
  45. }
  46. if (!this.collider.collisionFound) {
  47. position.addToRef(velocity, this.finalPosition);
  48. return;
  49. }
  50. if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
  51. this.collider._getResponse(position, velocity);
  52. }
  53. if (velocity.length() <= closeDistance) {
  54. //console.log("webworker collision with " + this.collider.collidedMesh);
  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. // No Octrees for now
  75. //if (this._submeshesOctree && this.useOctreeForCollisions) {
  76. // var radius = collider.velocityWorldLength + Math.max(collider.radius.x, collider.radius.y, collider.radius.z);
  77. // var intersections = this._submeshesOctree.intersects(collider.basePointWorld, radius);
  78. // len = intersections.length;
  79. // subMeshes = intersections.data;
  80. //} else {
  81. // subMeshes = this.subMeshes;
  82. // len = subMeshes.length;
  83. //}
  84. if (!mesh.geometryId) {
  85. console.log("no mesh geometry id");
  86. return;
  87. }
  88. var meshGeometry = this._collisionCache.getGeometry(mesh.geometryId);
  89. if (!meshGeometry) {
  90. console.log("couldn't find geometry", mesh.geometryId);
  91. return;
  92. }
  93. for (var index = 0; index < mesh.subMeshes.length; index++) {
  94. var subMesh = mesh.subMeshes[index];
  95. // Bounding test
  96. if (len > 1 && !this.checkSubmeshCollision(subMesh))
  97. continue;
  98. subMesh['getMesh'] = function () {
  99. return mesh.uniqueId;
  100. }
  101. this.collideForSubMesh(subMesh, transformMatrix, meshGeometry);
  102. }
  103. }
  104. private collideForSubMesh(subMesh: SerializedSubMesh, transformMatrix: BABYLON.Matrix, meshGeometry: SerializedGeometry): void {
  105. var positionsArray = [];
  106. for (var i = 0; i < meshGeometry.positions.length; i = i + 3) {
  107. var p = BABYLON.Vector3.FromArray([meshGeometry.positions[i], meshGeometry.positions[i + 1], meshGeometry.positions[i + 2]]);
  108. positionsArray.push(p);
  109. }
  110. subMesh['_lastColliderTransformMatrix'] = transformMatrix.clone();
  111. subMesh['_lastColliderWorldVertices'] = [];
  112. subMesh['_trianglePlanes'] = [];
  113. var start = subMesh.verticesStart;
  114. var end = (subMesh.verticesStart + subMesh.verticesCount);
  115. for (var i = start; i < end; i++) {
  116. subMesh['_lastColliderWorldVertices'].push(BABYLON.Vector3.TransformCoordinates(positionsArray[i], transformMatrix));
  117. }
  118. subMesh['getMaterial'] = function () {
  119. return true;
  120. }
  121. //}
  122. // Collide
  123. this.collider._collide(subMesh, subMesh['_lastColliderWorldVertices'], <any> meshGeometry.indices, subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart);
  124. }
  125. //TODO - this! :-)
  126. private checkSubmeshCollision(subMesh: SerializedSubMesh) {
  127. return true;
  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. console.log("updated");
  161. postMessage(replay, undefined);
  162. }
  163. public onCollision(payload: CollidePayload) {
  164. var finalPosition = BABYLON.Vector3.Zero();
  165. //create a new collider
  166. var collider = new BABYLON.Collider();
  167. collider.radius = BABYLON.Vector3.FromArray(payload.collider.radius);
  168. var colliderWorker = new CollideWorker(collider, this._collisionCache, finalPosition);
  169. colliderWorker.collideWithWorld(BABYLON.Vector3.FromArray(payload.collider.position), BABYLON.Vector3.FromArray(payload.collider.velocity), payload.maximumRetry, payload.excludedMeshUniqueId);
  170. var replyPayload: CollisionReplyPayload = {
  171. collidedMeshUniqueId: <any> collider.collidedMesh,
  172. collisionId: payload.collisionId,
  173. newPosition: finalPosition.asArray()
  174. }
  175. var reply: WorkerReply = {
  176. error: WorkerReplyType.SUCCESS,
  177. taskType: WorkerTaskType.COLLIDE,
  178. payload: replyPayload
  179. }
  180. postMessage(reply, undefined);
  181. }
  182. }
  183. //check if we are in a web worker, as this code should NOT run on the main UI thread
  184. if (self && !self.document) {
  185. var collisionDetector: ICollisionDetector = new CollisionDetectorTransferable();
  186. var onNewMessage = function (event: MessageEvent) {
  187. var message = <BabylonMessage> event.data;
  188. switch (message.taskType) {
  189. case WorkerTaskType.INIT:
  190. collisionDetector.onInit(<InitPayload> message.payload);
  191. break;
  192. case WorkerTaskType.COLLIDE:
  193. collisionDetector.onCollision(<CollidePayload> message.payload);
  194. break;
  195. case WorkerTaskType.UPDATE:
  196. collisionDetector.onUpdate(<UpdatePayload> message.payload);
  197. break;
  198. }
  199. }
  200. self.onmessage = onNewMessage;
  201. }
  202. }