particleSystemPropertyGridComponent.tsx 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import * as React from "react";
  2. import { Observable } from "babylonjs/Misc/observable";
  3. import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
  4. import { LineContainerComponent } from "../../../lineContainerComponent";
  5. import { TextLineComponent } from "../../../lines/textLineComponent";
  6. import { LockObject } from "../lockObject";
  7. import { GlobalState } from '../../../../globalState';
  8. import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
  9. import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
  10. import { FloatLineComponent } from '../../../lines/floatLineComponent';
  11. import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
  12. import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
  13. import { OptionsLineComponent } from '../../../lines/optionsLineComponent';
  14. import { ParticleSystem } from 'babylonjs/Particles/particleSystem';
  15. import { Color4LineComponent } from '../../../lines/color4LineComponent';
  16. import { Vector3LineComponent } from '../../../lines/vector3LineComponent';
  17. import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
  18. import { SliderLineComponent } from '../../../lines/sliderLineComponent';
  19. import { BoxParticleEmitter } from 'babylonjs/Particles/EmitterTypes/boxParticleEmitter';
  20. import { ConeParticleEmitter } from 'babylonjs/Particles/EmitterTypes/coneParticleEmitter';
  21. import { CylinderParticleEmitter } from 'babylonjs/Particles/EmitterTypes/cylinderParticleEmitter';
  22. import { HemisphericParticleEmitter } from 'babylonjs/Particles/EmitterTypes/hemisphericParticleEmitter';
  23. import { PointParticleEmitter } from 'babylonjs/Particles/EmitterTypes/pointParticleEmitter';
  24. import { SphereParticleEmitter } from 'babylonjs/Particles/EmitterTypes/sphereParticleEmitter';
  25. import { BoxEmitterGridComponent } from './boxEmitterGridComponent';
  26. import { ConeEmitterGridComponent } from './coneEmitterGridComponent';
  27. import { CylinderEmitterGridComponent } from './cylinderEmitterGridComponent';
  28. import { HemisphericEmitterGridComponent } from './hemisphericEmitterGridComponent';
  29. import { PointEmitterGridComponent } from './pointEmitterGridComponent';
  30. import { SphereEmitterGridComponent } from './sphereEmitterGridComponent';
  31. import { Vector3 } from 'babylonjs/Maths/math.vector';
  32. import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
  33. import { MeshParticleEmitter } from 'babylonjs/Particles/EmitterTypes/meshParticleEmitter';
  34. import { MeshEmitterGridComponent } from './meshEmitterGridComponent';
  35. import { FactorGradientGridComponent } from './factorGradientGridComponent';
  36. interface IParticleSystemPropertyGridComponentProps {
  37. globalState: GlobalState;
  38. system: IParticleSystem,
  39. lockObject: LockObject,
  40. onSelectionChangedObservable?: Observable<any>,
  41. onPropertyChangedObservable?: Observable<PropertyChangedEvent>
  42. }
  43. export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
  44. constructor(props: IParticleSystemPropertyGridComponentProps) {
  45. super(props);
  46. }
  47. renderEmitter() {
  48. const system = this.props.system;
  49. const replaySource = "particlesystem.particleEmitterType";
  50. switch(system.particleEmitterType?.getClassName()) {
  51. case "BoxParticleEmitter":
  52. return (
  53. <BoxEmitterGridComponent replaySourceReplacement={replaySource}
  54. globalState={this.props.globalState} emitter={system.particleEmitterType as BoxParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  55. );
  56. case "ConeParticleEmitter":
  57. return (
  58. <ConeEmitterGridComponent replaySourceReplacement={replaySource}
  59. globalState={this.props.globalState} emitter={system.particleEmitterType as ConeParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  60. );
  61. case "CylinderParticleEmitter":
  62. return (
  63. <CylinderEmitterGridComponent replaySourceReplacement={replaySource}
  64. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as CylinderParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  65. );
  66. case "HemisphericParticleEmitter":
  67. return (
  68. <HemisphericEmitterGridComponent replaySourceReplacement={replaySource}
  69. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as HemisphericParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  70. );
  71. case "MeshParticleEmitter":
  72. return (
  73. <MeshEmitterGridComponent replaySourceReplacement={replaySource}
  74. lockObject={this.props.lockObject} scene={system.getScene()} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  75. );
  76. case "PointParticleEmitter":
  77. return (
  78. <PointEmitterGridComponent replaySourceReplacement={replaySource}
  79. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as PointParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  80. );
  81. case "SphereParticleEmitter":
  82. return (
  83. <SphereEmitterGridComponent replaySourceReplacement={replaySource}
  84. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as SphereParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  85. );
  86. }
  87. return null;
  88. }
  89. raiseOnPropertyChanged(property: string, newValue: any, previousValue: any) {
  90. if (!this.props.onPropertyChangedObservable) {
  91. return;
  92. }
  93. const system = this.props.system;
  94. this.props.onPropertyChangedObservable.notifyObservers({
  95. object: system,
  96. property: property,
  97. value: newValue,
  98. initialValue: previousValue
  99. });
  100. }
  101. render() {
  102. const system = this.props.system;
  103. var blendModeOptions = [
  104. { label: "Add", value: ParticleSystem.BLENDMODE_ADD },
  105. { label: "Multiply", value: ParticleSystem.BLENDMODE_MULTIPLY },
  106. { label: "Multiply Add", value: ParticleSystem.BLENDMODE_MULTIPLYADD },
  107. { label: "OneOne", value: ParticleSystem.BLENDMODE_ONEONE },
  108. { label: "Standard", value: ParticleSystem.BLENDMODE_STANDARD },
  109. ];
  110. var particleEmitterTypeOptions = [
  111. { label: "Box", value: 0 },
  112. { label: "Cone", value: 1 },
  113. { label: "Cylinder", value: 2 },
  114. { label: "Hemispheric", value: 3 },
  115. { label: "Mesh", value: 4 },
  116. { label: "Point", value: 5 },
  117. { label: "Sphere", value: 6 },
  118. ];
  119. var meshEmitters = this.props.system.getScene().meshes.filter(m => !!m.name);
  120. var emitterOptions = [
  121. { label: "None", value: -1 },
  122. { label: "Vector3", value: 0 }
  123. ];
  124. meshEmitters.sort((a, b) => a.name.localeCompare(b.name));
  125. emitterOptions.push(...meshEmitters.map((v, i) => {
  126. return {label: v.name, value: i + 1}
  127. }));
  128. return (
  129. <div className="pane">
  130. <CustomPropertyGridComponent globalState={this.props.globalState} target={system}
  131. lockObject={this.props.lockObject}
  132. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  133. <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
  134. <TextLineComponent label="ID" value={system.id} />
  135. <TextLineComponent label="Class" value={system.getClassName()} />
  136. <TextLineComponent label="Capacity" value={system.getCapacity().toString()} />
  137. <TextureLinkLineComponent label="Texture" texture={system.particleTexture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
  138. <OptionsLineComponent label="Blend mode" options={blendModeOptions} target={system} propertyName="blendMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  139. <Vector3LineComponent label="Gravity" target={system} propertyName="gravity"
  140. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  141. <CheckBoxLineComponent label="Is billboard" target={system} propertyName="isBillboardBased" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  142. <CheckBoxLineComponent label="Is local" target={system} propertyName="isLocal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  143. <SliderLineComponent label="Update speed" target={system} propertyName="updateSpeed" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  144. </LineContainerComponent>
  145. <LineContainerComponent globalState={this.props.globalState} title="OPTIONS">
  146. <ButtonLineComponent label={system.isStarted() ? "Stop" : "Start"} onClick={() => {
  147. if (system.isStarted()) {
  148. system.stop();
  149. } else {
  150. system.start();
  151. }
  152. }} />
  153. </LineContainerComponent>
  154. <LineContainerComponent globalState={this.props.globalState} title="EMITTER">
  155. <OptionsLineComponent
  156. label="Emitter"
  157. options={emitterOptions}
  158. target={system}
  159. propertyName="emitter"
  160. noDirectUpdate={true}
  161. onSelect={(value: number) => {
  162. switch(value) {
  163. case -1:
  164. this.raiseOnPropertyChanged("emitter", null, system.emitter);
  165. system.emitter = null;
  166. break;
  167. case 0:
  168. this.raiseOnPropertyChanged("emitter", Vector3.Zero(), system.emitter);
  169. system.emitter = Vector3.Zero();
  170. break;
  171. default:
  172. this.raiseOnPropertyChanged("emitter", meshEmitters[value - 1], system.emitter);
  173. system.emitter = meshEmitters[value - 1];
  174. }
  175. this.forceUpdate();
  176. }}
  177. extractValue={() => {
  178. if (!system.emitter) {
  179. return -1;
  180. }
  181. if ((system.emitter as Vector3).x !== undefined) {
  182. return 0;
  183. }
  184. let meshIndex = meshEmitters.indexOf(system.emitter as AbstractMesh)
  185. if (meshIndex > -1) {
  186. return meshIndex + 1;
  187. }
  188. return -1;
  189. }}
  190. />
  191. {
  192. system.emitter && ((system.emitter as Vector3).x === undefined) &&
  193. <TextLineComponent label="Link to emitter" value={(system.emitter as AbstractMesh).name} onLink={() => this.props.globalState.onSelectionChangedObservable.notifyObservers(system.emitter)}/>
  194. }
  195. {
  196. system.emitter && ((system.emitter as Vector3).x !== undefined) &&
  197. <Vector3LineComponent label="Position" target={system} propertyName="emitter" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  198. }
  199. <OptionsLineComponent
  200. label="Type"
  201. options={particleEmitterTypeOptions}
  202. target={system}
  203. propertyName="particleEmitterType"
  204. noDirectUpdate={true}
  205. onSelect={(value: number) => {
  206. const currentType = system.particleEmitterType;
  207. switch(value) {
  208. case 0:
  209. system.particleEmitterType = new BoxParticleEmitter();
  210. break;
  211. case 1:
  212. system.particleEmitterType = new ConeParticleEmitter();
  213. break;
  214. case 2:
  215. system.particleEmitterType = new CylinderParticleEmitter();
  216. break;
  217. case 3:
  218. system.particleEmitterType = new HemisphericParticleEmitter();
  219. break;
  220. case 4:
  221. system.particleEmitterType = new MeshParticleEmitter();
  222. break;
  223. case 5:
  224. system.particleEmitterType = new PointParticleEmitter();
  225. break;
  226. case 6:
  227. system.particleEmitterType = new SphereParticleEmitter();
  228. break;
  229. }
  230. this.raiseOnPropertyChanged("particleEmitterType", system.particleEmitterType, currentType)
  231. this.forceUpdate();
  232. }}
  233. extractValue={() => {
  234. switch(system.particleEmitterType?.getClassName()) {
  235. case "BoxParticleEmitter":
  236. return 0;
  237. case "ConeParticleEmitter":
  238. return 1;
  239. case "CylinderParticleEmitter":
  240. return 2;
  241. case "HemisphericParticleEmitter":
  242. return 3;
  243. case "MeshParticleEmitter":
  244. return 4;
  245. case "PointParticleEmitter":
  246. return 5;
  247. case "SphereParticleEmitter":
  248. return 6;
  249. }
  250. return 0;
  251. }}/>
  252. {
  253. this.renderEmitter()
  254. }
  255. </LineContainerComponent>
  256. <LineContainerComponent globalState={this.props.globalState} title="EMISSION">
  257. <FloatLineComponent lockObject={this.props.lockObject} label="Rate" target={system} propertyName="emitRate" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  258. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getEmitRateGradients()!}
  259. label="Velocity gradients"
  260. docLink="https://doc.babylonjs.com/babylon101/particles#emit-rate-over-time"
  261. onCreateRequired={() => {
  262. system.addEmitRateGradient(0, 50, 50);
  263. }}
  264. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  265. <FloatLineComponent lockObject={this.props.lockObject} label="Min emit power" target={system} propertyName="minEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  266. <FloatLineComponent lockObject={this.props.lockObject} label="Max emit power" target={system} propertyName="maxEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  267. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getVelocityGradients()!}
  268. label="Velocity gradients"
  269. docLink="https://doc.babylonjs.com/babylon101/particles#velocity-over-time"
  270. onCreateRequired={() => {
  271. system.addVelocityGradient(0, 0.1, 0.1);
  272. }}
  273. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  274. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getLimitVelocityGradients()!}
  275. label="Limit velocity gradients"
  276. docLink="https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time"
  277. onCreateRequired={() => {
  278. system.addLimitVelocityGradient(0, 0.1, 0.1);
  279. }}
  280. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  281. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getDragGradients()!}
  282. label="Drag gradients"
  283. docLink="https://doc.babylonjs.com/babylon101/particles#drag-factor"
  284. onCreateRequired={() => {
  285. system.addDragGradient(0, 0.1, 0.1);
  286. }}
  287. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  288. </LineContainerComponent>
  289. <LineContainerComponent globalState={this.props.globalState} title="SIZE">
  290. <FloatLineComponent lockObject={this.props.lockObject} label="Min size" target={system} propertyName="minSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  291. <FloatLineComponent lockObject={this.props.lockObject} label="Max size" target={system} propertyName="maxSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  292. <FloatLineComponent lockObject={this.props.lockObject} label="Min scale X" target={system} propertyName="minScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  293. <FloatLineComponent lockObject={this.props.lockObject} label="Max scale X" target={system} propertyName="maxScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  294. <FloatLineComponent lockObject={this.props.lockObject} label="Min scale Y" target={system} propertyName="minScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  295. <FloatLineComponent lockObject={this.props.lockObject} label="Max scale Y" target={system} propertyName="maxScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  296. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getStartSizeGradients()!}
  297. label="Start size gradients"
  298. docLink="https://doc.babylonjs.com/babylon101/particles#start-size-over-time"
  299. onCreateRequired={() => {
  300. system.addStartSizeGradient(0, 1, 1);
  301. }}
  302. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  303. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getSizeGradients()!}
  304. label="Size gradients"
  305. docLink="https://doc.babylonjs.com/babylon101/particles#size"
  306. onCreateRequired={() => {
  307. system.addSizeGradient(0, 1, 1);
  308. }}
  309. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  310. </LineContainerComponent>
  311. <LineContainerComponent globalState={this.props.globalState} title="LIFETIME">
  312. <FloatLineComponent lockObject={this.props.lockObject} label="Min lifetime" target={system} propertyName="minLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  313. <FloatLineComponent lockObject={this.props.lockObject} label="Max lifetime" target={system} propertyName="maxLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  314. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getLifeTimeGradients()!}
  315. label="Lifetime gradients"
  316. docLink="https://doc.babylonjs.com/babylon101/particles#lifetime"
  317. onCreateRequired={() => {
  318. system.addLifeTimeGradient(0, 1, 1);
  319. }}
  320. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  321. </LineContainerComponent>
  322. <LineContainerComponent globalState={this.props.globalState} title="COLORS">
  323. <Color4LineComponent label="Color 1" target={system} propertyName="color1"
  324. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  325. <Color4LineComponent label="Color 2" target={system} propertyName="color2"
  326. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  327. <Color4LineComponent label="Color dead" target={system} propertyName="colorDead"
  328. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  329. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getColorRemapGradients()!}
  330. label="Color remap gradients"
  331. docLink="https://doc.babylonjs.com/babylon101/particles#ramp-gradients"
  332. onCreateRequired={() => {
  333. system.addColorRemapGradient(0, 1, 1);
  334. }}
  335. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  336. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getAlphaRemapGradients()!}
  337. label="Alpha remap gradients"
  338. docLink="https://doc.babylonjs.com/babylon101/particles#ramp-gradients"
  339. onCreateRequired={() => {
  340. system.addAlphaRemapGradient(0, 1, 1);
  341. }}
  342. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  343. </LineContainerComponent>
  344. <LineContainerComponent globalState={this.props.globalState} title="ROTATION">
  345. <FloatLineComponent lockObject={this.props.lockObject} label="Min angular speed" target={system} propertyName="minAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  346. <FloatLineComponent lockObject={this.props.lockObject} label="Max angular speed" target={system} propertyName="maxAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  347. <FloatLineComponent lockObject={this.props.lockObject} label="Min initial rotation" target={system} propertyName="minInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  348. <FloatLineComponent lockObject={this.props.lockObject} label="Max initial rotation" target={system} propertyName="maxInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  349. <FactorGradientGridComponent globalState={this.props.globalState} gradients={system.getAngularSpeedGradients()!}
  350. label="Angular speed gradients"
  351. docLink="hhttps://doc.babylonjs.com/babylon101/particles#rotation"
  352. onCreateRequired={() => {
  353. system.addAngularSpeedGradient(0, 0.1, 0.1);
  354. }}
  355. lockObject={this.props.lockObject} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  356. </LineContainerComponent>
  357. </div>
  358. );
  359. }
  360. }