abstractMesh.ts 87 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059
  1. import { Tools } from "../Misc/tools";
  2. import { Observer, Observable } from "../Misc/observable";
  3. import { Nullable, FloatArray, IndicesArray, DeepImmutable } from "../types";
  4. import { Camera } from "../Cameras/camera";
  5. import { Scene, IDisposable } from "../scene";
  6. import { Quaternion, Matrix, Vector3, Color3, Color4, Plane, Tmp, Epsilon, Axis, Vector2 } from "../Maths/math";
  7. import { Engine } from "../Engines/engine";
  8. import { Node } from "../node";
  9. import { VertexBuffer } from "../Meshes/buffer";
  10. import { VertexData, IGetSetVerticesData } from "../Meshes/mesh.vertexData";
  11. import { TransformNode } from "../Meshes/transformNode";
  12. import { SubMesh } from "../Meshes/subMesh";
  13. import { PickingInfo, IntersectionInfo } from "../Collisions/pickingInfo";
  14. import { Collider } from "../Collisions/collider";
  15. import { Ray } from "../Culling/ray";
  16. import { ICullable, BoundingInfo } from "../Culling/boundingInfo";
  17. import { Material } from "../Materials/material";
  18. import { MaterialDefines } from "../Materials/materialDefines";
  19. import { Light } from "../Lights/light";
  20. import { ActionManager } from "../Actions/actionManager";
  21. import { Skeleton } from "../Bones/skeleton";
  22. import { IEdgesRenderer } from "../Rendering/edgesRenderer";
  23. import { SolidParticle } from "../Particles/solidParticle";
  24. import { Constants } from "../Engines/constants";
  25. /** @hidden */
  26. class _FacetDataStorage {
  27. // facetData private properties
  28. public facetPositions: Vector3[]; // facet local positions
  29. public facetNormals: Vector3[]; // facet local normals
  30. public facetPartitioning: number[][]; // partitioning array of facet index arrays
  31. public facetNb: number = 0; // facet number
  32. public partitioningSubdivisions: number = 10; // number of subdivisions per axis in the partioning space
  33. public partitioningBBoxRatio: number = 1.01; // the partioning array space is by default 1% bigger than the bounding box
  34. public facetDataEnabled: boolean = false; // is the facet data feature enabled on this mesh ?
  35. public facetParameters: any = {}; // keep a reference to the object parameters to avoid memory re-allocation
  36. public bbSize: Vector3 = Vector3.Zero(); // bbox size approximated for facet data
  37. public subDiv = { // actual number of subdivisions per axis for ComputeNormals()
  38. max: 1,
  39. X: 1,
  40. Y: 1,
  41. Z: 1
  42. };
  43. public facetDepthSort: boolean = false; // is the facet depth sort to be computed
  44. public facetDepthSortEnabled: boolean = false; // is the facet depth sort initialized
  45. public depthSortedIndices: IndicesArray; // copy of the indices array to store them once sorted
  46. public depthSortedFacets: { ind: number, sqDistance: number }[]; // array of depth sorted facets
  47. public facetDepthSortFunction: (f1: { ind: number, sqDistance: number }, f2: { ind: number, sqDistance: number }) => number; // facet depth sort function
  48. public facetDepthSortFrom: Vector3; // location where to depth sort from
  49. public facetDepthSortOrigin: Vector3; // same as facetDepthSortFrom but expressed in the mesh local space
  50. public invertedMatrix: Matrix; // Inverted world matrix.
  51. }
  52. /**
  53. * Class used to store all common mesh properties
  54. */
  55. export class AbstractMesh extends TransformNode implements IDisposable, ICullable, IGetSetVerticesData {
  56. /** No occlusion */
  57. public static OCCLUSION_TYPE_NONE = 0;
  58. /** Occlusion set to optimisitic */
  59. public static OCCLUSION_TYPE_OPTIMISTIC = 1;
  60. /** Occlusion set to strict */
  61. public static OCCLUSION_TYPE_STRICT = 2;
  62. /** Use an accurante occlusion algorithm */
  63. public static OCCLUSION_ALGORITHM_TYPE_ACCURATE = 0;
  64. /** Use a conservative occlusion algorithm */
  65. public static OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE = 1;
  66. /** Default culling strategy : this is an exclusion test and it's the more accurate.
  67. * Test order :
  68. * Is the bounding sphere outside the frustum ?
  69. * If not, are the bounding box vertices outside the frustum ?
  70. * It not, then the cullable object is in the frustum.
  71. */
  72. public static readonly CULLINGSTRATEGY_STANDARD = Constants.MESHES_CULLINGSTRATEGY_STANDARD;
  73. /** Culling strategy : Bounding Sphere Only.
  74. * This is an exclusion test. It's faster than the standard strategy because the bounding box is not tested.
  75. * It's also less accurate than the standard because some not visible objects can still be selected.
  76. * Test : is the bounding sphere outside the frustum ?
  77. * If not, then the cullable object is in the frustum.
  78. */
  79. public static readonly CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY = Constants.MESHES_CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY;
  80. /** Culling strategy : Optimistic Inclusion.
  81. * This in an inclusion test first, then the standard exclusion test.
  82. * This can be faster when a cullable object is expected to be almost always in the camera frustum.
  83. * This could also be a little slower than the standard test when the tested object center is not the frustum but one of its bounding box vertex is still inside.
  84. * Anyway, it's as accurate as the standard strategy.
  85. * Test :
  86. * Is the cullable object bounding sphere center in the frustum ?
  87. * If not, apply the default culling strategy.
  88. */
  89. public static readonly CULLINGSTRATEGY_OPTIMISTIC_INCLUSION = Constants.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION;
  90. /** Culling strategy : Optimistic Inclusion then Bounding Sphere Only.
  91. * This in an inclusion test first, then the bounding sphere only exclusion test.
  92. * This can be the fastest test when a cullable object is expected to be almost always in the camera frustum.
  93. * This could also be a little slower than the BoundingSphereOnly strategy when the tested object center is not in the frustum but its bounding sphere still intersects it.
  94. * It's less accurate than the standard strategy and as accurate as the BoundingSphereOnly strategy.
  95. * Test :
  96. * Is the cullable object bounding sphere center in the frustum ?
  97. * If not, apply the Bounding Sphere Only strategy. No Bounding Box is tested here.
  98. */
  99. public static readonly CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY = Constants.MESHES_CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY;
  100. /**
  101. * No billboard
  102. */
  103. public static get BILLBOARDMODE_NONE(): number {
  104. return TransformNode.BILLBOARDMODE_NONE;
  105. }
  106. /** Billboard on X axis */
  107. public static get BILLBOARDMODE_X(): number {
  108. return TransformNode.BILLBOARDMODE_X;
  109. }
  110. /** Billboard on Y axis */
  111. public static get BILLBOARDMODE_Y(): number {
  112. return TransformNode.BILLBOARDMODE_Y;
  113. }
  114. /** Billboard on Z axis */
  115. public static get BILLBOARDMODE_Z(): number {
  116. return TransformNode.BILLBOARDMODE_Z;
  117. }
  118. /** Billboard on all axes */
  119. public static get BILLBOARDMODE_ALL(): number {
  120. return TransformNode.BILLBOARDMODE_ALL;
  121. }
  122. private _facetData = new _FacetDataStorage();
  123. /**
  124. * The culling strategy to use to check whether the mesh must be rendered or not.
  125. * This value can be changed at any time and will be used on the next render mesh selection.
  126. * The possible values are :
  127. * - AbstractMesh.CULLINGSTRATEGY_STANDARD
  128. * - AbstractMesh.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY
  129. * - AbstractMesh.CULLINGSTRATEGY_OPTIMISTIC_INCLUSION
  130. * - AbstractMesh.CULLINGSTRATEGY_OPTIMISTIC_INCLUSION_THEN_BSPHERE_ONLY
  131. * Please read each static variable documentation to get details about the culling process.
  132. * */
  133. public cullingStrategy = AbstractMesh.CULLINGSTRATEGY_STANDARD;
  134. /**
  135. * Gets the number of facets in the mesh
  136. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#what-is-a-mesh-facet
  137. */
  138. public get facetNb(): number {
  139. return this._facetData.facetNb;
  140. }
  141. /**
  142. * Gets or set the number (integer) of subdivisions per axis in the partioning space
  143. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#tweaking-the-partitioning
  144. */
  145. public get partitioningSubdivisions(): number {
  146. return this._facetData.partitioningSubdivisions;
  147. }
  148. public set partitioningSubdivisions(nb: number) {
  149. this._facetData.partitioningSubdivisions = nb;
  150. }
  151. /**
  152. * The ratio (float) to apply to the bouding box size to set to the partioning space.
  153. * Ex : 1.01 (default) the partioning space is 1% bigger than the bounding box
  154. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#tweaking-the-partitioning
  155. */
  156. public get partitioningBBoxRatio(): number {
  157. return this._facetData.partitioningBBoxRatio;
  158. }
  159. public set partitioningBBoxRatio(ratio: number) {
  160. this._facetData.partitioningBBoxRatio = ratio;
  161. }
  162. /**
  163. * Gets or sets a boolean indicating that the facets must be depth sorted on next call to `updateFacetData()`.
  164. * Works only for updatable meshes.
  165. * Doesn't work with multi-materials
  166. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#facet-depth-sort
  167. */
  168. public get mustDepthSortFacets(): boolean {
  169. return this._facetData.facetDepthSort;
  170. }
  171. public set mustDepthSortFacets(sort: boolean) {
  172. this._facetData.facetDepthSort = sort;
  173. }
  174. /**
  175. * The location (Vector3) where the facet depth sort must be computed from.
  176. * By default, the active camera position.
  177. * Used only when facet depth sort is enabled
  178. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#facet-depth-sort
  179. */
  180. public get facetDepthSortFrom(): Vector3 {
  181. return this._facetData.facetDepthSortFrom;
  182. }
  183. public set facetDepthSortFrom(location: Vector3) {
  184. this._facetData.facetDepthSortFrom = location;
  185. }
  186. /**
  187. * gets a boolean indicating if facetData is enabled
  188. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata#what-is-a-mesh-facet
  189. */
  190. public get isFacetDataEnabled(): boolean {
  191. return this._facetData.facetDataEnabled;
  192. }
  193. /** @hidden */
  194. public _updateNonUniformScalingState(value: boolean): boolean {
  195. if (!super._updateNonUniformScalingState(value)) {
  196. return false;
  197. }
  198. this._markSubMeshesAsMiscDirty();
  199. return true;
  200. }
  201. // Events
  202. /**
  203. * An event triggered when this mesh collides with another one
  204. */
  205. public onCollideObservable = new Observable<AbstractMesh>();
  206. private _onCollideObserver: Nullable<Observer<AbstractMesh>>;
  207. /** Set a function to call when this mesh collides with another one */
  208. public set onCollide(callback: () => void) {
  209. if (this._onCollideObserver) {
  210. this.onCollideObservable.remove(this._onCollideObserver);
  211. }
  212. this._onCollideObserver = this.onCollideObservable.add(callback);
  213. }
  214. /**
  215. * An event triggered when the collision's position changes
  216. */
  217. public onCollisionPositionChangeObservable = new Observable<Vector3>();
  218. private _onCollisionPositionChangeObserver: Nullable<Observer<Vector3>>;
  219. /** Set a function to call when the collision's position changes */
  220. public set onCollisionPositionChange(callback: () => void) {
  221. if (this._onCollisionPositionChangeObserver) {
  222. this.onCollisionPositionChangeObservable.remove(this._onCollisionPositionChangeObserver);
  223. }
  224. this._onCollisionPositionChangeObserver = this.onCollisionPositionChangeObservable.add(callback);
  225. }
  226. /**
  227. * An event triggered when material is changed
  228. */
  229. public onMaterialChangedObservable = new Observable<AbstractMesh>();
  230. // Properties
  231. /**
  232. * Gets or sets the orientation for POV movement & rotation
  233. */
  234. public definedFacingForward = true;
  235. /** @hidden */
  236. public _occlusionQuery: Nullable<WebGLQuery>;
  237. private _visibility = 1.0;
  238. /**
  239. * Gets or sets mesh visibility between 0 and 1 (default is 1)
  240. */
  241. public get visibility(): number {
  242. return this._visibility;
  243. }
  244. /**
  245. * Gets or sets mesh visibility between 0 and 1 (default is 1)
  246. */
  247. public set visibility(value: number) {
  248. if (this._visibility === value) {
  249. return;
  250. }
  251. this._visibility = value;
  252. this._markSubMeshesAsMiscDirty();
  253. }
  254. /** Gets or sets the alpha index used to sort transparent meshes
  255. * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#alpha-index
  256. */
  257. public alphaIndex = Number.MAX_VALUE;
  258. /**
  259. * Gets or sets a boolean indicating if the mesh is visible (renderable). Default is true
  260. */
  261. public isVisible = true;
  262. /**
  263. * Gets or sets a boolean indicating if the mesh can be picked (by scene.pick for instance or through actions). Default is true
  264. */
  265. public isPickable = true;
  266. /** Gets or sets a boolean indicating that bounding boxes of subMeshes must be rendered as well (false by default) */
  267. public showSubMeshesBoundingBox = false;
  268. /** Gets or sets a boolean indicating if the mesh must be considered as a ray blocker for lens flares (false by default)
  269. * @see http://doc.babylonjs.com/how_to/how_to_use_lens_flares
  270. */
  271. public isBlocker = false;
  272. /**
  273. * Gets or sets a boolean indicating that pointer move events must be supported on this mesh (false by default)
  274. */
  275. public enablePointerMoveEvents = false;
  276. /**
  277. * Specifies the rendering group id for this mesh (0 by default)
  278. * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
  279. */
  280. public renderingGroupId = 0;
  281. private _material: Nullable<Material>;
  282. /** Gets or sets current material */
  283. public get material(): Nullable<Material> {
  284. return this._material;
  285. }
  286. public set material(value: Nullable<Material>) {
  287. if (this._material === value) {
  288. return;
  289. }
  290. // remove from material mesh map id needed
  291. if (this._material && this._material.meshMap) {
  292. this._material.meshMap[this.uniqueId] = undefined;
  293. }
  294. this._material = value;
  295. if (value && value.meshMap) {
  296. value.meshMap[this.uniqueId] = this;
  297. }
  298. if (this.onMaterialChangedObservable.hasObservers) {
  299. this.onMaterialChangedObservable.notifyObservers(this);
  300. }
  301. if (!this.subMeshes) {
  302. return;
  303. }
  304. this._unBindEffect();
  305. }
  306. private _receiveShadows = false;
  307. /**
  308. * Gets or sets a boolean indicating that this mesh can receive realtime shadows
  309. * @see http://doc.babylonjs.com/babylon101/shadows
  310. */
  311. public get receiveShadows(): boolean {
  312. return this._receiveShadows;
  313. }
  314. public set receiveShadows(value: boolean) {
  315. if (this._receiveShadows === value) {
  316. return;
  317. }
  318. this._receiveShadows = value;
  319. this._markSubMeshesAsLightDirty();
  320. }
  321. /** Defines color to use when rendering outline */
  322. public outlineColor = Color3.Red();
  323. /** Define width to use when rendering outline */
  324. public outlineWidth = 0.02;
  325. /** Defines color to use when rendering overlay */
  326. public overlayColor = Color3.Red();
  327. /** Defines alpha to use when rendering overlay */
  328. public overlayAlpha = 0.5;
  329. private _hasVertexAlpha = false;
  330. /** Gets or sets a boolean indicating that this mesh contains vertex color data with alpha values */
  331. public get hasVertexAlpha(): boolean {
  332. return this._hasVertexAlpha;
  333. }
  334. public set hasVertexAlpha(value: boolean) {
  335. if (this._hasVertexAlpha === value) {
  336. return;
  337. }
  338. this._hasVertexAlpha = value;
  339. this._markSubMeshesAsAttributesDirty();
  340. this._markSubMeshesAsMiscDirty();
  341. }
  342. private _useVertexColors = true;
  343. /** Gets or sets a boolean indicating that this mesh needs to use vertex color data to render (if this kind of vertex data is available in the geometry) */
  344. public get useVertexColors(): boolean {
  345. return this._useVertexColors;
  346. }
  347. public set useVertexColors(value: boolean) {
  348. if (this._useVertexColors === value) {
  349. return;
  350. }
  351. this._useVertexColors = value;
  352. this._markSubMeshesAsAttributesDirty();
  353. }
  354. private _computeBonesUsingShaders = true;
  355. /**
  356. * Gets or sets a boolean indicating that bone animations must be computed by the CPU (false by default)
  357. */
  358. public get computeBonesUsingShaders(): boolean {
  359. return this._computeBonesUsingShaders;
  360. }
  361. public set computeBonesUsingShaders(value: boolean) {
  362. if (this._computeBonesUsingShaders === value) {
  363. return;
  364. }
  365. this._computeBonesUsingShaders = value;
  366. this._markSubMeshesAsAttributesDirty();
  367. }
  368. private _numBoneInfluencers = 4;
  369. /** Gets or sets the number of allowed bone influences per vertex (4 by default) */
  370. public get numBoneInfluencers(): number {
  371. return this._numBoneInfluencers;
  372. }
  373. public set numBoneInfluencers(value: number) {
  374. if (this._numBoneInfluencers === value) {
  375. return;
  376. }
  377. this._numBoneInfluencers = value;
  378. this._markSubMeshesAsAttributesDirty();
  379. }
  380. private _applyFog = true;
  381. /** Gets or sets a boolean indicating that this mesh will allow fog to be rendered on it (true by default) */
  382. public get applyFog(): boolean {
  383. return this._applyFog;
  384. }
  385. public set applyFog(value: boolean) {
  386. if (this._applyFog === value) {
  387. return;
  388. }
  389. this._applyFog = value;
  390. this._markSubMeshesAsMiscDirty();
  391. }
  392. /** Gets or sets a boolean indicating that internal octree (if available) can be used to boost submeshes selection (true by default) */
  393. public useOctreeForRenderingSelection = true;
  394. /** Gets or sets a boolean indicating that internal octree (if available) can be used to boost submeshes picking (true by default) */
  395. public useOctreeForPicking = true;
  396. /** Gets or sets a boolean indicating that internal octree (if available) can be used to boost submeshes collision (true by default) */
  397. public useOctreeForCollisions = true;
  398. private _layerMask: number = 0x0FFFFFFF;
  399. /**
  400. * Gets or sets the current layer mask (default is 0x0FFFFFFF)
  401. * @see http://doc.babylonjs.com/how_to/layermasks_and_multi-cam_textures
  402. */
  403. public get layerMask(): number {
  404. return this._layerMask;
  405. }
  406. public set layerMask(value: number) {
  407. if (value === this._layerMask) {
  408. return;
  409. }
  410. this._layerMask = value;
  411. this._resyncLightSources();
  412. }
  413. /**
  414. * True if the mesh must be rendered in any case (this will shortcut the frustum clipping phase)
  415. */
  416. public alwaysSelectAsActiveMesh = false;
  417. /**
  418. * Gets or sets the current action manager
  419. * @see http://doc.babylonjs.com/how_to/how_to_use_actions
  420. */
  421. public actionManager: Nullable<ActionManager> = null;
  422. // Collisions
  423. private _checkCollisions = false;
  424. private _collisionMask = -1;
  425. private _collisionGroup = -1;
  426. /**
  427. * Gets or sets the ellipsoid used to impersonate this mesh when using collision engine (default is (0.5, 1, 0.5))
  428. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity
  429. */
  430. public ellipsoid = new Vector3(0.5, 1, 0.5);
  431. /**
  432. * Gets or sets the ellipsoid offset used to impersonate this mesh when using collision engine (default is (0, 0, 0))
  433. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity
  434. */
  435. public ellipsoidOffset = new Vector3(0, 0, 0);
  436. private _collider: Collider;
  437. private _oldPositionForCollisions = new Vector3(0, 0, 0);
  438. private _diffPositionForCollisions = new Vector3(0, 0, 0);
  439. /**
  440. * Gets or sets a collision mask used to mask collisions (default is -1).
  441. * A collision between A and B will happen if A.collisionGroup & b.collisionMask !== 0
  442. */
  443. public get collisionMask(): number {
  444. return this._collisionMask;
  445. }
  446. public set collisionMask(mask: number) {
  447. this._collisionMask = !isNaN(mask) ? mask : -1;
  448. }
  449. /**
  450. * Gets or sets the current collision group mask (-1 by default).
  451. * A collision between A and B will happen if A.collisionGroup & b.collisionMask !== 0
  452. */
  453. public get collisionGroup(): number {
  454. return this._collisionGroup;
  455. }
  456. public set collisionGroup(mask: number) {
  457. this._collisionGroup = !isNaN(mask) ? mask : -1;
  458. }
  459. // Edges
  460. /**
  461. * Defines edge width used when edgesRenderer is enabled
  462. * @see https://www.babylonjs-playground.com/#10OJSG#13
  463. */
  464. public edgesWidth = 1;
  465. /**
  466. * Defines edge color used when edgesRenderer is enabled
  467. * @see https://www.babylonjs-playground.com/#10OJSG#13
  468. */
  469. public edgesColor = new Color4(1, 0, 0, 1);
  470. /** @hidden */
  471. public _edgesRenderer: Nullable<IEdgesRenderer>;
  472. /** @hidden */
  473. public _masterMesh: Nullable<AbstractMesh>;
  474. /** @hidden */
  475. public _boundingInfo: Nullable<BoundingInfo>;
  476. /** @hidden */
  477. public _renderId = 0;
  478. /**
  479. * Gets or sets the list of subMeshes
  480. * @see http://doc.babylonjs.com/how_to/multi_materials
  481. */
  482. public subMeshes: SubMesh[];
  483. /** @hidden */
  484. public _intersectionsInProgress = new Array<AbstractMesh>();
  485. /** @hidden */
  486. public _unIndexed = false;
  487. /** @hidden */
  488. public _lightSources = new Array<Light>();
  489. /** @hidden */
  490. public get _positions(): Nullable<Vector3[]> {
  491. return null;
  492. }
  493. // Loading properties
  494. /** @hidden */
  495. public _waitingActions: any;
  496. /** @hidden */
  497. public _waitingFreezeWorldMatrix: Nullable<boolean>;
  498. // Skeleton
  499. private _skeleton: Nullable<Skeleton>;
  500. /** @hidden */
  501. public _bonesTransformMatrices: Nullable<Float32Array>;
  502. /**
  503. * Gets or sets a skeleton to apply skining transformations
  504. * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons
  505. */
  506. public set skeleton(value: Nullable<Skeleton>) {
  507. if (this._skeleton && this._skeleton.needInitialSkinMatrix) {
  508. this._skeleton._unregisterMeshWithPoseMatrix(this);
  509. }
  510. if (value && value.needInitialSkinMatrix) {
  511. value._registerMeshWithPoseMatrix(this);
  512. }
  513. this._skeleton = value;
  514. if (!this._skeleton) {
  515. this._bonesTransformMatrices = null;
  516. }
  517. this._markSubMeshesAsAttributesDirty();
  518. }
  519. public get skeleton(): Nullable<Skeleton> {
  520. return this._skeleton;
  521. }
  522. /**
  523. * An event triggered when the mesh is rebuilt.
  524. */
  525. public onRebuildObservable = new Observable<AbstractMesh>();
  526. // Constructor
  527. /**
  528. * Creates a new AbstractMesh
  529. * @param name defines the name of the mesh
  530. * @param scene defines the hosting scene
  531. */
  532. constructor(name: string, scene: Nullable<Scene> = null) {
  533. super(name, scene, false);
  534. this.getScene().addMesh(this);
  535. this._resyncLightSources();
  536. }
  537. /**
  538. * Returns the string "AbstractMesh"
  539. * @returns "AbstractMesh"
  540. */
  541. public getClassName(): string {
  542. return "AbstractMesh";
  543. }
  544. /**
  545. * Gets a string representation of the current mesh
  546. * @param fullDetails defines a boolean indicating if full details must be included
  547. * @returns a string representation of the current mesh
  548. */
  549. public toString(fullDetails?: boolean): string {
  550. var ret = "Name: " + this.name + ", isInstance: " + (this.getClassName() !== "InstancedMesh" ? "YES" : "NO");
  551. ret += ", # of submeshes: " + (this.subMeshes ? this.subMeshes.length : 0);
  552. if (this._skeleton) {
  553. ret += ", skeleton: " + this._skeleton.name;
  554. }
  555. if (fullDetails) {
  556. ret += ", billboard mode: " + (["NONE", "X", "Y", null, "Z", null, null, "ALL"])[this.billboardMode];
  557. ret += ", freeze wrld mat: " + (this._isWorldMatrixFrozen || this._waitingFreezeWorldMatrix ? "YES" : "NO");
  558. }
  559. return ret;
  560. }
  561. /** @hidden */
  562. public _rebuild(): void {
  563. this.onRebuildObservable.notifyObservers(this);
  564. if (this._occlusionQuery) {
  565. this._occlusionQuery = null;
  566. }
  567. if (!this.subMeshes) {
  568. return;
  569. }
  570. for (var subMesh of this.subMeshes) {
  571. subMesh._rebuild();
  572. }
  573. }
  574. /** @hidden */
  575. public _resyncLightSources(): void {
  576. this._lightSources.length = 0;
  577. for (var light of this.getScene().lights) {
  578. if (!light.isEnabled()) {
  579. continue;
  580. }
  581. if (light.canAffectMesh(this)) {
  582. this._lightSources.push(light);
  583. }
  584. }
  585. this._markSubMeshesAsLightDirty();
  586. }
  587. /** @hidden */
  588. public _resyncLighSource(light: Light): void {
  589. var isIn = light.isEnabled() && light.canAffectMesh(this);
  590. var index = this._lightSources.indexOf(light);
  591. if (index === -1) {
  592. if (!isIn) {
  593. return;
  594. }
  595. this._lightSources.push(light);
  596. } else {
  597. if (isIn) {
  598. return;
  599. }
  600. this._lightSources.splice(index, 1);
  601. }
  602. this._markSubMeshesAsLightDirty();
  603. }
  604. /** @hidden */
  605. public _unBindEffect() {
  606. for (var subMesh of this.subMeshes) {
  607. subMesh.setEffect(null);
  608. }
  609. }
  610. /** @hidden */
  611. public _removeLightSource(light: Light): void {
  612. var index = this._lightSources.indexOf(light);
  613. if (index === -1) {
  614. return;
  615. }
  616. this._lightSources.splice(index, 1);
  617. this._markSubMeshesAsLightDirty();
  618. }
  619. private _markSubMeshesAsDirty(func: (defines: MaterialDefines) => void) {
  620. if (!this.subMeshes) {
  621. return;
  622. }
  623. for (var subMesh of this.subMeshes) {
  624. if (subMesh._materialDefines) {
  625. func(subMesh._materialDefines);
  626. }
  627. }
  628. }
  629. /** @hidden */
  630. public _markSubMeshesAsLightDirty() {
  631. this._markSubMeshesAsDirty((defines) => defines.markAsLightDirty());
  632. }
  633. /** @hidden */
  634. public _markSubMeshesAsAttributesDirty() {
  635. this._markSubMeshesAsDirty((defines) => defines.markAsAttributesDirty());
  636. }
  637. /** @hidden */
  638. public _markSubMeshesAsMiscDirty() {
  639. if (!this.subMeshes) {
  640. return;
  641. }
  642. for (var subMesh of this.subMeshes) {
  643. var material = subMesh.getMaterial();
  644. if (material) {
  645. material.markAsDirty(Constants.MATERIAL_MiscDirtyFlag);
  646. }
  647. }
  648. }
  649. /**
  650. * Gets or sets a Vector3 depicting the mesh scaling along each local axis X, Y, Z. Default is (1.0, 1.0, 1.0)
  651. */
  652. public get scaling(): Vector3 {
  653. return this._scaling;
  654. }
  655. public set scaling(newScaling: Vector3) {
  656. this._scaling = newScaling;
  657. if (this.physicsImpostor) {
  658. this.physicsImpostor.forceUpdate();
  659. }
  660. }
  661. // Methods
  662. /**
  663. * Returns true if the mesh is blocked. Implemented by child classes
  664. */
  665. public get isBlocked(): boolean {
  666. return false;
  667. }
  668. /**
  669. * Returns the mesh itself by default. Implemented by child classes
  670. * @param camera defines the camera to use to pick the right LOD level
  671. * @returns the currentAbstractMesh
  672. */
  673. public getLOD(camera: Camera): Nullable<AbstractMesh> {
  674. return this;
  675. }
  676. /**
  677. * Returns 0 by default. Implemented by child classes
  678. * @returns an integer
  679. */
  680. public getTotalVertices(): number {
  681. return 0;
  682. }
  683. /**
  684. * Returns a positive integer : the total number of indices in this mesh geometry.
  685. * @returns the numner of indices or zero if the mesh has no geometry.
  686. */
  687. public getTotalIndices(): number {
  688. return 0;
  689. }
  690. /**
  691. * Returns null by default. Implemented by child classes
  692. * @returns null
  693. */
  694. public getIndices(): Nullable<IndicesArray> {
  695. return null;
  696. }
  697. /**
  698. * Returns the array of the requested vertex data kind. Implemented by child classes
  699. * @param kind defines the vertex data kind to use
  700. * @returns null
  701. */
  702. public getVerticesData(kind: string): Nullable<FloatArray> {
  703. return null;
  704. }
  705. /**
  706. * Sets the vertex data of the mesh geometry for the requested `kind`.
  707. * If the mesh has no geometry, a new Geometry object is set to the mesh and then passed this vertex data.
  708. * Note that a new underlying VertexBuffer object is created each call.
  709. * If the `kind` is the `PositionKind`, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed.
  710. * @param kind defines vertex data kind:
  711. * * VertexBuffer.PositionKind
  712. * * VertexBuffer.UVKind
  713. * * VertexBuffer.UV2Kind
  714. * * VertexBuffer.UV3Kind
  715. * * VertexBuffer.UV4Kind
  716. * * VertexBuffer.UV5Kind
  717. * * VertexBuffer.UV6Kind
  718. * * VertexBuffer.ColorKind
  719. * * VertexBuffer.MatricesIndicesKind
  720. * * VertexBuffer.MatricesIndicesExtraKind
  721. * * VertexBuffer.MatricesWeightsKind
  722. * * VertexBuffer.MatricesWeightsExtraKind
  723. * @param data defines the data source
  724. * @param updatable defines if the data must be flagged as updatable (or static)
  725. * @param stride defines the vertex stride (size of an entire vertex). Can be null and in this case will be deduced from vertex data kind
  726. * @returns the current mesh
  727. */
  728. public setVerticesData(kind: string, data: FloatArray, updatable?: boolean, stride?: number): AbstractMesh {
  729. return this;
  730. }
  731. /**
  732. * Updates the existing vertex data of the mesh geometry for the requested `kind`.
  733. * If the mesh has no geometry, it is simply returned as it is.
  734. * @param kind defines vertex data kind:
  735. * * VertexBuffer.PositionKind
  736. * * VertexBuffer.UVKind
  737. * * VertexBuffer.UV2Kind
  738. * * VertexBuffer.UV3Kind
  739. * * VertexBuffer.UV4Kind
  740. * * VertexBuffer.UV5Kind
  741. * * VertexBuffer.UV6Kind
  742. * * VertexBuffer.ColorKind
  743. * * VertexBuffer.MatricesIndicesKind
  744. * * VertexBuffer.MatricesIndicesExtraKind
  745. * * VertexBuffer.MatricesWeightsKind
  746. * * VertexBuffer.MatricesWeightsExtraKind
  747. * @param data defines the data source
  748. * @param updateExtends If `kind` is `PositionKind` and if `updateExtends` is true, the mesh BoundingInfo is renewed, so the bounding box and sphere, and the mesh World Matrix is recomputed
  749. * @param makeItUnique If true, a new global geometry is created from this data and is set to the mesh
  750. * @returns the current mesh
  751. */
  752. public updateVerticesData(kind: string, data: FloatArray, updateExtends?: boolean, makeItUnique?: boolean): AbstractMesh {
  753. return this;
  754. }
  755. /**
  756. * Sets the mesh indices,
  757. * If the mesh has no geometry, a new Geometry object is created and set to the mesh.
  758. * @param indices Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array)
  759. * @param totalVertices Defines the total number of vertices
  760. * @returns the current mesh
  761. */
  762. public setIndices(indices: IndicesArray, totalVertices: Nullable<number>): AbstractMesh {
  763. return this;
  764. }
  765. /**
  766. * Gets a boolean indicating if specific vertex data is present
  767. * @param kind defines the vertex data kind to use
  768. * @returns true is data kind is present
  769. */
  770. public isVerticesDataPresent(kind: string): boolean {
  771. return false;
  772. }
  773. /**
  774. * Returns the mesh BoundingInfo object or creates a new one and returns if it was undefined
  775. * @returns a BoundingInfo
  776. */
  777. public getBoundingInfo(): BoundingInfo {
  778. if (this._masterMesh) {
  779. return this._masterMesh.getBoundingInfo();
  780. }
  781. if (!this._boundingInfo) {
  782. // this._boundingInfo is being created here
  783. this._updateBoundingInfo();
  784. }
  785. // cannot be null.
  786. return this._boundingInfo!;
  787. }
  788. /**
  789. * Uniformly scales the mesh to fit inside of a unit cube (1 X 1 X 1 units)
  790. * @param includeDescendants Use the hierarchy's bounding box instead of the mesh's bounding box
  791. * @returns the current mesh
  792. */
  793. public normalizeToUnitCube(includeDescendants = true): AbstractMesh {
  794. let boundingVectors = this.getHierarchyBoundingVectors(includeDescendants);
  795. let sizeVec = boundingVectors.max.subtract(boundingVectors.min);
  796. let maxDimension = Math.max(sizeVec.x, sizeVec.y, sizeVec.z);
  797. if (maxDimension === 0) {
  798. return this;
  799. }
  800. let scale = 1 / maxDimension;
  801. this.scaling.scaleInPlace(scale);
  802. return this;
  803. }
  804. /**
  805. * Overwrite the current bounding info
  806. * @param boundingInfo defines the new bounding info
  807. * @returns the current mesh
  808. */
  809. public setBoundingInfo(boundingInfo: BoundingInfo): AbstractMesh {
  810. this._boundingInfo = boundingInfo;
  811. return this;
  812. }
  813. /** Gets a boolean indicating if this mesh has skinning data and an attached skeleton */
  814. public get useBones(): boolean {
  815. return (<boolean>(this.skeleton && this.getScene().skeletonsEnabled && this.isVerticesDataPresent(VertexBuffer.MatricesIndicesKind) && this.isVerticesDataPresent(VertexBuffer.MatricesWeightsKind)));
  816. }
  817. /** @hidden */
  818. public _preActivate(): void {
  819. }
  820. /** @hidden */
  821. public _preActivateForIntermediateRendering(renderId: number): void {
  822. }
  823. /** @hidden */
  824. public _activate(renderId: number): void {
  825. this._renderId = renderId;
  826. }
  827. /**
  828. * Gets the current world matrix
  829. * @returns a Matrix
  830. */
  831. public getWorldMatrix(): Matrix {
  832. if (this._masterMesh) {
  833. return this._masterMesh.getWorldMatrix();
  834. }
  835. return super.getWorldMatrix();
  836. }
  837. /** @hidden */
  838. public _getWorldMatrixDeterminant(): number {
  839. if (this._masterMesh) {
  840. return this._masterMesh._getWorldMatrixDeterminant();
  841. }
  842. return super._getWorldMatrixDeterminant();
  843. }
  844. // ================================== Point of View Movement =================================
  845. /**
  846. * Perform relative position change from the point of view of behind the front of the mesh.
  847. * This is performed taking into account the meshes current rotation, so you do not have to care.
  848. * Supports definition of mesh facing forward or backward
  849. * @param amountRight defines the distance on the right axis
  850. * @param amountUp defines the distance on the up axis
  851. * @param amountForward defines the distance on the forward axis
  852. * @returns the current mesh
  853. */
  854. public movePOV(amountRight: number, amountUp: number, amountForward: number): AbstractMesh {
  855. this.position.addInPlace(this.calcMovePOV(amountRight, amountUp, amountForward));
  856. return this;
  857. }
  858. /**
  859. * Calculate relative position change from the point of view of behind the front of the mesh.
  860. * This is performed taking into account the meshes current rotation, so you do not have to care.
  861. * Supports definition of mesh facing forward or backward
  862. * @param amountRight defines the distance on the right axis
  863. * @param amountUp defines the distance on the up axis
  864. * @param amountForward defines the distance on the forward axis
  865. * @returns the new displacement vector
  866. */
  867. public calcMovePOV(amountRight: number, amountUp: number, amountForward: number): Vector3 {
  868. var rotMatrix = new Matrix();
  869. var rotQuaternion = (this.rotationQuaternion) ? this.rotationQuaternion : Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
  870. rotQuaternion.toRotationMatrix(rotMatrix);
  871. var translationDelta = Vector3.Zero();
  872. var defForwardMult = this.definedFacingForward ? -1 : 1;
  873. Vector3.TransformCoordinatesFromFloatsToRef(amountRight * defForwardMult, amountUp, amountForward * defForwardMult, rotMatrix, translationDelta);
  874. return translationDelta;
  875. }
  876. // ================================== Point of View Rotation =================================
  877. /**
  878. * Perform relative rotation change from the point of view of behind the front of the mesh.
  879. * Supports definition of mesh facing forward or backward
  880. * @param flipBack defines the flip
  881. * @param twirlClockwise defines the twirl
  882. * @param tiltRight defines the tilt
  883. * @returns the current mesh
  884. */
  885. public rotatePOV(flipBack: number, twirlClockwise: number, tiltRight: number): AbstractMesh {
  886. this.rotation.addInPlace(this.calcRotatePOV(flipBack, twirlClockwise, tiltRight));
  887. return this;
  888. }
  889. /**
  890. * Calculate relative rotation change from the point of view of behind the front of the mesh.
  891. * Supports definition of mesh facing forward or backward.
  892. * @param flipBack defines the flip
  893. * @param twirlClockwise defines the twirl
  894. * @param tiltRight defines the tilt
  895. * @returns the new rotation vector
  896. */
  897. public calcRotatePOV(flipBack: number, twirlClockwise: number, tiltRight: number): Vector3 {
  898. var defForwardMult = this.definedFacingForward ? 1 : -1;
  899. return new Vector3(flipBack * defForwardMult, twirlClockwise, tiltRight * defForwardMult);
  900. }
  901. /**
  902. * Return the minimum and maximum world vectors of the entire hierarchy under current mesh
  903. * @param includeDescendants Include bounding info from descendants as well (true by default)
  904. * @param predicate defines a callback function that can be customize to filter what meshes should be included in the list used to compute the bounding vectors
  905. * @returns the new bounding vectors
  906. */
  907. public getHierarchyBoundingVectors(includeDescendants = true, predicate: Nullable<(abstractMesh: AbstractMesh) => boolean> = null): { min: Vector3, max: Vector3 } {
  908. // Ensures that all world matrix will be recomputed.
  909. this.getScene().incrementRenderId();
  910. this.computeWorldMatrix(true);
  911. let min: Vector3;
  912. let max: Vector3;
  913. let boundingInfo = this.getBoundingInfo();
  914. if (!this.subMeshes) {
  915. min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
  916. max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
  917. } else {
  918. min = boundingInfo.boundingBox.minimumWorld;
  919. max = boundingInfo.boundingBox.maximumWorld;
  920. }
  921. if (includeDescendants) {
  922. let descendants = this.getDescendants(false);
  923. for (var descendant of descendants) {
  924. let childMesh = <AbstractMesh>descendant;
  925. childMesh.computeWorldMatrix(true);
  926. // Filters meshes based on custom predicate function.
  927. if (predicate && !predicate(childMesh)) {
  928. continue;
  929. }
  930. //make sure we have the needed params to get mix and max
  931. if (!childMesh.getBoundingInfo || childMesh.getTotalVertices() === 0) {
  932. continue;
  933. }
  934. let childBoundingInfo = childMesh.getBoundingInfo();
  935. let boundingBox = childBoundingInfo.boundingBox;
  936. var minBox = boundingBox.minimumWorld;
  937. var maxBox = boundingBox.maximumWorld;
  938. Tools.CheckExtends(minBox, min, max);
  939. Tools.CheckExtends(maxBox, min, max);
  940. }
  941. }
  942. return {
  943. min: min,
  944. max: max
  945. };
  946. }
  947. /**
  948. * This method recomputes and sets a new BoundingInfo to the mesh unless it is locked.
  949. * This means the mesh underlying bounding box and sphere are recomputed.
  950. * @param applySkeleton defines whether to apply the skeleton before computing the bounding info
  951. * @returns the current mesh
  952. */
  953. public refreshBoundingInfo(applySkeleton: boolean = false): AbstractMesh {
  954. if (this._boundingInfo && this._boundingInfo.isLocked) {
  955. return this;
  956. }
  957. this._refreshBoundingInfo(this._getPositionData(applySkeleton), null);
  958. return this;
  959. }
  960. /** @hidden */
  961. public _refreshBoundingInfo(data: Nullable<FloatArray>, bias: Nullable<Vector2>): void {
  962. if (data) {
  963. var extend = Tools.ExtractMinAndMax(data, 0, this.getTotalVertices(), bias);
  964. if (this._boundingInfo) {
  965. this._boundingInfo.reConstruct(extend.minimum, extend.maximum);
  966. }
  967. else {
  968. this._boundingInfo = new BoundingInfo(extend.minimum, extend.maximum);
  969. }
  970. }
  971. if (this.subMeshes) {
  972. for (var index = 0; index < this.subMeshes.length; index++) {
  973. this.subMeshes[index].refreshBoundingInfo();
  974. }
  975. }
  976. this._updateBoundingInfo();
  977. }
  978. /** @hidden */
  979. public _getPositionData(applySkeleton: boolean): Nullable<FloatArray> {
  980. var data = this.getVerticesData(VertexBuffer.PositionKind);
  981. if (data && applySkeleton && this.skeleton) {
  982. data = Tools.Slice(data);
  983. var matricesIndicesData = this.getVerticesData(VertexBuffer.MatricesIndicesKind);
  984. var matricesWeightsData = this.getVerticesData(VertexBuffer.MatricesWeightsKind);
  985. if (matricesWeightsData && matricesIndicesData) {
  986. var needExtras = this.numBoneInfluencers > 4;
  987. var matricesIndicesExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesIndicesExtraKind) : null;
  988. var matricesWeightsExtraData = needExtras ? this.getVerticesData(VertexBuffer.MatricesWeightsExtraKind) : null;
  989. var skeletonMatrices = this.skeleton.getTransformMatrices(this);
  990. var tempVector = Tmp.Vector3[0];
  991. var finalMatrix = Tmp.Matrix[0];
  992. var tempMatrix = Tmp.Matrix[1];
  993. var matWeightIdx = 0;
  994. for (var index = 0; index < data.length; index += 3, matWeightIdx += 4) {
  995. finalMatrix.reset();
  996. var inf: number;
  997. var weight: number;
  998. for (inf = 0; inf < 4; inf++) {
  999. weight = matricesWeightsData[matWeightIdx + inf];
  1000. if (weight > 0) {
  1001. Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesData[matWeightIdx + inf] * 16), weight, tempMatrix);
  1002. finalMatrix.addToSelf(tempMatrix);
  1003. }
  1004. }
  1005. if (needExtras) {
  1006. for (inf = 0; inf < 4; inf++) {
  1007. weight = matricesWeightsExtraData![matWeightIdx + inf];
  1008. if (weight > 0) {
  1009. Matrix.FromFloat32ArrayToRefScaled(skeletonMatrices, Math.floor(matricesIndicesExtraData![matWeightIdx + inf] * 16), weight, tempMatrix);
  1010. finalMatrix.addToSelf(tempMatrix);
  1011. }
  1012. }
  1013. }
  1014. Vector3.TransformCoordinatesFromFloatsToRef(data[index], data[index + 1], data[index + 2], finalMatrix, tempVector);
  1015. tempVector.toArray(data, index);
  1016. }
  1017. }
  1018. }
  1019. return data;
  1020. }
  1021. /** @hidden */
  1022. public _updateBoundingInfo(): AbstractMesh {
  1023. const effectiveMesh = (this.skeleton && this.skeleton.overrideMesh) || this;
  1024. if (this._boundingInfo) {
  1025. this._boundingInfo.update(effectiveMesh.worldMatrixFromCache);
  1026. }
  1027. else {
  1028. this._boundingInfo = new BoundingInfo(this.absolutePosition, this.absolutePosition, effectiveMesh.worldMatrixFromCache);
  1029. }
  1030. this._updateSubMeshesBoundingInfo(effectiveMesh.worldMatrixFromCache);
  1031. return this;
  1032. }
  1033. /** @hidden */
  1034. public _updateSubMeshesBoundingInfo(matrix: DeepImmutable<Matrix>): AbstractMesh {
  1035. if (!this.subMeshes) {
  1036. return this;
  1037. }
  1038. let count = this.subMeshes.length;
  1039. for (var subIndex = 0; subIndex < count; subIndex++) {
  1040. var subMesh = this.subMeshes[subIndex];
  1041. if (count > 1 || !subMesh.IsGlobal) {
  1042. subMesh.updateBoundingInfo(matrix);
  1043. }
  1044. }
  1045. return this;
  1046. }
  1047. /** @hidden */
  1048. protected _afterComputeWorldMatrix(): void {
  1049. // Bounding info
  1050. this._updateBoundingInfo();
  1051. }
  1052. /**
  1053. * Returns `true` if the mesh is within the frustum defined by the passed array of planes.
  1054. * A mesh is in the frustum if its bounding box intersects the frustum
  1055. * @param frustumPlanes defines the frustum to test
  1056. * @returns true if the mesh is in the frustum planes
  1057. */
  1058. public isInFrustum(frustumPlanes: Plane[]): boolean {
  1059. return this._boundingInfo !== null && this._boundingInfo.isInFrustum(frustumPlanes, this.cullingStrategy);
  1060. }
  1061. /**
  1062. * Returns `true` if the mesh is completely in the frustum defined be the passed array of planes.
  1063. * A mesh is completely in the frustum if its bounding box it completely inside the frustum.
  1064. * @param frustumPlanes defines the frustum to test
  1065. * @returns true if the mesh is completely in the frustum planes
  1066. */
  1067. public isCompletelyInFrustum(frustumPlanes: Plane[]): boolean {
  1068. return this._boundingInfo !== null && this._boundingInfo.isCompletelyInFrustum(frustumPlanes);
  1069. }
  1070. /**
  1071. * True if the mesh intersects another mesh or a SolidParticle object
  1072. * @param mesh defines a target mesh or SolidParticle to test
  1073. * @param precise Unless the parameter `precise` is set to `true` the intersection is computed according to Axis Aligned Bounding Boxes (AABB), else according to OBB (Oriented BBoxes)
  1074. * @param includeDescendants Can be set to true to test if the mesh defined in parameters intersects with the current mesh or any child meshes
  1075. * @returns true if there is an intersection
  1076. */
  1077. public intersectsMesh(mesh: AbstractMesh | SolidParticle, precise: boolean = false, includeDescendants?: boolean): boolean {
  1078. if (!this._boundingInfo || !mesh._boundingInfo) {
  1079. return false;
  1080. }
  1081. if (this._boundingInfo.intersects(mesh._boundingInfo, precise)) {
  1082. return true;
  1083. }
  1084. if (includeDescendants) {
  1085. for (var child of this.getChildMeshes()) {
  1086. if (child.intersectsMesh(mesh, precise, true)) {
  1087. return true;
  1088. }
  1089. }
  1090. }
  1091. return false;
  1092. }
  1093. /**
  1094. * Returns true if the passed point (Vector3) is inside the mesh bounding box
  1095. * @param point defines the point to test
  1096. * @returns true if there is an intersection
  1097. */
  1098. public intersectsPoint(point: Vector3): boolean {
  1099. if (!this._boundingInfo) {
  1100. return false;
  1101. }
  1102. return this._boundingInfo.intersectsPoint(point);
  1103. }
  1104. // Collisions
  1105. /**
  1106. * Gets or sets a boolean indicating that this mesh can be used in the collision engine
  1107. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity
  1108. */
  1109. public get checkCollisions(): boolean {
  1110. return this._checkCollisions;
  1111. }
  1112. public set checkCollisions(collisionEnabled: boolean) {
  1113. this._checkCollisions = collisionEnabled;
  1114. }
  1115. /**
  1116. * Gets Collider object used to compute collisions (not physics)
  1117. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity
  1118. */
  1119. public get collider(): Collider {
  1120. return this._collider;
  1121. }
  1122. /**
  1123. * Move the mesh using collision engine
  1124. * @see http://doc.babylonjs.com/babylon101/cameras,_mesh_collisions_and_gravity
  1125. * @param displacement defines the requested displacement vector
  1126. * @returns the current mesh
  1127. */
  1128. public moveWithCollisions(displacement: Vector3): AbstractMesh {
  1129. var globalPosition = this.getAbsolutePosition();
  1130. globalPosition.addToRef(this.ellipsoidOffset, this._oldPositionForCollisions);
  1131. if (!this._collider) {
  1132. this._collider = new Collider();
  1133. }
  1134. this._collider._radius = this.ellipsoid;
  1135. this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, displacement, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
  1136. return this;
  1137. }
  1138. private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
  1139. newPosition.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
  1140. if (this._diffPositionForCollisions.length() > Engine.CollisionsEpsilon) {
  1141. this.position.addInPlace(this._diffPositionForCollisions);
  1142. }
  1143. if (collidedMesh) {
  1144. this.onCollideObservable.notifyObservers(collidedMesh);
  1145. }
  1146. this.onCollisionPositionChangeObservable.notifyObservers(this.position);
  1147. }
  1148. // Collisions
  1149. /** @hidden */
  1150. public _collideForSubMesh(subMesh: SubMesh, transformMatrix: Matrix, collider: Collider): AbstractMesh {
  1151. this._generatePointsArray();
  1152. if (!this._positions) {
  1153. return this;
  1154. }
  1155. // Transformation
  1156. if (!subMesh._lastColliderWorldVertices || !subMesh._lastColliderTransformMatrix.equals(transformMatrix)) {
  1157. subMesh._lastColliderTransformMatrix = transformMatrix.clone();
  1158. subMesh._lastColliderWorldVertices = [];
  1159. subMesh._trianglePlanes = [];
  1160. var start = subMesh.verticesStart;
  1161. var end = (subMesh.verticesStart + subMesh.verticesCount);
  1162. for (var i = start; i < end; i++) {
  1163. subMesh._lastColliderWorldVertices.push(Vector3.TransformCoordinates(this._positions[i], transformMatrix));
  1164. }
  1165. }
  1166. // Collide
  1167. collider._collide(subMesh._trianglePlanes, subMesh._lastColliderWorldVertices, (<IndicesArray>this.getIndices()), subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, !!subMesh.getMaterial());
  1168. if (collider.collisionFound) {
  1169. collider.collidedMesh = this;
  1170. }
  1171. return this;
  1172. }
  1173. /** @hidden */
  1174. public _processCollisionsForSubMeshes(collider: Collider, transformMatrix: Matrix): AbstractMesh {
  1175. const subMeshes = this._scene.getCollidingSubMeshCandidates(this, collider);
  1176. const len = subMeshes.length;
  1177. for (var index = 0; index < len; index++) {
  1178. var subMesh = subMeshes.data[index];
  1179. // Bounding test
  1180. if (len > 1 && !subMesh._checkCollision(collider)) {
  1181. continue;
  1182. }
  1183. this._collideForSubMesh(subMesh, transformMatrix, collider);
  1184. }
  1185. return this;
  1186. }
  1187. /** @hidden */
  1188. public _checkCollision(collider: Collider): AbstractMesh {
  1189. // Bounding box test
  1190. if (!this._boundingInfo || !this._boundingInfo._checkCollision(collider)) {
  1191. return this;
  1192. }
  1193. // Transformation matrix
  1194. const collisionsScalingMatrix = Tmp.Matrix[0];
  1195. const collisionsTransformMatrix = Tmp.Matrix[1];
  1196. Matrix.ScalingToRef(1.0 / collider._radius.x, 1.0 / collider._radius.y, 1.0 / collider._radius.z, collisionsScalingMatrix);
  1197. this.worldMatrixFromCache.multiplyToRef(collisionsScalingMatrix, collisionsTransformMatrix);
  1198. this._processCollisionsForSubMeshes(collider, collisionsTransformMatrix);
  1199. return this;
  1200. }
  1201. // Picking
  1202. /** @hidden */
  1203. public _generatePointsArray(): boolean {
  1204. return false;
  1205. }
  1206. /**
  1207. * Checks if the passed Ray intersects with the mesh
  1208. * @param ray defines the ray to use
  1209. * @param fastCheck defines if fast mode (but less precise) must be used (false by default)
  1210. * @returns the picking info
  1211. * @see http://doc.babylonjs.com/babylon101/intersect_collisions_-_mesh
  1212. */
  1213. public intersects(ray: Ray, fastCheck?: boolean): PickingInfo {
  1214. var pickingInfo = new PickingInfo();
  1215. const intersectionThreshold = this.getClassName() === "InstancedLinesMesh" || this.getClassName() === "LinesMesh" ? (this as any).intersectionThreshold : 0;
  1216. const boundingInfo = this._boundingInfo;
  1217. if (!this.subMeshes || !boundingInfo || !ray.intersectsSphere(boundingInfo.boundingSphere, intersectionThreshold) || !ray.intersectsBox(boundingInfo.boundingBox, intersectionThreshold)) {
  1218. return pickingInfo;
  1219. }
  1220. if (!this._generatePointsArray()) {
  1221. return pickingInfo;
  1222. }
  1223. var intersectInfo: Nullable<IntersectionInfo> = null;
  1224. var subMeshes = this._scene.getIntersectingSubMeshCandidates(this, ray);
  1225. var len: number = subMeshes.length;
  1226. for (var index = 0; index < len; index++) {
  1227. var subMesh = subMeshes.data[index];
  1228. // Bounding test
  1229. if (len > 1 && !subMesh.canIntersects(ray)) {
  1230. continue;
  1231. }
  1232. var currentIntersectInfo = subMesh.intersects(ray, (<Vector3[]>this._positions), (<IndicesArray>this.getIndices()), fastCheck);
  1233. if (currentIntersectInfo) {
  1234. if (fastCheck || !intersectInfo || currentIntersectInfo.distance < intersectInfo.distance) {
  1235. intersectInfo = currentIntersectInfo;
  1236. intersectInfo.subMeshId = index;
  1237. if (fastCheck) {
  1238. break;
  1239. }
  1240. }
  1241. }
  1242. }
  1243. if (intersectInfo) {
  1244. // Get picked point
  1245. const world = this.getWorldMatrix();
  1246. const worldOrigin = Tmp.Vector3[0];
  1247. const direction = Tmp.Vector3[1];
  1248. Vector3.TransformCoordinatesToRef(ray.origin, world, worldOrigin);
  1249. ray.direction.scaleToRef(intersectInfo.distance, direction);
  1250. const worldDirection = Vector3.TransformNormal(direction, world);
  1251. const pickedPoint = worldDirection.addInPlace(worldOrigin);
  1252. // Return result
  1253. pickingInfo.hit = true;
  1254. pickingInfo.distance = Vector3.Distance(worldOrigin, pickedPoint);
  1255. pickingInfo.pickedPoint = pickedPoint;
  1256. pickingInfo.pickedMesh = this;
  1257. pickingInfo.bu = intersectInfo.bu || 0;
  1258. pickingInfo.bv = intersectInfo.bv || 0;
  1259. pickingInfo.faceId = intersectInfo.faceId;
  1260. pickingInfo.subMeshId = intersectInfo.subMeshId;
  1261. return pickingInfo;
  1262. }
  1263. return pickingInfo;
  1264. }
  1265. /**
  1266. * Clones the current mesh
  1267. * @param name defines the mesh name
  1268. * @param newParent defines the new mesh parent
  1269. * @param doNotCloneChildren defines a boolean indicating that children must not be cloned (false by default)
  1270. * @returns the new mesh
  1271. */
  1272. public clone(name: string, newParent: Node, doNotCloneChildren?: boolean): Nullable<AbstractMesh> {
  1273. return null;
  1274. }
  1275. /**
  1276. * Disposes all the submeshes of the current meshnp
  1277. * @returns the current mesh
  1278. */
  1279. public releaseSubMeshes(): AbstractMesh {
  1280. if (this.subMeshes) {
  1281. while (this.subMeshes.length) {
  1282. this.subMeshes[0].dispose();
  1283. }
  1284. } else {
  1285. this.subMeshes = new Array<SubMesh>();
  1286. }
  1287. return this;
  1288. }
  1289. /**
  1290. * Releases resources associated with this abstract mesh.
  1291. * @param doNotRecurse Set to true to not recurse into each children (recurse into each children by default)
  1292. * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
  1293. */
  1294. public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
  1295. var index: number;
  1296. // Smart Array Retainers.
  1297. this.getScene().freeActiveMeshes();
  1298. this.getScene().freeRenderingGroups();
  1299. // Action manager
  1300. if (this.actionManager !== undefined && this.actionManager !== null) {
  1301. this.actionManager.dispose();
  1302. this.actionManager = null;
  1303. }
  1304. // Skeleton
  1305. this._skeleton = null;
  1306. // Intersections in progress
  1307. for (index = 0; index < this._intersectionsInProgress.length; index++) {
  1308. var other = this._intersectionsInProgress[index];
  1309. var pos = other._intersectionsInProgress.indexOf(this);
  1310. other._intersectionsInProgress.splice(pos, 1);
  1311. }
  1312. this._intersectionsInProgress = [];
  1313. // Lights
  1314. var lights = this.getScene().lights;
  1315. lights.forEach((light: Light) => {
  1316. var meshIndex = light.includedOnlyMeshes.indexOf(this);
  1317. if (meshIndex !== -1) {
  1318. light.includedOnlyMeshes.splice(meshIndex, 1);
  1319. }
  1320. meshIndex = light.excludedMeshes.indexOf(this);
  1321. if (meshIndex !== -1) {
  1322. light.excludedMeshes.splice(meshIndex, 1);
  1323. }
  1324. // Shadow generators
  1325. var generator = light.getShadowGenerator();
  1326. if (generator) {
  1327. var shadowMap = generator.getShadowMap();
  1328. if (shadowMap && shadowMap.renderList) {
  1329. meshIndex = shadowMap.renderList.indexOf(this);
  1330. if (meshIndex !== -1) {
  1331. shadowMap.renderList.splice(meshIndex, 1);
  1332. }
  1333. }
  1334. }
  1335. });
  1336. // SubMeshes
  1337. if (this.getClassName() !== "InstancedMesh" || this.getClassName() !== "InstancedLinesMesh") {
  1338. this.releaseSubMeshes();
  1339. }
  1340. // Query
  1341. let engine = this.getScene().getEngine();
  1342. if (this._occlusionQuery) {
  1343. this.isOcclusionQueryInProgress = false;
  1344. engine.deleteQuery(this._occlusionQuery);
  1345. this._occlusionQuery = null;
  1346. }
  1347. // Engine
  1348. engine.wipeCaches();
  1349. // Remove from scene
  1350. this.getScene().removeMesh(this);
  1351. if (disposeMaterialAndTextures) {
  1352. if (this.material) {
  1353. this.material.dispose(false, true);
  1354. }
  1355. }
  1356. if (!doNotRecurse) {
  1357. // Particles
  1358. for (index = 0; index < this.getScene().particleSystems.length; index++) {
  1359. if (this.getScene().particleSystems[index].emitter === this) {
  1360. this.getScene().particleSystems[index].dispose();
  1361. index--;
  1362. }
  1363. }
  1364. }
  1365. // facet data
  1366. if (this._facetData.facetDataEnabled) {
  1367. this.disableFacetData();
  1368. }
  1369. this.onAfterWorldMatrixUpdateObservable.clear();
  1370. this.onCollideObservable.clear();
  1371. this.onCollisionPositionChangeObservable.clear();
  1372. this.onRebuildObservable.clear();
  1373. super.dispose(doNotRecurse, disposeMaterialAndTextures);
  1374. }
  1375. /**
  1376. * Adds the passed mesh as a child to the current mesh
  1377. * @param mesh defines the child mesh
  1378. * @returns the current mesh
  1379. */
  1380. public addChild(mesh: AbstractMesh): AbstractMesh {
  1381. mesh.setParent(this);
  1382. return this;
  1383. }
  1384. /**
  1385. * Removes the passed mesh from the current mesh children list
  1386. * @param mesh defines the child mesh
  1387. * @returns the current mesh
  1388. */
  1389. public removeChild(mesh: AbstractMesh): AbstractMesh {
  1390. mesh.setParent(null);
  1391. return this;
  1392. }
  1393. // Facet data
  1394. /** @hidden */
  1395. private _initFacetData(): AbstractMesh {
  1396. const data = this._facetData;
  1397. if (!data.facetNormals) {
  1398. data.facetNormals = new Array<Vector3>();
  1399. }
  1400. if (!data.facetPositions) {
  1401. data.facetPositions = new Array<Vector3>();
  1402. }
  1403. if (!data.facetPartitioning) {
  1404. data.facetPartitioning = new Array<number[]>();
  1405. }
  1406. data.facetNb = ((<IndicesArray>this.getIndices()).length / 3) | 0;
  1407. data.partitioningSubdivisions = (data.partitioningSubdivisions) ? data.partitioningSubdivisions : 10; // default nb of partitioning subdivisions = 10
  1408. data.partitioningBBoxRatio = (data.partitioningBBoxRatio) ? data.partitioningBBoxRatio : 1.01; // default ratio 1.01 = the partitioning is 1% bigger than the bounding box
  1409. for (var f = 0; f < data.facetNb; f++) {
  1410. data.facetNormals[f] = Vector3.Zero();
  1411. data.facetPositions[f] = Vector3.Zero();
  1412. }
  1413. data.facetDataEnabled = true;
  1414. return this;
  1415. }
  1416. /**
  1417. * Updates the mesh facetData arrays and the internal partitioning when the mesh is morphed or updated.
  1418. * This method can be called within the render loop.
  1419. * You don't need to call this method by yourself in the render loop when you update/morph a mesh with the methods CreateXXX() as they automatically manage this computation
  1420. * @returns the current mesh
  1421. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1422. */
  1423. public updateFacetData(): AbstractMesh {
  1424. const data = this._facetData;
  1425. if (!data.facetDataEnabled) {
  1426. this._initFacetData();
  1427. }
  1428. var positions = this.getVerticesData(VertexBuffer.PositionKind);
  1429. var indices = this.getIndices();
  1430. var normals = this.getVerticesData(VertexBuffer.NormalKind);
  1431. var bInfo = this.getBoundingInfo();
  1432. if (data.facetDepthSort && !data.facetDepthSortEnabled) {
  1433. // init arrays, matrix and sort function on first call
  1434. data.facetDepthSortEnabled = true;
  1435. if (indices instanceof Uint16Array) {
  1436. data.depthSortedIndices = new Uint16Array(indices!);
  1437. }
  1438. else if (indices instanceof Uint32Array) {
  1439. data.depthSortedIndices = new Uint32Array(indices!);
  1440. }
  1441. else {
  1442. var needs32bits = false;
  1443. for (var i = 0; i < indices!.length; i++) {
  1444. if (indices![i] > 65535) {
  1445. needs32bits = true;
  1446. break;
  1447. }
  1448. }
  1449. if (needs32bits) {
  1450. data.depthSortedIndices = new Uint32Array(indices!);
  1451. }
  1452. else {
  1453. data.depthSortedIndices = new Uint16Array(indices!);
  1454. }
  1455. }
  1456. data.facetDepthSortFunction = function(f1, f2) {
  1457. return (f2.sqDistance - f1.sqDistance);
  1458. };
  1459. if (!data.facetDepthSortFrom) {
  1460. var camera = this.getScene().activeCamera;
  1461. data.facetDepthSortFrom = (camera) ? camera.position : Vector3.Zero();
  1462. }
  1463. data.depthSortedFacets = [];
  1464. for (var f = 0; f < data.facetNb; f++) {
  1465. var depthSortedFacet = { ind: f * 3, sqDistance: 0.0 };
  1466. data.depthSortedFacets.push(depthSortedFacet);
  1467. }
  1468. data.invertedMatrix = Matrix.Identity();
  1469. data.facetDepthSortOrigin = Vector3.Zero();
  1470. }
  1471. data.bbSize.x = (bInfo.maximum.x - bInfo.minimum.x > Epsilon) ? bInfo.maximum.x - bInfo.minimum.x : Epsilon;
  1472. data.bbSize.y = (bInfo.maximum.y - bInfo.minimum.y > Epsilon) ? bInfo.maximum.y - bInfo.minimum.y : Epsilon;
  1473. data.bbSize.z = (bInfo.maximum.z - bInfo.minimum.z > Epsilon) ? bInfo.maximum.z - bInfo.minimum.z : Epsilon;
  1474. var bbSizeMax = (data.bbSize.x > data.bbSize.y) ? data.bbSize.x : data.bbSize.y;
  1475. bbSizeMax = (bbSizeMax > data.bbSize.z) ? bbSizeMax : data.bbSize.z;
  1476. data.subDiv.max = data.partitioningSubdivisions;
  1477. data.subDiv.X = Math.floor(data.subDiv.max * data.bbSize.x / bbSizeMax); // adjust the number of subdivisions per axis
  1478. data.subDiv.Y = Math.floor(data.subDiv.max * data.bbSize.y / bbSizeMax); // according to each bbox size per axis
  1479. data.subDiv.Z = Math.floor(data.subDiv.max * data.bbSize.z / bbSizeMax);
  1480. data.subDiv.X = data.subDiv.X < 1 ? 1 : data.subDiv.X; // at least one subdivision
  1481. data.subDiv.Y = data.subDiv.Y < 1 ? 1 : data.subDiv.Y;
  1482. data.subDiv.Z = data.subDiv.Z < 1 ? 1 : data.subDiv.Z;
  1483. // set the parameters for ComputeNormals()
  1484. data.facetParameters.facetNormals = this.getFacetLocalNormals();
  1485. data.facetParameters.facetPositions = this.getFacetLocalPositions();
  1486. data.facetParameters.facetPartitioning = this.getFacetLocalPartitioning();
  1487. data.facetParameters.bInfo = bInfo;
  1488. data.facetParameters.bbSize = data.bbSize;
  1489. data.facetParameters.subDiv = data.subDiv;
  1490. data.facetParameters.ratio = this.partitioningBBoxRatio;
  1491. data.facetParameters.depthSort = data.facetDepthSort;
  1492. if (data.facetDepthSort && data.facetDepthSortEnabled) {
  1493. this.computeWorldMatrix(true);
  1494. this._worldMatrix.invertToRef(data.invertedMatrix);
  1495. Vector3.TransformCoordinatesToRef(data.facetDepthSortFrom, data.invertedMatrix, data.facetDepthSortOrigin);
  1496. data.facetParameters.distanceTo = data.facetDepthSortOrigin;
  1497. }
  1498. data.facetParameters.depthSortedFacets = data.depthSortedFacets;
  1499. VertexData.ComputeNormals(positions, indices, normals, data.facetParameters);
  1500. if (data.facetDepthSort && data.facetDepthSortEnabled) {
  1501. data.depthSortedFacets.sort(data.facetDepthSortFunction);
  1502. var l = (data.depthSortedIndices.length / 3) | 0;
  1503. for (var f = 0; f < l; f++) {
  1504. var sind = data.depthSortedFacets[f].ind;
  1505. data.depthSortedIndices[f * 3] = indices![sind];
  1506. data.depthSortedIndices[f * 3 + 1] = indices![sind + 1];
  1507. data.depthSortedIndices[f * 3 + 2] = indices![sind + 2];
  1508. }
  1509. this.updateIndices(data.depthSortedIndices);
  1510. }
  1511. return this;
  1512. }
  1513. /**
  1514. * Returns the facetLocalNormals array.
  1515. * The normals are expressed in the mesh local spac
  1516. * @returns an array of Vector3
  1517. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1518. */
  1519. public getFacetLocalNormals(): Vector3[] {
  1520. if (!this._facetData.facetNormals) {
  1521. this.updateFacetData();
  1522. }
  1523. return this._facetData.facetNormals;
  1524. }
  1525. /**
  1526. * Returns the facetLocalPositions array.
  1527. * The facet positions are expressed in the mesh local space
  1528. * @returns an array of Vector3
  1529. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1530. */
  1531. public getFacetLocalPositions(): Vector3[] {
  1532. if (!this._facetData.facetPositions) {
  1533. this.updateFacetData();
  1534. }
  1535. return this._facetData.facetPositions;
  1536. }
  1537. /**
  1538. * Returns the facetLocalPartioning array
  1539. * @returns an array of array of numbers
  1540. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1541. */
  1542. public getFacetLocalPartitioning(): number[][] {
  1543. if (!this._facetData.facetPartitioning) {
  1544. this.updateFacetData();
  1545. }
  1546. return this._facetData.facetPartitioning;
  1547. }
  1548. /**
  1549. * Returns the i-th facet position in the world system.
  1550. * This method allocates a new Vector3 per call
  1551. * @param i defines the facet index
  1552. * @returns a new Vector3
  1553. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1554. */
  1555. public getFacetPosition(i: number): Vector3 {
  1556. var pos = Vector3.Zero();
  1557. this.getFacetPositionToRef(i, pos);
  1558. return pos;
  1559. }
  1560. /**
  1561. * Sets the reference Vector3 with the i-th facet position in the world system
  1562. * @param i defines the facet index
  1563. * @param ref defines the target vector
  1564. * @returns the current mesh
  1565. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1566. */
  1567. public getFacetPositionToRef(i: number, ref: Vector3): AbstractMesh {
  1568. var localPos = (this.getFacetLocalPositions())[i];
  1569. var world = this.getWorldMatrix();
  1570. Vector3.TransformCoordinatesToRef(localPos, world, ref);
  1571. return this;
  1572. }
  1573. /**
  1574. * Returns the i-th facet normal in the world system.
  1575. * This method allocates a new Vector3 per call
  1576. * @param i defines the facet index
  1577. * @returns a new Vector3
  1578. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1579. */
  1580. public getFacetNormal(i: number): Vector3 {
  1581. var norm = Vector3.Zero();
  1582. this.getFacetNormalToRef(i, norm);
  1583. return norm;
  1584. }
  1585. /**
  1586. * Sets the reference Vector3 with the i-th facet normal in the world system
  1587. * @param i defines the facet index
  1588. * @param ref defines the target vector
  1589. * @returns the current mesh
  1590. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1591. */
  1592. public getFacetNormalToRef(i: number, ref: Vector3) {
  1593. var localNorm = (this.getFacetLocalNormals())[i];
  1594. Vector3.TransformNormalToRef(localNorm, this.getWorldMatrix(), ref);
  1595. return this;
  1596. }
  1597. /**
  1598. * Returns the facets (in an array) in the same partitioning block than the one the passed coordinates are located (expressed in the mesh local system)
  1599. * @param x defines x coordinate
  1600. * @param y defines y coordinate
  1601. * @param z defines z coordinate
  1602. * @returns the array of facet indexes
  1603. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1604. */
  1605. public getFacetsAtLocalCoordinates(x: number, y: number, z: number): Nullable<number[]> {
  1606. var bInfo = this.getBoundingInfo();
  1607. const data = this._facetData;
  1608. var ox = Math.floor((x - bInfo.minimum.x * data.partitioningBBoxRatio) * data.subDiv.X * data.partitioningBBoxRatio / data.bbSize.x);
  1609. var oy = Math.floor((y - bInfo.minimum.y * data.partitioningBBoxRatio) * data.subDiv.Y * data.partitioningBBoxRatio / data.bbSize.y);
  1610. var oz = Math.floor((z - bInfo.minimum.z * data.partitioningBBoxRatio) * data.subDiv.Z * data.partitioningBBoxRatio / data.bbSize.z);
  1611. if (ox < 0 || ox > data.subDiv.max || oy < 0 || oy > data.subDiv.max || oz < 0 || oz > data.subDiv.max) {
  1612. return null;
  1613. }
  1614. return data.facetPartitioning[ox + data.subDiv.max * oy + data.subDiv.max * data.subDiv.max * oz];
  1615. }
  1616. /**
  1617. * Returns the closest mesh facet index at (x,y,z) World coordinates, null if not found
  1618. * @param projected sets as the (x,y,z) world projection on the facet
  1619. * @param checkFace if true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned
  1620. * @param facing if facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position. If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position
  1621. * @param x defines x coordinate
  1622. * @param y defines y coordinate
  1623. * @param z defines z coordinate
  1624. * @returns the face index if found (or null instead)
  1625. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1626. */
  1627. public getClosestFacetAtCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): Nullable<number> {
  1628. var world = this.getWorldMatrix();
  1629. var invMat = Tmp.Matrix[5];
  1630. world.invertToRef(invMat);
  1631. var invVect = Tmp.Vector3[8];
  1632. Vector3.TransformCoordinatesFromFloatsToRef(x, y, z, invMat, invVect); // transform (x,y,z) to coordinates in the mesh local space
  1633. var closest = this.getClosestFacetAtLocalCoordinates(invVect.x, invVect.y, invVect.z, projected, checkFace, facing);
  1634. if (projected) {
  1635. // tranform the local computed projected vector to world coordinates
  1636. Vector3.TransformCoordinatesFromFloatsToRef(projected.x, projected.y, projected.z, world, projected);
  1637. }
  1638. return closest;
  1639. }
  1640. /**
  1641. * Returns the closest mesh facet index at (x,y,z) local coordinates, null if not found
  1642. * @param projected sets as the (x,y,z) local projection on the facet
  1643. * @param checkFace if true (default false), only the facet "facing" to (x,y,z) or only the ones "turning their backs", according to the parameter "facing" are returned
  1644. * @param facing if facing and checkFace are true, only the facet "facing" to (x, y, z) are returned : positive dot (x, y, z) * facet position. If facing si false and checkFace is true, only the facet "turning their backs" to (x, y, z) are returned : negative dot (x, y, z) * facet position
  1645. * @param x defines x coordinate
  1646. * @param y defines y coordinate
  1647. * @param z defines z coordinate
  1648. * @returns the face index if found (or null instead)
  1649. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1650. */
  1651. public getClosestFacetAtLocalCoordinates(x: number, y: number, z: number, projected?: Vector3, checkFace: boolean = false, facing: boolean = true): Nullable<number> {
  1652. var closest = null;
  1653. var tmpx = 0.0;
  1654. var tmpy = 0.0;
  1655. var tmpz = 0.0;
  1656. var d = 0.0; // tmp dot facet normal * facet position
  1657. var t0 = 0.0;
  1658. var projx = 0.0;
  1659. var projy = 0.0;
  1660. var projz = 0.0;
  1661. // Get all the facets in the same partitioning block than (x, y, z)
  1662. var facetPositions = this.getFacetLocalPositions();
  1663. var facetNormals = this.getFacetLocalNormals();
  1664. var facetsInBlock = this.getFacetsAtLocalCoordinates(x, y, z);
  1665. if (!facetsInBlock) {
  1666. return null;
  1667. }
  1668. // Get the closest facet to (x, y, z)
  1669. var shortest = Number.MAX_VALUE; // init distance vars
  1670. var tmpDistance = shortest;
  1671. var fib; // current facet in the block
  1672. var norm; // current facet normal
  1673. var p0; // current facet barycenter position
  1674. // loop on all the facets in the current partitioning block
  1675. for (var idx = 0; idx < facetsInBlock.length; idx++) {
  1676. fib = facetsInBlock[idx];
  1677. norm = facetNormals[fib];
  1678. p0 = facetPositions[fib];
  1679. d = (x - p0.x) * norm.x + (y - p0.y) * norm.y + (z - p0.z) * norm.z;
  1680. if (!checkFace || (checkFace && facing && d >= 0.0) || (checkFace && !facing && d <= 0.0)) {
  1681. // compute (x,y,z) projection on the facet = (projx, projy, projz)
  1682. d = norm.x * p0.x + norm.y * p0.y + norm.z * p0.z;
  1683. t0 = -(norm.x * x + norm.y * y + norm.z * z - d) / (norm.x * norm.x + norm.y * norm.y + norm.z * norm.z);
  1684. projx = x + norm.x * t0;
  1685. projy = y + norm.y * t0;
  1686. projz = z + norm.z * t0;
  1687. tmpx = projx - x;
  1688. tmpy = projy - y;
  1689. tmpz = projz - z;
  1690. tmpDistance = tmpx * tmpx + tmpy * tmpy + tmpz * tmpz; // compute length between (x, y, z) and its projection on the facet
  1691. if (tmpDistance < shortest) { // just keep the closest facet to (x, y, z)
  1692. shortest = tmpDistance;
  1693. closest = fib;
  1694. if (projected) {
  1695. projected.x = projx;
  1696. projected.y = projy;
  1697. projected.z = projz;
  1698. }
  1699. }
  1700. }
  1701. }
  1702. return closest;
  1703. }
  1704. /**
  1705. * Returns the object "parameter" set with all the expected parameters for facetData computation by ComputeNormals()
  1706. * @returns the parameters
  1707. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1708. */
  1709. public getFacetDataParameters(): any {
  1710. return this._facetData.facetParameters;
  1711. }
  1712. /**
  1713. * Disables the feature FacetData and frees the related memory
  1714. * @returns the current mesh
  1715. * @see http://doc.babylonjs.com/how_to/how_to_use_facetdata
  1716. */
  1717. public disableFacetData(): AbstractMesh {
  1718. if (this._facetData.facetDataEnabled) {
  1719. this._facetData.facetDataEnabled = false;
  1720. this._facetData.facetPositions = new Array<Vector3>();
  1721. this._facetData.facetNormals = new Array<Vector3>();
  1722. this._facetData.facetPartitioning = new Array<number[]>();
  1723. this._facetData.facetParameters = null;
  1724. this._facetData.depthSortedIndices = new Uint32Array(0);
  1725. }
  1726. return this;
  1727. }
  1728. /**
  1729. * Updates the AbstractMesh indices array
  1730. * @param indices defines the data source
  1731. * @returns the current mesh
  1732. */
  1733. public updateIndices(indices: IndicesArray): AbstractMesh {
  1734. return this;
  1735. }
  1736. /**
  1737. * Creates new normals data for the mesh
  1738. * @param updatable defines if the normal vertex buffer must be flagged as updatable
  1739. * @returns the current mesh
  1740. */
  1741. public createNormals(updatable: boolean): AbstractMesh {
  1742. var positions = this.getVerticesData(VertexBuffer.PositionKind);
  1743. var indices = this.getIndices();
  1744. var normals: FloatArray;
  1745. if (this.isVerticesDataPresent(VertexBuffer.NormalKind)) {
  1746. normals = (<FloatArray>this.getVerticesData(VertexBuffer.NormalKind));
  1747. } else {
  1748. normals = [];
  1749. }
  1750. VertexData.ComputeNormals(positions, indices, normals, { useRightHandedSystem: this.getScene().useRightHandedSystem });
  1751. this.setVerticesData(VertexBuffer.NormalKind, normals, updatable);
  1752. return this;
  1753. }
  1754. /**
  1755. * Align the mesh with a normal
  1756. * @param normal defines the normal to use
  1757. * @param upDirection can be used to redefined the up vector to use (will use the (0, 1, 0) by default)
  1758. * @returns the current mesh
  1759. */
  1760. public alignWithNormal(normal: Vector3, upDirection?: Vector3): AbstractMesh {
  1761. if (!upDirection) {
  1762. upDirection = Axis.Y;
  1763. }
  1764. var axisX = Tmp.Vector3[0];
  1765. var axisZ = Tmp.Vector3[1];
  1766. Vector3.CrossToRef(upDirection, normal, axisZ);
  1767. Vector3.CrossToRef(normal, axisZ, axisX);
  1768. if (this.rotationQuaternion) {
  1769. Quaternion.RotationQuaternionFromAxisToRef(axisX, normal, axisZ, this.rotationQuaternion);
  1770. } else {
  1771. Vector3.RotationFromAxisToRef(axisX, normal, axisZ, this.rotation);
  1772. }
  1773. return this;
  1774. }
  1775. /** @hidden */
  1776. public _checkOcclusionQuery(): boolean { // Will be replaced by correct code if Occlusion queries are referenced
  1777. return false;
  1778. }
  1779. }