babylon.collisionCoordinator.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. module BABYLON {
  2. export interface ICollisionCoordinator {
  3. getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void;
  4. init(scene: Scene): void;
  5. destroy(): void;
  6. //Update meshes and geometries
  7. onMeshAdded(mesh: AbstractMesh);
  8. onMeshUpdated(mesh: AbstractMesh);
  9. onMeshRemoved(mesh: AbstractMesh);
  10. onGeometryAdded(geometry: Geometry);
  11. onGeometryUpdated(geometry: Geometry);
  12. onGeometryDeleted(geometry: Geometry);
  13. }
  14. export interface SerializedMesh {
  15. id: string;
  16. name: string;
  17. uniqueId: number;
  18. geometryId: string;
  19. sphereCenter: Array<number>;
  20. sphereRadius: number;
  21. boxMinimum: Array<number>;
  22. boxMaximum: Array<number>;
  23. worldMatrixFromCache: any;
  24. subMeshes: Array<SerializedSubMesh>;
  25. checkCollisions: boolean;
  26. }
  27. export interface SerializedSubMesh {
  28. position: number;
  29. verticesStart: number;
  30. verticesCount: number;
  31. indexStart: number;
  32. indexCount: number;
  33. }
  34. export interface SerializedGeometry {
  35. id: string;
  36. positions: Float32Array;
  37. indices: Int32Array;
  38. normals: Float32Array;
  39. //uvs?: Float32Array;
  40. }
  41. export interface BabylonMessage {
  42. taskType: WorkerTaskType;
  43. payload: InitPayload|CollidePayload|UpdatePayload /*any for TS under 1.4*/;
  44. }
  45. export interface SerializedColliderToWorker {
  46. position: Array<number>;
  47. velocity: Array<number>;
  48. radius: Array<number>;
  49. }
  50. export enum WorkerTaskType {
  51. INIT,
  52. UPDATE,
  53. COLLIDE
  54. }
  55. export interface WorkerReply {
  56. error: WorkerReplyType;
  57. taskType: WorkerTaskType;
  58. payload?: any;
  59. }
  60. export interface CollisionReplyPayload {
  61. newPosition: Array<number>;
  62. collisionId: number;
  63. collidedMeshUniqueId: number;
  64. }
  65. export interface InitPayload {
  66. }
  67. export interface CollidePayload {
  68. collisionId: number;
  69. collider: SerializedColliderToWorker;
  70. maximumRetry: number;
  71. excludedMeshUniqueId?: number;
  72. }
  73. export interface UpdatePayload {
  74. updatedMeshes: { [n: number]: SerializedMesh; };
  75. updatedGeometries: { [s: string]: SerializedGeometry; };
  76. removedMeshes: Array<number>;
  77. removedGeometries: Array<string>;
  78. }
  79. export enum WorkerReplyType {
  80. SUCCESS,
  81. UNKNOWN_ERROR
  82. }
  83. export class CollisionCoordinatorWorker implements ICollisionCoordinator {
  84. private _scene: Scene;
  85. private _scaledPosition = Vector3.Zero();
  86. private _scaledVelocity = Vector3.Zero();
  87. private _collisionsCallbackArray: Array<(collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void>;
  88. private _init: boolean;
  89. private _runningUpdated: number;
  90. private _runningCollisionTask: boolean;
  91. private _worker: Worker;
  92. private _addUpdateMeshesList: { [n: number]: SerializedMesh; }
  93. private _addUpdateGeometriesList: { [s: string]: SerializedGeometry; };
  94. private _toRemoveMeshesArray: Array<number>;
  95. private _toRemoveGeometryArray: Array<string>;
  96. constructor() {
  97. this._collisionsCallbackArray = [];
  98. this._init = false;
  99. this._runningUpdated = 0;
  100. this._runningCollisionTask = false;
  101. this._addUpdateMeshesList = {};
  102. this._addUpdateGeometriesList = {};
  103. this._toRemoveGeometryArray = [];
  104. this._toRemoveMeshesArray = [];
  105. }
  106. public static SerializeMesh = function (mesh: BABYLON.AbstractMesh): SerializedMesh {
  107. var submeshes = [];
  108. if (mesh.subMeshes) {
  109. submeshes = mesh.subMeshes.map(function (sm, idx) {
  110. return {
  111. position: idx,
  112. verticesStart: sm.verticesStart,
  113. verticesCount: sm.verticesCount,
  114. indexStart: sm.indexStart,
  115. indexCount: sm.indexCount
  116. }
  117. });
  118. }
  119. var geometryId = (<BABYLON.Mesh>mesh).geometry ? (<BABYLON.Mesh>mesh).geometry.id : null;
  120. return {
  121. uniqueId: mesh.uniqueId,
  122. id: mesh.id,
  123. name: mesh.name,
  124. geometryId: geometryId,
  125. sphereCenter: mesh.getBoundingInfo().boundingSphere.centerWorld.asArray(),
  126. sphereRadius: mesh.getBoundingInfo().boundingSphere.radiusWorld,
  127. boxMinimum: mesh.getBoundingInfo().boundingBox.minimumWorld.asArray(),
  128. boxMaximum: mesh.getBoundingInfo().boundingBox.maximumWorld.asArray(),
  129. worldMatrixFromCache: mesh.worldMatrixFromCache.asArray(),
  130. subMeshes: submeshes,
  131. checkCollisions: mesh.checkCollisions
  132. }
  133. }
  134. public static SerializeGeometry = function (geometry: BABYLON.Geometry): SerializedGeometry {
  135. return {
  136. id: geometry.id,
  137. positions: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind) || []),
  138. normals: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.NormalKind) || []),
  139. indices: new Int32Array(geometry.getIndices() || []),
  140. //uvs: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.UVKind) || [])
  141. }
  142. }
  143. public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void {
  144. if (this._collisionsCallbackArray[collisionIndex]) return;
  145. position.divideToRef(collider.radius, this._scaledPosition);
  146. velocity.divideToRef(collider.radius, this._scaledVelocity);
  147. this._collisionsCallbackArray[collisionIndex] = onNewPosition;
  148. }
  149. public init(scene: Scene): void {
  150. this._scene = scene;
  151. this._scene.registerAfterRender(this._afterRender);
  152. //TODO init worker
  153. }
  154. public destroy(): void {
  155. this._scene.unregisterAfterRender(this._afterRender);
  156. //TODO destroy worker
  157. }
  158. public onMeshAdded(mesh: AbstractMesh) {
  159. mesh.registerAfterWorldMatrixUpdate(this.onMeshUpdated);
  160. this.onMeshUpdated(mesh);
  161. }
  162. public onMeshUpdated = (mesh: AbstractMesh) => {
  163. this._addUpdateMeshesList[mesh.uniqueId] = CollisionCoordinatorWorker.SerializeMesh(mesh);
  164. }
  165. public onMeshRemoved(mesh: AbstractMesh) {
  166. this._toRemoveMeshesArray.push(mesh.uniqueId);
  167. }
  168. public onGeometryAdded(geometry: Geometry) {
  169. //TODO this will break if the user uses his own function. This should be an array on callbacks!
  170. geometry.onGeometryUpdated = this.onGeometryUpdated;
  171. this.onGeometryUpdated(geometry);
  172. }
  173. public onGeometryUpdated = (geometry: Geometry) => {
  174. this._addUpdateGeometriesList[geometry.id] = CollisionCoordinatorWorker.SerializeGeometry(geometry);
  175. }
  176. public onGeometryDeleted(geometry: Geometry) {
  177. this._toRemoveGeometryArray.push(geometry.id);
  178. }
  179. private _afterRender = () => {
  180. var payload: UpdatePayload = {
  181. updatedMeshes: this._addUpdateMeshesList,
  182. updatedGeometries: this._addUpdateGeometriesList,
  183. removedGeometries: this._toRemoveGeometryArray,
  184. removedMeshes: this._toRemoveMeshesArray
  185. };
  186. var message: BabylonMessage = {
  187. payload: payload,
  188. taskType: WorkerTaskType.UPDATE
  189. }
  190. var serializable = [];
  191. for (var id in payload.updatedGeometries) {
  192. if (payload.updatedGeometries.hasOwnProperty(id)) {
  193. //prepare transferables
  194. serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].indices.buffer);
  195. serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].normals.buffer);
  196. serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].positions.buffer);
  197. }
  198. }
  199. //this variable is here only in case the update takes longer than a frame!
  200. this._runningUpdated++;
  201. this._worker.postMessage(message, serializable);
  202. this._addUpdateMeshesList = {};
  203. this._addUpdateGeometriesList = {};
  204. this._toRemoveGeometryArray = [];
  205. this._toRemoveMeshesArray = [];
  206. }
  207. private _onMessageFromWorker = (e: MessageEvent) => {
  208. var returnData = <WorkerReply> e.data;
  209. switch (returnData.taskType) {
  210. case WorkerTaskType.INIT:
  211. //TODO is init required after worker is done initializing?
  212. this._init = true;
  213. break;
  214. case WorkerTaskType.UPDATE:
  215. this._runningUpdated--;
  216. break;
  217. case WorkerTaskType.COLLIDE:
  218. this._runningCollisionTask = false;
  219. var returnPayload: CollisionReplyPayload = returnData.payload;
  220. if (!this._collisionsCallbackArray[returnPayload.collisionId]) return;
  221. this._collisionsCallbackArray[returnPayload.collisionId](returnPayload.collisionId, Vector3.FromArray(returnPayload.newPosition), this._scene.getMeshByUniqueID(returnPayload.collidedMeshUniqueId));
  222. //cleanup
  223. this._collisionsCallbackArray[returnPayload.collisionId] = undefined;
  224. break;
  225. }
  226. }
  227. }
  228. export class CollisionCoordinatorLegacy implements ICollisionCoordinator {
  229. private _scene: Scene;
  230. private _scaledPosition = Vector3.Zero();
  231. private _scaledVelocity = Vector3.Zero();
  232. private _finalPosition = Vector3.Zero();
  233. public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void {
  234. position.divideToRef(collider.radius, this._scaledPosition);
  235. velocity.divideToRef(collider.radius, this._scaledVelocity);
  236. collider.retry = 0;
  237. collider.initialVelocity = this._scaledVelocity;
  238. collider.initialPosition = this._scaledPosition;
  239. this._collideWithWorld(this._scaledPosition, this._scaledVelocity, collider, maximumRetry, this._finalPosition, excludedMesh);
  240. this._finalPosition.multiplyInPlace(collider.radius);
  241. //run the callback
  242. onNewPosition(collisionIndex, this._finalPosition, collider.collidedMesh);
  243. }
  244. public init(scene: Scene): void {
  245. this._scene = scene;
  246. }
  247. public destroy(): void {
  248. //Legacy need no destruction method.
  249. }
  250. //No update in legacy mode
  251. public onMeshAdded(mesh: AbstractMesh) { }
  252. public onMeshUpdated(mesh: AbstractMesh) { }
  253. public onMeshRemoved(mesh: AbstractMesh) { }
  254. public onGeometryAdded(geometry: Geometry) { }
  255. public onGeometryUpdated(geometry: Geometry) { }
  256. public onGeometryDeleted(geometry: Geometry) { }
  257. private _collideWithWorld(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: AbstractMesh = null): void {
  258. var closeDistance = Engine.CollisionsEpsilon * 10.0;
  259. if (collider.retry >= maximumRetry) {
  260. finalPosition.copyFrom(position);
  261. return;
  262. }
  263. collider._initialize(position, velocity, closeDistance);
  264. // Check all meshes
  265. for (var index = 0; index < this._scene.meshes.length; index++) {
  266. var mesh = this._scene.meshes[index];
  267. if (mesh.isEnabled() && mesh.checkCollisions && mesh.subMeshes && mesh !== excludedMesh) {
  268. mesh._checkCollision(collider);
  269. }
  270. }
  271. if (!collider.collisionFound) {
  272. position.addToRef(velocity, finalPosition);
  273. return;
  274. }
  275. if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
  276. collider._getResponse(position, velocity);
  277. }
  278. if (velocity.length() <= closeDistance) {
  279. finalPosition.copyFrom(position);
  280. return;
  281. }
  282. collider.retry++;
  283. this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
  284. }
  285. }
  286. }