coneParticleEmitter.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { DeepCopier } from "../../Misc/deepCopier";
  2. import { Vector3, Matrix } from "../../Maths/math.vector";
  3. import { Scalar } from "../../Maths/math.scalar";
  4. import { Effect } from "../../Materials/effect";
  5. import { Particle } from "../../Particles/particle";
  6. import { IParticleEmitterType } from "./IParticleEmitterType";
  7. /**
  8. * Particle emitter emitting particles from the inside of a cone.
  9. * It emits the particles alongside the cone volume from the base to the particle.
  10. * The emission direction might be randomized.
  11. */
  12. export class ConeParticleEmitter implements IParticleEmitterType {
  13. private _radius: number;
  14. private _angle: number;
  15. private _height: number;
  16. /**
  17. * Gets or sets a value indicating where on the radius the start position should be picked (1 = everywhere, 0 = only surface)
  18. */
  19. public radiusRange = 1;
  20. /**
  21. * Gets or sets a value indicating where on the height the start position should be picked (1 = everywhere, 0 = only surface)
  22. */
  23. public heightRange = 1;
  24. /**
  25. * Gets or sets a value indicating if all the particles should be emitted from the spawn point only (the base of the cone)
  26. */
  27. public emitFromSpawnPointOnly = false;
  28. /**
  29. * Gets or sets the radius of the emission cone
  30. */
  31. public get radius(): number {
  32. return this._radius;
  33. }
  34. public set radius(value: number) {
  35. this._radius = value;
  36. this._buildHeight();
  37. }
  38. /**
  39. * Gets or sets the angle of the emission cone
  40. */
  41. public get angle(): number {
  42. return this._angle;
  43. }
  44. public set angle(value: number) {
  45. this._angle = value;
  46. this._buildHeight();
  47. }
  48. private _buildHeight() {
  49. if (this._angle !== 0) {
  50. this._height = this._radius / Math.tan(this._angle / 2);
  51. }
  52. else {
  53. this._height = 1;
  54. }
  55. }
  56. /**
  57. * Creates a new instance ConeParticleEmitter
  58. * @param radius the radius of the emission cone (1 by default)
  59. * @param angle the cone base angle (PI by default)
  60. * @param directionRandomizer defines how much to randomize the particle direction [0-1] (default is 0)
  61. */
  62. constructor(radius = 1, angle = Math.PI,
  63. /** defines how much to randomize the particle direction [0-1] (default is 0) */
  64. public directionRandomizer = 0) {
  65. this.angle = angle;
  66. this.radius = radius;
  67. }
  68. /**
  69. * Called by the particle System when the direction is computed for the created particle.
  70. * @param worldMatrix is the world matrix of the particle system
  71. * @param directionToUpdate is the direction vector to update with the result
  72. * @param particle is the particle we are computed the direction for
  73. */
  74. public startDirectionFunction(worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
  75. if (Math.abs(Math.cos(this._angle)) === 1.0) {
  76. Vector3.TransformNormalFromFloatsToRef(0, 1.0, 0, worldMatrix, directionToUpdate);
  77. }
  78. else {
  79. // measure the direction Vector from the emitter to the particle.
  80. var direction = particle.position.subtract(worldMatrix.getTranslation()).normalize();
  81. var randX = Scalar.RandomRange(0, this.directionRandomizer);
  82. var randY = Scalar.RandomRange(0, this.directionRandomizer);
  83. var randZ = Scalar.RandomRange(0, this.directionRandomizer);
  84. direction.x += randX;
  85. direction.y += randY;
  86. direction.z += randZ;
  87. direction.normalize();
  88. Vector3.TransformNormalFromFloatsToRef(direction.x, direction.y, direction.z, worldMatrix, directionToUpdate);
  89. }
  90. }
  91. /**
  92. * Called by the particle System when the position is computed for the created particle.
  93. * @param worldMatrix is the world matrix of the particle system
  94. * @param positionToUpdate is the position vector to update with the result
  95. * @param particle is the particle we are computed the position for
  96. */
  97. startPositionFunction(worldMatrix: Matrix, positionToUpdate: Vector3, particle: Particle): void {
  98. var s = Scalar.RandomRange(0, Math.PI * 2);
  99. var h: number;
  100. if (!this.emitFromSpawnPointOnly) {
  101. h = Scalar.RandomRange(0, this.heightRange);
  102. // Better distribution in a cone at normal angles.
  103. h = 1 - h * h;
  104. } else {
  105. h = 0.0001;
  106. }
  107. var radius = this._radius - Scalar.RandomRange(0, this._radius * this.radiusRange);
  108. radius = radius * h;
  109. var randX = radius * Math.sin(s);
  110. var randZ = radius * Math.cos(s);
  111. var randY = h * this._height;
  112. Vector3.TransformCoordinatesFromFloatsToRef(randX, randY, randZ, worldMatrix, positionToUpdate);
  113. }
  114. /**
  115. * Clones the current emitter and returns a copy of it
  116. * @returns the new emitter
  117. */
  118. public clone(): ConeParticleEmitter {
  119. let newOne = new ConeParticleEmitter(this._radius, this._angle, this.directionRandomizer);
  120. DeepCopier.DeepCopy(this, newOne);
  121. return newOne;
  122. }
  123. /**
  124. * Called by the GPUParticleSystem to setup the update shader
  125. * @param effect defines the update shader
  126. */
  127. public applyToShader(effect: Effect): void {
  128. effect.setFloat2("radius", this._radius, this.radiusRange);
  129. effect.setFloat("coneAngle", this._angle);
  130. effect.setFloat2("height", this._height, this.heightRange);
  131. effect.setFloat("directionRandomizer", this.directionRandomizer);
  132. }
  133. /**
  134. * Returns a string to use to update the GPU particles update shader
  135. * @returns a string containng the defines string
  136. */
  137. public getEffectDefines(): string {
  138. let defines = "#define CONEEMITTER";
  139. if (this.emitFromSpawnPointOnly) {
  140. defines += "\n#define CONEEMITTERSPAWNPOINT";
  141. }
  142. return defines;
  143. }
  144. /**
  145. * Returns the string "ConeParticleEmitter"
  146. * @returns a string containing the class name
  147. */
  148. public getClassName(): string {
  149. return "ConeParticleEmitter";
  150. }
  151. /**
  152. * Serializes the particle system to a JSON object.
  153. * @returns the JSON object
  154. */
  155. public serialize(): any {
  156. var serializationObject: any = {};
  157. serializationObject.type = this.getClassName();
  158. serializationObject.radius = this._radius;
  159. serializationObject.angle = this._angle;
  160. serializationObject.directionRandomizer = this.directionRandomizer;
  161. serializationObject.radiusRange = this.radiusRange;
  162. serializationObject.heightRange = this.heightRange;
  163. serializationObject.emitFromSpawnPointOnly = this.emitFromSpawnPointOnly;
  164. return serializationObject;
  165. }
  166. /**
  167. * Parse properties from a JSON object
  168. * @param serializationObject defines the JSON object
  169. */
  170. public parse(serializationObject: any): void {
  171. this.radius = serializationObject.radius;
  172. this.angle = serializationObject.angle;
  173. this.directionRandomizer = serializationObject.directionRandomizer;
  174. this.radiusRange = serializationObject.radiusRange;
  175. this.heightRange = serializationObject.heightRange;
  176. this.emitFromSpawnPointOnly = serializationObject.emitFromSpawnPointOnly;
  177. }
  178. }