abstractMesh.ts 83 KB

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