babylon.boneLookController.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. module BABYLON {
  2. export class BoneLookController {
  3. private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
  4. private static _tmpQuat = Quaternion.Identity();
  5. private static _tmpMats: Matrix[] = [Matrix.Identity(), Matrix.Identity(), Matrix.Identity(), Matrix.Identity(), Matrix.Identity()];
  6. /**
  7. * The target Vector3 that the bone will look at.
  8. */
  9. public target: Vector3;
  10. /**
  11. * The mesh that the bone is attached to.
  12. */
  13. public mesh: AbstractMesh;
  14. /**
  15. * The bone that will be looking to the target.
  16. */
  17. public bone: Bone;
  18. /**
  19. * The up axis of the coordinate system that is used when the bone is rotated.
  20. */
  21. public upAxis: Vector3 = Vector3.Up();
  22. /**
  23. * The space that the up axis is in - BABYLON.Space.BONE, BABYLON.Space.LOCAL (default), or BABYLON.Space.WORLD.
  24. */
  25. public upAxisSpace: Space = Space.LOCAL;
  26. /**
  27. * Used to make an adjustment to the yaw of the bone.
  28. */
  29. public adjustYaw = 0;
  30. /**
  31. * Used to make an adjustment to the pitch of the bone.
  32. */
  33. public adjustPitch = 0;
  34. /**
  35. * Used to make an adjustment to the roll of the bone.
  36. */
  37. public adjustRoll = 0;
  38. /**
  39. * The amount to slerp (spherical linear interpolation) to the target. Set this to a value between 0 and 1 (a value of 1 disables slerp).
  40. */
  41. public slerpAmount = 1;
  42. private _minYaw:number;
  43. private _maxYaw:number;
  44. private _minPitch:number;
  45. private _maxPitch:number;
  46. private _minYawSin:number;
  47. private _minYawCos:number;
  48. private _maxYawSin:number;
  49. private _maxYawCos:number;
  50. private _midYawConstraint:number;
  51. private _minPitchTan:number;
  52. private _maxPitchTan:number;
  53. private _boneQuat:Quaternion = Quaternion.Identity();
  54. private _slerping = false;
  55. private _transformYawPitch:Matrix;
  56. private _transformYawPitchInv:Matrix;
  57. private _firstFrameSkipped = false;
  58. private _yawRange:number;
  59. private _fowardAxis: Vector3 = Vector3.Forward();
  60. /**
  61. * Get/set the minimum yaw angle that the bone can look to.
  62. */
  63. get minYaw():number{
  64. return this._minYaw;
  65. }
  66. set minYaw(value:number){
  67. this._minYaw = value;
  68. this._minYawSin = Math.sin(value);
  69. this._minYawCos = Math.cos(value);
  70. if(this._maxYaw != null){
  71. this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw)*.5 + this._minYaw;
  72. this._yawRange = this._maxYaw - this._minYaw;
  73. }
  74. }
  75. /**
  76. * Get/set the maximum yaw angle that the bone can look to.
  77. */
  78. get maxYaw():number{
  79. return this._maxYaw;
  80. }
  81. set maxYaw(value:number){
  82. this._maxYaw = value;
  83. this._maxYawSin = Math.sin(value);
  84. this._maxYawCos = Math.cos(value);
  85. if(this._minYaw != null){
  86. this._midYawConstraint = this._getAngleDiff(this._minYaw, this._maxYaw)*.5 + this._minYaw;
  87. this._yawRange = this._maxYaw - this._minYaw;
  88. }
  89. }
  90. /**
  91. * Get/set the minimum pitch angle that the bone can look to.
  92. */
  93. get minPitch():number{
  94. return this._minPitch;
  95. }
  96. set minPitch(value:number){
  97. this._minPitch = value;
  98. this._minPitchTan = Math.tan(value);
  99. }
  100. /**
  101. * Get/set the maximum pitch angle that the bone can look to.
  102. */
  103. get maxPitch():number{
  104. return this._maxPitch;
  105. }
  106. set maxPitch(value:number){
  107. this._maxPitch = value;
  108. this._maxPitchTan = Math.tan(value);
  109. }
  110. /**
  111. * Create a BoneLookController
  112. * @param mesh the mesh that the bone belongs to
  113. * @param bone the bone that will be looking to the target
  114. * @param target the target Vector3 to look at
  115. * @param settings optional settings:
  116. * - maxYaw: the maximum angle the bone will yaw to
  117. * - minYaw: the minimum angle the bone will yaw to
  118. * - maxPitch: the maximum angle the bone will pitch to
  119. * - minPitch: the minimum angle the bone will yaw to
  120. * - slerpAmount: set the between 0 and 1 to make the bone slerp to the target.
  121. * - upAxis: the up axis of the coordinate system
  122. * - upAxisSpace: the space that the up axis is in - BABYLON.Space.BONE, BABYLON.Space.LOCAL (default), or BABYLON.Space.WORLD.
  123. * - yawAxis: set yawAxis if the bone does not yaw on the y axis
  124. * - pitchAxis: set pitchAxis if the bone does not pitch on the x axis
  125. * - adjustYaw: used to make an adjustment to the yaw of the bone
  126. * - adjustPitch: used to make an adjustment to the pitch of the bone
  127. * - adjustRoll: used to make an adjustment to the roll of the bone
  128. **/
  129. constructor(mesh: AbstractMesh,
  130. bone: Bone,
  131. target: Vector3,
  132. options?: {
  133. adjustYaw?: number,
  134. adjustPitch?: number,
  135. adjustRoll?: number,
  136. slerpAmount?: number,
  137. maxYaw?:number,
  138. minYaw?:number,
  139. maxPitch?:number,
  140. minPitch?:number,
  141. upAxis?:Vector3,
  142. upAxisSpace?:Space,
  143. yawAxis?:Vector3,
  144. pitchAxis?:Vector3
  145. }){
  146. this.mesh = mesh;
  147. this.bone = bone;
  148. this.target = target;
  149. if(options){
  150. if(options.adjustYaw){
  151. this.adjustYaw = options.adjustYaw;
  152. }
  153. if(options.adjustPitch){
  154. this.adjustPitch = options.adjustPitch;
  155. }
  156. if(options.adjustRoll){
  157. this.adjustRoll = options.adjustRoll;
  158. }
  159. if(options.maxYaw != null){
  160. this.maxYaw = options.maxYaw;
  161. }else{
  162. this.maxYaw = Math.PI;
  163. }
  164. if(options.minYaw != null){
  165. this.minYaw = options.minYaw;
  166. }else{
  167. this.minYaw = -Math.PI;
  168. }
  169. if(options.maxPitch != null){
  170. this.maxPitch = options.maxPitch;
  171. }else{
  172. this.maxPitch = Math.PI;
  173. }
  174. if(options.minPitch != null){
  175. this.minPitch = options.minPitch;
  176. }else{
  177. this.minPitch = -Math.PI;
  178. }
  179. if(options.slerpAmount != null){
  180. this.slerpAmount = options.slerpAmount;
  181. }
  182. if(options.upAxis != null){
  183. this.upAxis = options.upAxis;
  184. }
  185. if(options.upAxisSpace != null){
  186. this.upAxisSpace = options.upAxisSpace;
  187. }
  188. if(options.yawAxis != null || options.pitchAxis != null){
  189. var newYawAxis = Axis.Y;
  190. var newPitchAxis = Axis.X;
  191. if(options.yawAxis != null){
  192. newYawAxis = options.yawAxis.clone();
  193. newYawAxis.normalize();
  194. }
  195. if(options.pitchAxis != null){
  196. newPitchAxis = options.pitchAxis.clone();
  197. newPitchAxis.normalize();
  198. }
  199. var newRollAxis = Vector3.Cross(newPitchAxis, newYawAxis);
  200. this._transformYawPitch = Matrix.Identity();
  201. Matrix.FromXYZAxesToRef(newPitchAxis, newYawAxis, newRollAxis, this._transformYawPitch);
  202. this._transformYawPitchInv = this._transformYawPitch.clone();
  203. this._transformYawPitch.invert();
  204. }
  205. }
  206. if(!bone.getParent() && this.upAxisSpace == Space.BONE){
  207. this.upAxisSpace = Space.LOCAL;
  208. }
  209. }
  210. /**
  211. * Update the bone to look at the target. This should be called before the scene is rendered (use scene.registerBeforeRender()).
  212. */
  213. public update (): void {
  214. //skip the first frame when slerping so that the mesh rotation is correct
  215. if(this.slerpAmount < 1 && !this._firstFrameSkipped){
  216. this._firstFrameSkipped = true;
  217. return;
  218. }
  219. var bone = this.bone;
  220. var bonePos = BoneLookController._tmpVecs[0];
  221. bone.getAbsolutePositionToRef(this.mesh, bonePos);
  222. var target = this.target;
  223. var _tmpMat1 = BoneLookController._tmpMats[0];
  224. var _tmpMat2 = BoneLookController._tmpMats[1];
  225. var mesh = this.mesh;
  226. var parentBone = bone.getParent();
  227. var upAxis = BoneLookController._tmpVecs[1];
  228. upAxis.copyFrom(this.upAxis);
  229. if(this.upAxisSpace == Space.BONE){
  230. if (this._transformYawPitch){
  231. Vector3.TransformCoordinatesToRef(upAxis, this._transformYawPitchInv, upAxis);
  232. }
  233. parentBone.getDirectionToRef(upAxis, this.mesh, upAxis);
  234. }else if(this.upAxisSpace == Space.LOCAL){
  235. mesh.getDirectionToRef(upAxis, upAxis);
  236. if(mesh.scaling.x != 1 || mesh.scaling.y != 1 || mesh.scaling.z != 1){
  237. upAxis.normalize();
  238. }
  239. }
  240. var checkYaw = false;
  241. var checkPitch = false;
  242. if(this._maxYaw != Math.PI || this._minYaw != -Math.PI){
  243. checkYaw = true;
  244. }
  245. if(this._maxPitch != Math.PI || this._minPitch != -Math.PI){
  246. checkPitch = true;
  247. }
  248. if(checkYaw || checkPitch){
  249. var _tmpMat3 = BoneLookController._tmpMats[2];
  250. var _tmpMat3Inv = BoneLookController._tmpMats[3];
  251. if(this.upAxisSpace == Space.BONE && upAxis.y == 1){
  252. parentBone.getRotationMatrixToRef(Space.WORLD, this.mesh, _tmpMat3);
  253. }else if(this.upAxisSpace == Space.LOCAL && upAxis.y == 1 && !parentBone){
  254. _tmpMat3.copyFrom(mesh.getWorldMatrix());
  255. }else{
  256. var forwardAxis = BoneLookController._tmpVecs[2];
  257. forwardAxis.copyFrom(this._fowardAxis);
  258. if (this._transformYawPitch) {
  259. Vector3.TransformCoordinatesToRef(forwardAxis, this._transformYawPitchInv, forwardAxis);
  260. }
  261. if(parentBone){
  262. parentBone.getDirectionToRef(forwardAxis, this.mesh, forwardAxis);
  263. }else{
  264. mesh.getDirectionToRef(forwardAxis, forwardAxis);
  265. }
  266. var rightAxis = Vector3.Cross(upAxis, forwardAxis);
  267. rightAxis.normalize();
  268. var forwardAxis = Vector3.Cross(rightAxis, upAxis);
  269. Matrix.FromXYZAxesToRef(rightAxis, upAxis, forwardAxis, _tmpMat3);
  270. }
  271. _tmpMat3.invertToRef(_tmpMat3Inv);
  272. var xzlen:number;
  273. if(checkPitch){
  274. var localTarget = BoneLookController._tmpVecs[3];
  275. Vector3.TransformCoordinatesToRef(target.subtract(bonePos), _tmpMat3Inv, localTarget);
  276. var xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
  277. var pitch = Math.atan2(localTarget.y, xzlen);
  278. var newPitch = pitch;
  279. if(pitch > this._maxPitch){
  280. localTarget.y = this._maxPitchTan*xzlen;
  281. newPitch = this._maxPitch;
  282. }else if(pitch < this._minPitch){
  283. localTarget.y = this._minPitchTan*xzlen;
  284. newPitch = this._minPitch;
  285. }
  286. if(pitch != newPitch){
  287. Vector3.TransformCoordinatesToRef(localTarget, _tmpMat3, localTarget);
  288. localTarget.addInPlace(bonePos);
  289. target = localTarget;
  290. }
  291. }
  292. if(checkYaw){
  293. var localTarget = BoneLookController._tmpVecs[4];
  294. Vector3.TransformCoordinatesToRef(target.subtract(bonePos), _tmpMat3Inv, localTarget);
  295. var yaw = Math.atan2(localTarget.x, localTarget.z);
  296. var newYaw = yaw;
  297. if(yaw > this._maxYaw || yaw < this._minYaw){
  298. if(xzlen == null){
  299. xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
  300. }
  301. if(this._yawRange > Math.PI){
  302. if (this._isAngleBetween(yaw, this._maxYaw, this._midYawConstraint)) {
  303. localTarget.z = this._maxYawCos * xzlen;
  304. localTarget.x = this._maxYawSin * xzlen;
  305. newYaw = this._maxYaw;
  306. }else if (this._isAngleBetween(yaw, this._midYawConstraint, this._minYaw)) {
  307. localTarget.z = this._minYawCos * xzlen;
  308. localTarget.x = this._minYawSin * xzlen;
  309. newYaw = this._minYaw;
  310. }
  311. }else{
  312. if (yaw > this._maxYaw) {
  313. localTarget.z = this._maxYawCos * xzlen;
  314. localTarget.x = this._maxYawSin * xzlen;
  315. newYaw = this._maxYaw;
  316. }else if (yaw < this._minYaw) {
  317. localTarget.z = this._minYawCos * xzlen;
  318. localTarget.x = this._minYawSin * xzlen;
  319. newYaw = this._minYaw;
  320. }
  321. }
  322. }
  323. if(this._slerping && this._yawRange > Math.PI){
  324. //are we going to be crossing into the min/max region
  325. var _tmpVec8 = BoneLookController._tmpVecs[8];
  326. _tmpVec8.copyFrom(Axis.Z);
  327. if (this._transformYawPitch) {
  328. Vector3.TransformCoordinatesToRef(_tmpVec8, this._transformYawPitchInv, _tmpVec8);
  329. }
  330. var boneRotMat = BABYLON.BoneLookController._tmpMats[4];
  331. this._boneQuat.toRotationMatrix(boneRotMat);
  332. this.mesh.getWorldMatrix().multiplyToRef(boneRotMat, boneRotMat);
  333. BABYLON.Vector3.TransformCoordinatesToRef(_tmpVec8, boneRotMat, _tmpVec8);
  334. BABYLON.Vector3.TransformCoordinatesToRef(_tmpVec8, _tmpMat3Inv, _tmpVec8);
  335. var boneYaw = Math.atan2(_tmpVec8.x, _tmpVec8.z);
  336. var ang1 = this._getAngleBetween(boneYaw, yaw);
  337. var ang2 = this._getAngleBetween(boneYaw, this._midYawConstraint);
  338. if(ang1 > ang2){
  339. if (xzlen == null) {
  340. xzlen = Math.sqrt(localTarget.x * localTarget.x + localTarget.z * localTarget.z);
  341. }
  342. var ang3 = this._getAngleBetween(boneYaw, this._maxYaw);
  343. var ang4 = this._getAngleBetween(boneYaw, this._minYaw);
  344. if(ang4 < ang3){
  345. newYaw = boneYaw+Math.PI*.75;
  346. localTarget.z = Math.cos(newYaw) * xzlen;
  347. localTarget.x = Math.sin(newYaw) * xzlen;
  348. }else{
  349. newYaw = boneYaw-Math.PI*.75;
  350. localTarget.z = Math.cos(newYaw) * xzlen;
  351. localTarget.x = Math.sin(newYaw) * xzlen;
  352. }
  353. }
  354. }
  355. if(yaw != newYaw){
  356. Vector3.TransformCoordinatesToRef(localTarget, _tmpMat3, localTarget);
  357. localTarget.addInPlace(bonePos);
  358. target = localTarget;
  359. }
  360. }
  361. }
  362. var zaxis = BoneLookController._tmpVecs[5];
  363. var xaxis = BoneLookController._tmpVecs[6];
  364. var yaxis = BoneLookController._tmpVecs[7];
  365. var _tmpQuat = BoneLookController._tmpQuat;
  366. target.subtractToRef(bonePos, zaxis);
  367. zaxis.normalize();
  368. Vector3.CrossToRef(upAxis, zaxis, xaxis);
  369. xaxis.normalize();
  370. Vector3.CrossToRef(zaxis, xaxis, yaxis);
  371. yaxis.normalize();
  372. Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, _tmpMat1);
  373. if(xaxis.x === 0 && xaxis.y === 0 && xaxis.z === 0){
  374. return;
  375. }
  376. if(yaxis.x === 0 && yaxis.y === 0 && yaxis.z === 0){
  377. return;
  378. }
  379. if(zaxis.x === 0 && zaxis.y === 0 && zaxis.z === 0){
  380. return;
  381. }
  382. if (this.adjustYaw || this.adjustPitch || this.adjustRoll) {
  383. Matrix.RotationYawPitchRollToRef(this.adjustYaw, this.adjustPitch, this.adjustRoll, _tmpMat2);
  384. _tmpMat2.multiplyToRef(_tmpMat1, _tmpMat1);
  385. }
  386. if (this.slerpAmount < 1) {
  387. if (!this._slerping) {
  388. this.bone.getRotationQuaternionToRef(Space.WORLD, this.mesh, this._boneQuat);
  389. }
  390. if(this._transformYawPitch){
  391. this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
  392. }
  393. Quaternion.FromRotationMatrixToRef(_tmpMat1, _tmpQuat);
  394. Quaternion.SlerpToRef(this._boneQuat, _tmpQuat, this.slerpAmount, this._boneQuat);
  395. this.bone.setRotationQuaternion(this._boneQuat, Space.WORLD, this.mesh);
  396. this._slerping = true;
  397. } else {
  398. if(this._transformYawPitch){
  399. this._transformYawPitch.multiplyToRef(_tmpMat1, _tmpMat1);
  400. }
  401. this.bone.setRotationMatrix(_tmpMat1, Space.WORLD, this.mesh);
  402. this._slerping = false;
  403. }
  404. }
  405. private _getAngleDiff(ang1, ang2):number {
  406. var angDiff = ang2 - ang1;
  407. angDiff %= Math.PI*2;
  408. if(angDiff > Math.PI){
  409. angDiff -= Math.PI*2;
  410. }else if (angDiff < -Math.PI){
  411. angDiff += Math.PI*2;
  412. }
  413. return angDiff;
  414. }
  415. private _getAngleBetween(ang1, ang2):number {
  416. ang1 %= (2 * Math.PI);
  417. ang1 = (ang1 < 0) ? ang1 + (2 * Math.PI) : ang1;
  418. ang2 %= (2 * Math.PI);
  419. ang2 = (ang2 < 0) ? ang2 + (2 * Math.PI) : ang2;
  420. var ab = 0;
  421. if(ang1 < ang2){
  422. ab = ang2 - ang1;
  423. }else{
  424. ab = ang1 - ang2;
  425. }
  426. if(ab > Math.PI){
  427. ab = Math.PI*2 - ab;
  428. }
  429. return ab;
  430. }
  431. private _isAngleBetween(ang, ang1, ang2):boolean {
  432. ang %= (2 * Math.PI);
  433. ang = (ang < 0) ? ang + (2 * Math.PI) : ang;
  434. ang1 %= (2 * Math.PI);
  435. ang1 = (ang1 < 0) ? ang1 + (2 * Math.PI) : ang1;
  436. ang2 %= (2 * Math.PI);
  437. ang2 = (ang2 < 0) ? ang2 + (2 * Math.PI) : ang2;
  438. if(ang1 < ang2){
  439. if(ang > ang1 && ang < ang2){
  440. return true;
  441. }
  442. }else{
  443. if(ang > ang2 && ang < ang1){
  444. return true;
  445. }
  446. }
  447. return false;
  448. }
  449. }
  450. }