particleSystemPropertyGridComponent.tsx 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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 "../../../../../sharedUiComponents/lines/textLineComponent";
  6. import { LockObject } from "../../../../../sharedUiComponents/tabs/propertyGrids/lockObject";
  7. import { GlobalState } from '../../../../globalState';
  8. import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
  9. import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
  10. import { FloatLineComponent } from '../../../../../sharedUiComponents/lines/floatLineComponent';
  11. import { ButtonLineComponent } from '../../../../../sharedUiComponents/lines/buttonLineComponent';
  12. import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
  13. import { OptionsLineComponent } from '../../../../../sharedUiComponents/lines/optionsLineComponent';
  14. import { ParticleSystem } from 'babylonjs/Particles/particleSystem';
  15. import { Vector3LineComponent } from '../../../../../sharedUiComponents/lines/vector3LineComponent';
  16. import { CheckBoxLineComponent } from '../../../../../sharedUiComponents/lines/checkBoxLineComponent';
  17. import { SliderLineComponent } from '../../../../../sharedUiComponents/lines/sliderLineComponent';
  18. import { BoxParticleEmitter } from 'babylonjs/Particles/EmitterTypes/boxParticleEmitter';
  19. import { ConeParticleEmitter } from 'babylonjs/Particles/EmitterTypes/coneParticleEmitter';
  20. import { CylinderParticleEmitter } from 'babylonjs/Particles/EmitterTypes/cylinderParticleEmitter';
  21. import { HemisphericParticleEmitter } from 'babylonjs/Particles/EmitterTypes/hemisphericParticleEmitter';
  22. import { PointParticleEmitter } from 'babylonjs/Particles/EmitterTypes/pointParticleEmitter';
  23. import { SphereParticleEmitter } from 'babylonjs/Particles/EmitterTypes/sphereParticleEmitter';
  24. import { BoxEmitterGridComponent } from './boxEmitterGridComponent';
  25. import { ConeEmitterGridComponent } from './coneEmitterGridComponent';
  26. import { CylinderEmitterGridComponent } from './cylinderEmitterGridComponent';
  27. import { HemisphericEmitterGridComponent } from './hemisphericEmitterGridComponent';
  28. import { PointEmitterGridComponent } from './pointEmitterGridComponent';
  29. import { SphereEmitterGridComponent } from './sphereEmitterGridComponent';
  30. import { Vector3 } from 'babylonjs/Maths/math.vector';
  31. import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
  32. import { MeshParticleEmitter } from 'babylonjs/Particles/EmitterTypes/meshParticleEmitter';
  33. import { MeshEmitterGridComponent } from './meshEmitterGridComponent';
  34. import { ValueGradientGridComponent, GradientGridMode } from './valueGradientGridComponent';
  35. import { Color3, Color4 } from 'babylonjs/Maths/math.color';
  36. import { GPUParticleSystem } from 'babylonjs/Particles/gpuParticleSystem';
  37. import { Tools } from 'babylonjs/Misc/tools';
  38. import { FileButtonLineComponent } from '../../../../../sharedUiComponents/lines/fileButtonLineComponent';
  39. import { TextInputLineComponent } from '../../../../../sharedUiComponents/lines/textInputLineComponent';
  40. import { ParticleHelper } from 'babylonjs/Particles/particleHelper';
  41. import { Color4LineComponent } from "../../../../../sharedUiComponents/lines/color4LineComponent";
  42. interface IParticleSystemPropertyGridComponentProps {
  43. globalState: GlobalState;
  44. system: IParticleSystem,
  45. lockObject: LockObject,
  46. onSelectionChangedObservable?: Observable<any>,
  47. onPropertyChangedObservable?: Observable<PropertyChangedEvent>
  48. }
  49. export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
  50. private _snippetUrl = "https://snippet.babylonjs.com";
  51. constructor(props: IParticleSystemPropertyGridComponentProps) {
  52. super(props);
  53. }
  54. renderEmitter() {
  55. const system = this.props.system;
  56. switch(system.particleEmitterType?.getClassName()) {
  57. case "BoxParticleEmitter":
  58. return (
  59. <BoxEmitterGridComponent
  60. globalState={this.props.globalState} emitter={system.particleEmitterType as BoxParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  61. );
  62. case "ConeParticleEmitter":
  63. return (
  64. <ConeEmitterGridComponent
  65. globalState={this.props.globalState} emitter={system.particleEmitterType as ConeParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  66. );
  67. case "CylinderParticleEmitter":
  68. return (
  69. <CylinderEmitterGridComponent
  70. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as CylinderParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  71. );
  72. case "HemisphericParticleEmitter":
  73. return (
  74. <HemisphericEmitterGridComponent
  75. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as HemisphericParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  76. );
  77. case "MeshParticleEmitter":
  78. return (
  79. <MeshEmitterGridComponent
  80. lockObject={this.props.lockObject} scene={system.getScene()!} globalState={this.props.globalState} emitter={system.particleEmitterType as MeshParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  81. );
  82. case "PointParticleEmitter":
  83. return (
  84. <PointEmitterGridComponent
  85. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as PointParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  86. );
  87. case "SphereParticleEmitter":
  88. return (
  89. <SphereEmitterGridComponent
  90. lockObject={this.props.lockObject} globalState={this.props.globalState} emitter={system.particleEmitterType as SphereParticleEmitter} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  91. );
  92. }
  93. return null;
  94. }
  95. raiseOnPropertyChanged(property: string, newValue: any, previousValue: any) {
  96. if (!this.props.onPropertyChangedObservable) {
  97. return;
  98. }
  99. const system = this.props.system;
  100. this.props.onPropertyChangedObservable.notifyObservers({
  101. object: system,
  102. property: property,
  103. value: newValue,
  104. initialValue: previousValue
  105. });
  106. }
  107. renderControls() {
  108. const system = this.props.system;
  109. if (system instanceof GPUParticleSystem) {
  110. let isStarted = system.isStarted() && !system.isStopped();
  111. return (
  112. <ButtonLineComponent label={isStarted ? "Stop" : "Start"} onClick={() => {
  113. if (isStarted) {
  114. system.stop();
  115. system.reset();
  116. } else {
  117. system.start();
  118. }
  119. this.forceUpdate();
  120. }} />
  121. );
  122. }
  123. let isStarted = system.isStarted();
  124. return (
  125. <>
  126. {
  127. !system.isStopping() &&
  128. <ButtonLineComponent label={isStarted ? "Stop" : "Start"} onClick={() => {
  129. if (isStarted) {
  130. system.stop();
  131. } else {
  132. system.start();
  133. }
  134. this.forceUpdate();
  135. }} />
  136. }
  137. {
  138. system.isStopping() &&
  139. <TextLineComponent label="System is stoppping..." ignoreValue={true}/>
  140. }
  141. </>
  142. )
  143. }
  144. saveToFile() {
  145. const system = this.props.system;
  146. let content = JSON.stringify(system.serialize(true));
  147. Tools.Download(new Blob([content]), "particleSystem.json");
  148. }
  149. loadFromFile(file: File) {
  150. const system = this.props.system;
  151. const scene = system.getScene();
  152. if (!scene) {
  153. return;
  154. }
  155. Tools.ReadFile(file, (data) => {
  156. let decoder = new TextDecoder("utf-8");
  157. let jsonObject = JSON.parse(decoder.decode(data));
  158. let isGpu = system instanceof GPUParticleSystem;
  159. system.dispose();
  160. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  161. let newSystem = isGpu ? GPUParticleSystem.Parse(jsonObject, scene!, "") : ParticleSystem.Parse(jsonObject, scene!, "");
  162. this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
  163. }, undefined, true);
  164. }
  165. loadFromSnippet() {
  166. const system = this.props.system;
  167. const scene = system.getScene()!;
  168. let isGpu = system instanceof GPUParticleSystem;
  169. let snippedID = window.prompt("Please enter the snippet ID to use");
  170. if (!snippedID || !scene) {
  171. return;
  172. }
  173. system.dispose();
  174. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  175. ParticleHelper.CreateFromSnippetAsync(snippedID, scene, isGpu).then((newSystem) => {
  176. this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
  177. }).catch(err => {
  178. alert("Unable to load your particle system: " + err);
  179. });
  180. }
  181. saveToSnippet() {
  182. const system = this.props.system;
  183. let content = JSON.stringify(system.serialize(true));
  184. var xmlHttp = new XMLHttpRequest();
  185. xmlHttp.onreadystatechange = () => {
  186. if (xmlHttp.readyState == 4) {
  187. if (xmlHttp.status == 200) {
  188. var snippet = JSON.parse(xmlHttp.responseText);
  189. const oldId = system.snippetId || "_BLANK";
  190. system.snippetId = snippet.id;
  191. if (snippet.version && snippet.version != "0") {
  192. system.snippetId += "#" + snippet.version;
  193. }
  194. this.forceUpdate();
  195. if (navigator.clipboard) {
  196. navigator.clipboard.writeText(system.snippetId);
  197. }
  198. let windowAsAny = window as any;
  199. if (windowAsAny.Playground && oldId) {
  200. windowAsAny.Playground.onRequestCodeChangeObservable.notifyObservers({
  201. regex: new RegExp(`ParticleHelper.CreateFromSnippetAsync\\("${oldId}`, "g"),
  202. replace: `ParticleHelper.CreateFromSnippetAsync("${system.snippetId}`
  203. });
  204. }
  205. alert("Particle system saved with ID: " + system.snippetId + " (please note that the id was also saved to your clipboard)");
  206. }
  207. else {
  208. alert("Unable to save your particle system");
  209. }
  210. }
  211. }
  212. xmlHttp.open("POST", this._snippetUrl + (system.snippetId ? "/" + system.snippetId : ""), true);
  213. xmlHttp.setRequestHeader("Content-Type", "application/json");
  214. var dataToSend = {
  215. payload : JSON.stringify({
  216. particleSystem: content
  217. }),
  218. name: "",
  219. description: "",
  220. tags: ""
  221. };
  222. xmlHttp.send(JSON.stringify(dataToSend));
  223. }
  224. render() {
  225. const system = this.props.system;
  226. var blendModeOptions = [
  227. { label: "Add", value: ParticleSystem.BLENDMODE_ADD },
  228. { label: "Multiply", value: ParticleSystem.BLENDMODE_MULTIPLY },
  229. { label: "Multiply Add", value: ParticleSystem.BLENDMODE_MULTIPLYADD },
  230. { label: "OneOne", value: ParticleSystem.BLENDMODE_ONEONE },
  231. { label: "Standard", value: ParticleSystem.BLENDMODE_STANDARD },
  232. ];
  233. var particleEmitterTypeOptions = [
  234. { label: "Box", value: 0 },
  235. { label: "Cone", value: 1 },
  236. { label: "Cylinder", value: 2 },
  237. { label: "Hemispheric", value: 3 },
  238. { label: "Mesh", value: 4 },
  239. { label: "Point", value: 5 },
  240. { label: "Sphere", value: 6 },
  241. ];
  242. var meshEmitters = this.props.system.getScene()!.meshes.filter(m => !!m.name);
  243. var emitterOptions = [
  244. { label: "None", value: -1 },
  245. { label: "Vector3", value: 0 }
  246. ];
  247. meshEmitters.sort((a, b) => a.name.localeCompare(b.name));
  248. emitterOptions.push(...meshEmitters.map((v, i) => {
  249. return {label: v.name, value: i + 1}
  250. }));
  251. return (
  252. <div className="pane">
  253. <CustomPropertyGridComponent globalState={this.props.globalState} target={system}
  254. lockObject={this.props.lockObject}
  255. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  256. <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
  257. <TextLineComponent label="ID" value={system.id} />
  258. <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={system} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  259. <TextLineComponent label="Class" value={system.getClassName()} />
  260. <TextLineComponent label="Capacity" value={system.getCapacity().toString()} />
  261. <TextLineComponent label="Active count" value={system.getActiveCount().toString()} />
  262. <TextureLinkLineComponent label="Texture" texture={system.particleTexture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
  263. <OptionsLineComponent label="Blend mode" options={blendModeOptions} target={system} propertyName="blendMode" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  264. <Vector3LineComponent label="World offset" target={system} propertyName="worldOffset"
  265. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  266. <Vector3LineComponent label="Gravity" target={system} propertyName="gravity"
  267. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  268. <CheckBoxLineComponent label="Is billboard" target={system} propertyName="isBillboardBased" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  269. <CheckBoxLineComponent label="Is local" target={system} propertyName="isLocal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  270. <CheckBoxLineComponent label="Force depth write" target={system} propertyName="forceDepthWrite" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  271. <SliderLineComponent label="Update speed" target={system} propertyName="updateSpeed" minimum={0} maximum={0.1} decimalCount={3} step={0.001} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  272. </LineContainerComponent>
  273. <LineContainerComponent globalState={this.props.globalState} title="COMMANDS">
  274. {this.renderControls()}
  275. <ButtonLineComponent label={"Dispose"} onClick={() => {
  276. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  277. system.dispose();
  278. }} />
  279. </LineContainerComponent>
  280. <LineContainerComponent globalState={this.props.globalState} title="FILE">
  281. <FileButtonLineComponent label="Load" onClick={(file) => this.loadFromFile(file)} accept=".json" />
  282. <ButtonLineComponent label="Save" onClick={() => this.saveToFile()} />
  283. </LineContainerComponent>
  284. <LineContainerComponent globalState={this.props.globalState} title="SNIPPET">
  285. {
  286. system.snippetId &&
  287. <TextLineComponent label="Snippet ID" value={system.snippetId} />
  288. }
  289. <ButtonLineComponent label="Load from snippet server" onClick={() => this.loadFromSnippet()} />
  290. <ButtonLineComponent label="Save to snippet server" onClick={() => this.saveToSnippet()} />
  291. </LineContainerComponent>
  292. <LineContainerComponent globalState={this.props.globalState} title="EMITTER" closed={true}>
  293. <OptionsLineComponent
  294. label="Emitter"
  295. options={emitterOptions}
  296. target={system}
  297. propertyName="emitter"
  298. noDirectUpdate={true}
  299. onSelect={(value: number) => {
  300. switch(value) {
  301. case -1:
  302. this.raiseOnPropertyChanged("emitter", null, system.emitter);
  303. system.emitter = null;
  304. break;
  305. case 0:
  306. this.raiseOnPropertyChanged("emitter", Vector3.Zero(), system.emitter);
  307. system.emitter = Vector3.Zero();
  308. break;
  309. default:
  310. this.raiseOnPropertyChanged("emitter", meshEmitters[value - 1], system.emitter);
  311. system.emitter = meshEmitters[value - 1];
  312. }
  313. this.forceUpdate();
  314. }}
  315. extractValue={() => {
  316. if (!system.emitter) {
  317. return -1;
  318. }
  319. if ((system.emitter as Vector3).x !== undefined) {
  320. return 0;
  321. }
  322. let meshIndex = meshEmitters.indexOf(system.emitter as AbstractMesh)
  323. if (meshIndex > -1) {
  324. return meshIndex + 1;
  325. }
  326. return -1;
  327. }}
  328. />
  329. {
  330. system.emitter && ((system.emitter as Vector3).x === undefined) &&
  331. <TextLineComponent label="Link to emitter" value={(system.emitter as AbstractMesh).name} onLink={() => this.props.globalState.onSelectionChangedObservable.notifyObservers(system.emitter)}/>
  332. }
  333. {
  334. system.emitter && ((system.emitter as Vector3).x !== undefined) &&
  335. <Vector3LineComponent label="Position" target={system} propertyName="emitter" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
  336. }
  337. <OptionsLineComponent
  338. label="Type"
  339. options={particleEmitterTypeOptions}
  340. target={system}
  341. propertyName="particleEmitterType"
  342. noDirectUpdate={true}
  343. onSelect={(value: number) => {
  344. const currentType = system.particleEmitterType;
  345. switch(value) {
  346. case 0:
  347. system.particleEmitterType = new BoxParticleEmitter();
  348. break;
  349. case 1:
  350. system.particleEmitterType = new ConeParticleEmitter();
  351. break;
  352. case 2:
  353. system.particleEmitterType = new CylinderParticleEmitter();
  354. break;
  355. case 3:
  356. system.particleEmitterType = new HemisphericParticleEmitter();
  357. break;
  358. case 4:
  359. system.particleEmitterType = new MeshParticleEmitter();
  360. break;
  361. case 5:
  362. system.particleEmitterType = new PointParticleEmitter();
  363. break;
  364. case 6:
  365. system.particleEmitterType = new SphereParticleEmitter();
  366. break;
  367. }
  368. this.raiseOnPropertyChanged("particleEmitterType", system.particleEmitterType, currentType)
  369. this.forceUpdate();
  370. }}
  371. extractValue={() => {
  372. switch(system.particleEmitterType?.getClassName()) {
  373. case "BoxParticleEmitter":
  374. return 0;
  375. case "ConeParticleEmitter":
  376. return 1;
  377. case "CylinderParticleEmitter":
  378. return 2;
  379. case "HemisphericParticleEmitter":
  380. return 3;
  381. case "MeshParticleEmitter":
  382. return 4;
  383. case "PointParticleEmitter":
  384. return 5;
  385. case "SphereParticleEmitter":
  386. return 6;
  387. }
  388. return 0;
  389. }}/>
  390. {
  391. this.renderEmitter()
  392. }
  393. </LineContainerComponent>
  394. <LineContainerComponent globalState={this.props.globalState} title="EMISSION" closed={true}>
  395. <FloatLineComponent lockObject={this.props.lockObject} label="Rate" target={system} propertyName="emitRate" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  396. {
  397. system instanceof ParticleSystem &&
  398. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getEmitRateGradients()!}
  399. label="Velocity gradients"
  400. docLink="https://doc.babylonjs.com/babylon101/particles#emit-rate-over-time"
  401. onCreateRequired={() => {
  402. system.addEmitRateGradient(0, 50, 50);
  403. }}
  404. mode={GradientGridMode.Factor}
  405. host={system}
  406. codeRecorderPropertyName="getEmitRateGradients()"
  407. lockObject={this.props.lockObject}/>
  408. }
  409. <FloatLineComponent lockObject={this.props.lockObject} label="Min emit power" target={system} propertyName="minEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  410. <FloatLineComponent lockObject={this.props.lockObject} label="Max emit power" target={system} propertyName="maxEmitPower" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  411. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getVelocityGradients()!}
  412. label="Velocity gradients"
  413. docLink="https://doc.babylonjs.com/babylon101/particles#velocity-over-time"
  414. onCreateRequired={() => {
  415. system.addVelocityGradient(0, 0.1, 0.1);
  416. }}
  417. mode={GradientGridMode.Factor}
  418. host={system}
  419. codeRecorderPropertyName="getVelocityGradients()"
  420. lockObject={this.props.lockObject}/>
  421. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getLimitVelocityGradients()!}
  422. label="Limit velocity gradients"
  423. docLink="https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time"
  424. onCreateRequired={() => {
  425. system.addLimitVelocityGradient(0, 0.1, 0.1);
  426. }}
  427. mode={GradientGridMode.Factor}
  428. host={system}
  429. codeRecorderPropertyName="getLimitVelocityGradients()"
  430. lockObject={this.props.lockObject}/>
  431. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getDragGradients()!}
  432. label="Drag gradients"
  433. docLink="https://doc.babylonjs.com/babylon101/particles#drag-factor"
  434. onCreateRequired={() => {
  435. system.addDragGradient(0, 0.1, 0.1);
  436. }}
  437. host={system}
  438. codeRecorderPropertyName="getDragGradients()"
  439. mode={GradientGridMode.Factor}
  440. lockObject={this.props.lockObject}/>
  441. </LineContainerComponent>
  442. <LineContainerComponent globalState={this.props.globalState} title="SIZE" closed={true}>
  443. {
  444. (!system.getSizeGradients() || system.getSizeGradients()?.length === 0)&&
  445. <>
  446. <FloatLineComponent lockObject={this.props.lockObject} label="Min size" target={system} propertyName="minSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  447. <FloatLineComponent lockObject={this.props.lockObject} label="Max size" target={system} propertyName="maxSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  448. </>
  449. }
  450. <FloatLineComponent lockObject={this.props.lockObject} label="Min scale X" target={system} propertyName="minScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  451. <FloatLineComponent lockObject={this.props.lockObject} label="Max scale X" target={system} propertyName="maxScaleX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  452. <FloatLineComponent lockObject={this.props.lockObject} label="Min scale Y" target={system} propertyName="minScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  453. <FloatLineComponent lockObject={this.props.lockObject} label="Max scale Y" target={system} propertyName="maxScaleY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  454. {
  455. system instanceof ParticleSystem &&
  456. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getStartSizeGradients()!}
  457. label="Start size gradients"
  458. docLink="https://doc.babylonjs.com/babylon101/particles#start-size-over-time"
  459. onCreateRequired={() => {
  460. system.addStartSizeGradient(0, 1, 1);
  461. }}
  462. host={system}
  463. codeRecorderPropertyName="getStartSizeGradients()"
  464. mode={GradientGridMode.Factor}
  465. lockObject={this.props.lockObject}/>
  466. }
  467. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getSizeGradients()!}
  468. label="Size gradients"
  469. docLink="https://doc.babylonjs.com/babylon101/particles#size"
  470. onCreateRequired={() => {
  471. system.addSizeGradient(0, 1, 1);
  472. }}
  473. host={system}
  474. codeRecorderPropertyName="getSizeGradients()"
  475. mode={GradientGridMode.Factor}
  476. lockObject={this.props.lockObject}/>
  477. </LineContainerComponent>
  478. <LineContainerComponent globalState={this.props.globalState} title="LIFETIME" closed={true}>
  479. <FloatLineComponent lockObject={this.props.lockObject} label="Min lifetime" target={system} propertyName="minLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  480. <FloatLineComponent lockObject={this.props.lockObject} label="Max lifetime" target={system} propertyName="maxLifeTime" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  481. <FloatLineComponent lockObject={this.props.lockObject} label="Target stop duration" target={system} propertyName="targetStopDuration" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  482. {
  483. system instanceof ParticleSystem &&
  484. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getLifeTimeGradients()!}
  485. label="Lifetime gradients"
  486. docLink="https://doc.babylonjs.com/babylon101/particles#lifetime"
  487. onCreateRequired={() => {
  488. system.addLifeTimeGradient(0, 1, 1);
  489. }}
  490. host={system}
  491. codeRecorderPropertyName="getLifeTimeGradients()"
  492. mode={GradientGridMode.Factor}
  493. lockObject={this.props.lockObject}/>
  494. }
  495. </LineContainerComponent>
  496. <LineContainerComponent globalState={this.props.globalState} title="COLORS" closed={true}>
  497. {
  498. (!system.getColorGradients() || system.getColorGradients()?.length === 0) &&
  499. <>
  500. <Color4LineComponent label="Color 1" target={system} propertyName="color1"
  501. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  502. <Color4LineComponent label="Color 2" target={system} propertyName="color2"
  503. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  504. <Color4LineComponent label="Color dead" target={system} propertyName="colorDead"
  505. onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  506. </>
  507. }
  508. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getColorGradients()!}
  509. label="Color gradients"
  510. docLink="https://doc.babylonjs.com/babylon101/particles#particle-colors"
  511. onCreateRequired={() => {
  512. system.addColorGradient(0, new Color4(0, 0, 0, 1), new Color4(0, 0, 0, 1));
  513. system.addColorGradient(1, new Color4(1, 1, 1, 1), new Color4(1, 1, 1, 1));
  514. }}
  515. host={system}
  516. codeRecorderPropertyName="getColorGradients()"
  517. mode={GradientGridMode.Color4}
  518. lockObject={this.props.lockObject}/>
  519. {
  520. system instanceof ParticleSystem &&
  521. <>
  522. <CheckBoxLineComponent label="Enable ramp grandients" target={system} propertyName="useRampGradients"/>
  523. {
  524. system.useRampGradients &&
  525. <>
  526. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getRampGradients()!}
  527. label="Ramp gradients"
  528. docLink="https://doc.babylonjs.com/babylon101/particles#ramp-gradients"
  529. onCreateRequired={() => {
  530. system.addRampGradient(0, Color3.White());
  531. system.addRampGradient(1, Color3.Black());
  532. }}
  533. mode={GradientGridMode.Color3}
  534. host={system}
  535. codeRecorderPropertyName="getRampGradients()"
  536. lockObject={this.props.lockObject}/>
  537. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getColorRemapGradients()!}
  538. label="Color remap gradients"
  539. docLink="https://doc.babylonjs.com/babylon101/particles#ramp-gradients"
  540. onCreateRequired={() => {
  541. system.addColorRemapGradient(0, 1, 1);
  542. }}
  543. host={system}
  544. codeRecorderPropertyName="getColorRemapGradients()"
  545. mode={GradientGridMode.Factor}
  546. lockObject={this.props.lockObject}/>
  547. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getAlphaRemapGradients()!}
  548. label="Alpha remap gradients"
  549. docLink="https://doc.babylonjs.com/babylon101/particles#ramp-gradients"
  550. onCreateRequired={() => {
  551. system.addAlphaRemapGradient(0, 1, 1);
  552. }}
  553. host={system}
  554. codeRecorderPropertyName="getAlphaRemapGradients()"
  555. mode={GradientGridMode.Factor}
  556. lockObject={this.props.lockObject}/>
  557. </>
  558. }
  559. </>
  560. }
  561. </LineContainerComponent>
  562. <LineContainerComponent globalState={this.props.globalState} title="ROTATION" closed={true}>
  563. <FloatLineComponent lockObject={this.props.lockObject} label="Min angular speed" target={system} propertyName="minAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  564. <FloatLineComponent lockObject={this.props.lockObject} label="Max angular speed" target={system} propertyName="maxAngularSpeed" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  565. <FloatLineComponent lockObject={this.props.lockObject} label="Min initial rotation" target={system} propertyName="minInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  566. <FloatLineComponent lockObject={this.props.lockObject} label="Max initial rotation" target={system} propertyName="maxInitialRotation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  567. <ValueGradientGridComponent globalState={this.props.globalState} gradients={system.getAngularSpeedGradients()!}
  568. label="Angular speed gradients"
  569. docLink="hhttps://doc.babylonjs.com/babylon101/particles#rotation"
  570. onCreateRequired={() => {
  571. system.addAngularSpeedGradient(0, 0.1, 0.1);
  572. }}
  573. host={system}
  574. codeRecorderPropertyName="getAngularSpeedGradients()"
  575. mode={GradientGridMode.Factor}
  576. lockObject={this.props.lockObject}/>
  577. </LineContainerComponent>
  578. <LineContainerComponent globalState={this.props.globalState} title="SPRITESHEET" closed={true}>
  579. <CheckBoxLineComponent label="Animation sheet enabled" target={system} propertyName="isAnimationSheetEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  580. <FloatLineComponent label="First sprite index" isInteger={true} target={system} propertyName="startSpriteCellID" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  581. <FloatLineComponent label="Last sprite index" isInteger={true} target={system} propertyName="endSpriteCellID" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  582. <CheckBoxLineComponent label="Random start cell index" target={system} propertyName="spriteRandomStartCell" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  583. <FloatLineComponent label="Cell width" isInteger={true} target={system} propertyName="spriteCellWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  584. <FloatLineComponent label="Cell height" isInteger={true} target={system} propertyName="spriteCellHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  585. <SliderLineComponent label="Cell change speed" target={system} propertyName="spriteCellChangeSpeed" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
  586. </LineContainerComponent>
  587. </div>
  588. );
  589. }
  590. }