@nice2dev/game-engine 1.3.14 → 1.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/accessibility/Accessibility.js +152 -165
  3. package/dist/cjs/ai/AI.js +292 -294
  4. package/dist/cjs/ai/BehaviorTree.js +1029 -1113
  5. package/dist/cjs/ai/StateMachine.js +694 -756
  6. package/dist/cjs/animation/Animation.js +232 -241
  7. package/dist/cjs/audio/AudioAdvanced.js +221 -236
  8. package/dist/cjs/audio/AudioBridge.js +441 -435
  9. package/dist/cjs/audio/AudioManager.js +242 -259
  10. package/dist/cjs/build-cloud/BuildOrchestration.js +1059 -1098
  11. package/dist/cjs/build-cloud/GodotBuildCloud.js +641 -670
  12. package/dist/cjs/build-cloud/UnityBuildCloud.js +563 -610
  13. package/dist/cjs/build-cloud/UnrealBuildCloud.js +610 -635
  14. package/dist/cjs/core/EventBus.js +51 -56
  15. package/dist/cjs/core/GameClock.js +62 -66
  16. package/dist/cjs/core/GameConfig.js +72 -79
  17. package/dist/cjs/core/GameLoop.js +80 -89
  18. package/dist/cjs/core/ServiceLocator.js +61 -65
  19. package/dist/cjs/core/Validation.js +72 -82
  20. package/dist/cjs/core/math.js +108 -112
  21. package/dist/cjs/devtools/DevTools.js +542 -563
  22. package/dist/cjs/devtools/DeveloperExperience.js +521 -559
  23. package/dist/cjs/devtools/GameplayAnalytics.js +636 -646
  24. package/dist/cjs/dialogue/DialogueSystem.js +984 -986
  25. package/dist/cjs/docs/DocGenerator.js +339 -364
  26. package/dist/cjs/ecs/World.js +355 -371
  27. package/dist/cjs/editor/AchievementEditor.js +580 -636
  28. package/dist/cjs/editor/AdvancedEditor.js +184 -191
  29. package/dist/cjs/editor/AnimationRetargeting.js +696 -741
  30. package/dist/cjs/editor/AssetBrowser.js +1072 -1160
  31. package/dist/cjs/editor/AssetManager.js +187 -192
  32. package/dist/cjs/editor/ConsolePro.js +971 -1061
  33. package/dist/cjs/editor/CraftingEditor.js +680 -730
  34. package/dist/cjs/editor/DebugTools.js +187 -185
  35. package/dist/cjs/editor/DialogueSystem.js +844 -901
  36. package/dist/cjs/editor/LeaderboardSystem.js +429 -483
  37. package/dist/cjs/editor/NiceGameEditor.js +1903 -1201
  38. package/dist/cjs/editor/PrefabSystem.js +772 -838
  39. package/dist/cjs/editor/ProfilerPanel.js +649 -700
  40. package/dist/cjs/editor/QuestEditor.js +792 -839
  41. package/dist/cjs/editor/ReplaySystem.js +585 -634
  42. package/dist/cjs/editor/SceneEditor.js +176 -200
  43. package/dist/cjs/editor/ScreenshotMode.js +621 -688
  44. package/dist/cjs/editor/ShaderGraph.js +1556 -1582
  45. package/dist/cjs/editor/TimelineEditor.js +715 -778
  46. package/dist/cjs/editor/UndoRedoPro.js +830 -904
  47. package/dist/cjs/editor/VRARMode.js +582 -645
  48. package/dist/cjs/engine/NiceGameEngine.js +141 -164
  49. package/dist/cjs/engine/SaveSystem.js +441 -495
  50. package/dist/cjs/enterprise/Enterprise.js +254 -261
  51. package/dist/cjs/export/BevyExporter.js +927 -967
  52. package/dist/cjs/export/CocosExporter.js +644 -673
  53. package/dist/cjs/export/Construct3Exporter.js +783 -794
  54. package/dist/cjs/export/DefoldExporter.js +1028 -1069
  55. package/dist/cjs/export/GDevelopExporter.js +674 -708
  56. package/dist/cjs/export/GameMakerExporter.js +791 -805
  57. package/dist/cjs/export/GodotExporter.js +1003 -1045
  58. package/dist/cjs/export/MinecraftBedrockExporter.js +573 -578
  59. package/dist/cjs/export/MonoGameExporter.js +1265 -1293
  60. package/dist/cjs/export/Pico8Exporter.js +766 -812
  61. package/dist/cjs/export/PlatformExporter.js +188 -206
  62. package/dist/cjs/export/RPGMakerExporter.js +860 -879
  63. package/dist/cjs/export/RobloxExporter.js +883 -919
  64. package/dist/cjs/export/Solar2DExporter.js +1218 -1256
  65. package/dist/cjs/export/ThreeJSExporter.js +1006 -1073
  66. package/dist/cjs/export/Tic80Exporter.js +1210 -1267
  67. package/dist/cjs/export/UnityExporter.js +1116 -1131
  68. package/dist/cjs/export/UnrealExporter.js +889 -919
  69. package/dist/cjs/export/WebExporter.js +931 -970
  70. package/dist/cjs/export/index.js +42 -65
  71. package/dist/cjs/godot-integration/GodotAssetLibrary.js +659 -692
  72. package/dist/cjs/godot-integration/GodotImport.js +929 -1027
  73. package/dist/cjs/godot-integration/GodotLiveLink.js +915 -945
  74. package/dist/cjs/godot-integration/components/GodotProjectBrowser.js +911 -611
  75. package/dist/cjs/i18n/I18n.js +860 -900
  76. package/dist/cjs/i18n/useTranslation.js +84 -89
  77. package/dist/cjs/import/AsepriteImporter.js +658 -716
  78. package/dist/cjs/import/DragonBonesImporter.js +437 -461
  79. package/dist/cjs/import/GameMakerImporter.js +511 -521
  80. package/dist/cjs/import/GodotSceneImporter.js +788 -822
  81. package/dist/cjs/import/LDtkImporter.js +436 -454
  82. package/dist/cjs/import/Live2DImporter.js +497 -518
  83. package/dist/cjs/import/NdgFormat.js +424 -457
  84. package/dist/cjs/import/OgmoImporter.js +466 -493
  85. package/dist/cjs/import/RPGMakerImporter.js +451 -477
  86. package/dist/cjs/import/SceneImporter.js +378 -423
  87. package/dist/cjs/import/SpineImporter.js +511 -546
  88. package/dist/cjs/import/SpriterImporter.js +590 -613
  89. package/dist/cjs/import/TiledMapImporter.js +811 -846
  90. package/dist/cjs/import/UnitySceneImporter.js +687 -703
  91. package/dist/cjs/import/index.js +206 -251
  92. package/dist/cjs/index.js +275 -278
  93. package/dist/cjs/input/GamepadNavigation.js +185 -205
  94. package/dist/cjs/input/InputManager.js +397 -421
  95. package/dist/cjs/input/useGamepads.js +70 -81
  96. package/dist/cjs/integration/IconSprite.js +129 -256
  97. package/dist/cjs/inventory/InventorySystem.js +920 -995
  98. package/dist/cjs/kids/KidMode.js +997 -1022
  99. package/dist/cjs/kids/KidTools.js +570 -594
  100. package/dist/cjs/monetization/Monetization.js +170 -177
  101. package/dist/cjs/multiplayer/LocalMultiplayer.js +287 -287
  102. package/dist/cjs/multiplayer/MiniGameTypes.js +49 -59
  103. package/dist/cjs/native-engine/AnimationSystem.js +731 -806
  104. package/dist/cjs/native-engine/AssetPipeline.js +454 -515
  105. package/dist/cjs/native-engine/AudioEngine.js +763 -804
  106. package/dist/cjs/native-engine/InputSystem.js +1075 -1145
  107. package/dist/cjs/native-engine/NetworkingSystem.js +733 -793
  108. package/dist/cjs/native-engine/PhysicsEngine.js +583 -607
  109. package/dist/cjs/native-engine/RenderingEngine.js +302 -350
  110. package/dist/cjs/native-engine/SceneGraph.js +769 -808
  111. package/dist/cjs/network/MultiplayerTransport.js +97 -132
  112. package/dist/cjs/network/Networking.js +549 -564
  113. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AbortController.js +21 -29
  114. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js +40 -48
  115. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js +35 -43
  116. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js +10 -15
  117. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Errors.js +103 -135
  118. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js +131 -158
  119. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js +39 -53
  120. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js +5 -8
  121. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpClient.js +40 -48
  122. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js +494 -572
  123. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnection.js +826 -953
  124. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js +125 -146
  125. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js +13 -23
  126. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ILogger.js +11 -25
  127. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ITransport.js +13 -24
  128. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js +108 -121
  129. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Loggers.js +10 -14
  130. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js +150 -185
  131. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js +158 -196
  132. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js +90 -105
  133. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Subject.js +30 -35
  134. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js +16 -21
  135. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Utils.js +215 -248
  136. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js +135 -157
  137. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js +76 -85
  138. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/index.js +24 -27
  139. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/pkg-version.js +3 -4
  140. package/dist/cjs/pathfinding/Pathfinding.js +245 -263
  141. package/dist/cjs/performance/AssetStreaming.js +561 -635
  142. package/dist/cjs/performance/BenchmarkSuite.js +283 -311
  143. package/dist/cjs/performance/DeltaCompression.js +467 -526
  144. package/dist/cjs/performance/DeterministicPhysics.js +450 -498
  145. package/dist/cjs/performance/ECSOptimization.js +644 -695
  146. package/dist/cjs/performance/GameSaveSystem.js +509 -556
  147. package/dist/cjs/performance/HotReload.js +447 -500
  148. package/dist/cjs/performance/InputRecording.js +511 -561
  149. package/dist/cjs/performance/InterestManagement.js +453 -500
  150. package/dist/cjs/performance/LocalizationRuntime.js +376 -423
  151. package/dist/cjs/performance/MemoryManagement.js +410 -470
  152. package/dist/cjs/performance/ModSupport.js +445 -496
  153. package/dist/cjs/performance/MultiThreadedPhysics.js +400 -443
  154. package/dist/cjs/performance/NetworkPrediction.js +424 -482
  155. package/dist/cjs/performance/ObjectPooling.js +569 -617
  156. package/dist/cjs/performance/Performance.js +174 -190
  157. package/dist/cjs/performance/PerformanceAdvanced.js +614 -637
  158. package/dist/cjs/performance/WebGPURenderer.js +656 -683
  159. package/dist/cjs/physics/PhysicsAdvanced.js +345 -379
  160. package/dist/cjs/physics/PhysicsEngine2D.js +391 -427
  161. package/dist/cjs/plugins/PluginSDK.js +432 -466
  162. package/dist/cjs/plugins/PluginTestKit.js +307 -330
  163. package/dist/cjs/procedural/Procedural.js +243 -254
  164. package/dist/cjs/procedural/WorldBuilding.js +297 -314
  165. package/dist/cjs/quest/QuestSystem.js +975 -932
  166. package/dist/cjs/release/MobileExport.js +157 -163
  167. package/dist/cjs/release/Release.js +225 -238
  168. package/dist/cjs/rendering/Camera2D.js +121 -124
  169. package/dist/cjs/rendering/Renderer2D.js +321 -335
  170. package/dist/cjs/rendering/WebGPURenderPipeline.js +632 -651
  171. package/dist/cjs/runtime3d/Runtime3D.js +359 -378
  172. package/dist/cjs/runtime3d/SceneEditor3D.js +252 -263
  173. package/dist/cjs/scene/SceneManager.js +177 -193
  174. package/dist/cjs/scripting/GraphToAST.js +574 -547
  175. package/dist/cjs/scripting/LanguageExporter.js +254 -279
  176. package/dist/cjs/scripting/NodeGraph.js +546 -570
  177. package/dist/cjs/scripting/ScriptAST.js +31 -39
  178. package/dist/cjs/scripting/VisualScripting2.js +1016 -1065
  179. package/dist/cjs/scripting/exporters/CSharpExporter.js +464 -496
  180. package/dist/cjs/scripting/exporters/GDScriptExporter.js +390 -442
  181. package/dist/cjs/scripting/exporters/LuaExporter.js +406 -447
  182. package/dist/cjs/scripting/exporters/PythonExporter.js +484 -549
  183. package/dist/cjs/scripting/exporters/RustExporter.js +459 -512
  184. package/dist/cjs/scripting/exporters/TypeScriptExporter.js +505 -556
  185. package/dist/cjs/social/Social.js +72 -80
  186. package/dist/cjs/systems/LightingSystem.js +844 -931
  187. package/dist/cjs/systems/ParticleSystem2.js +1447 -1451
  188. package/dist/cjs/systems/PhysicsDebugSystem.js +1070 -1141
  189. package/dist/cjs/systems/TerrainSystem.js +985 -1098
  190. package/dist/cjs/templates/ActionTemplates.js +296 -309
  191. package/dist/cjs/templates/FightingTemplate.js +417 -463
  192. package/dist/cjs/templates/MetroidvaniaTemplate.js +353 -405
  193. package/dist/cjs/templates/PartyTemplates.js +190 -202
  194. package/dist/cjs/templates/PuzzleTemplates.js +245 -261
  195. package/dist/cjs/templates/RPGTemplates.js +160 -178
  196. package/dist/cjs/templates/SportsTemplates.js +237 -250
  197. package/dist/cjs/templates/StrategyTemplates.js +143 -153
  198. package/dist/cjs/templates/SurvivalTemplate.js +336 -380
  199. package/dist/cjs/templates/WaveDefense.js +499 -526
  200. package/dist/cjs/tilemap/Tilemap.js +207 -214
  201. package/dist/cjs/tutorials/TutorialBranching.js +396 -417
  202. package/dist/cjs/tutorials/TutorialEngine.js +888 -931
  203. package/dist/cjs/tutorials/TutorialOverlay.js +602 -640
  204. package/dist/cjs/tutorials/content/Tutorials_T05_T08.js +787 -809
  205. package/dist/cjs/tutorials/content/Tutorials_T09_T11.js +736 -756
  206. package/dist/cjs/tutorials/content/Tutorials_T12_T15.js +754 -778
  207. package/dist/cjs/ui/GameUI.js +295 -312
  208. package/dist/cjs/unity-integration/UnityAssetStore.js +700 -741
  209. package/dist/cjs/unity-integration/UnityImport.js +1066 -1176
  210. package/dist/cjs/unity-integration/UnityLiveLink.js +934 -981
  211. package/dist/cjs/unity-integration/components/UnityAssetStoreBrowser.js +1233 -757
  212. package/dist/cjs/unity-integration/components/UnityProjectBrowser.js +1240 -768
  213. package/dist/cjs/unreal-integration/UnrealImport.js +391 -412
  214. package/dist/cjs/unreal-integration/UnrealLiveLink.js +1102 -1137
  215. package/dist/cjs/unreal-integration/UnrealMarketplace.js +436 -473
  216. package/dist/cjs/unreal-integration/components/UnrealProjectBrowser.js +1150 -767
  217. package/dist/cjs/xr/ARVR.js +167 -175
  218. package/dist/esm/accessibility/Accessibility.js +160 -164
  219. package/dist/esm/ai/AI.js +310 -293
  220. package/dist/esm/ai/BehaviorTree.js +1057 -1112
  221. package/dist/esm/ai/StateMachine.js +709 -755
  222. package/dist/esm/animation/Animation.js +237 -240
  223. package/dist/esm/audio/AudioAdvanced.js +239 -235
  224. package/dist/esm/audio/AudioBridge.js +448 -434
  225. package/dist/esm/audio/AudioManager.js +243 -258
  226. package/dist/esm/build-cloud/BuildOrchestration.js +1071 -1097
  227. package/dist/esm/build-cloud/GodotBuildCloud.js +647 -669
  228. package/dist/esm/build-cloud/UnityBuildCloud.js +571 -609
  229. package/dist/esm/build-cloud/UnrealBuildCloud.js +615 -634
  230. package/dist/esm/core/EventBus.js +52 -55
  231. package/dist/esm/core/GameClock.js +63 -65
  232. package/dist/esm/core/GameConfig.js +77 -78
  233. package/dist/esm/core/GameLoop.js +81 -88
  234. package/dist/esm/core/ServiceLocator.js +63 -64
  235. package/dist/esm/core/Validation.js +82 -81
  236. package/dist/esm/core/math.js +109 -111
  237. package/dist/esm/devtools/DevTools.js +546 -562
  238. package/dist/esm/devtools/DeveloperExperience.js +535 -558
  239. package/dist/esm/devtools/GameplayAnalytics.js +647 -645
  240. package/dist/esm/dialogue/DialogueSystem.js +998 -985
  241. package/dist/esm/docs/DocGenerator.js +342 -363
  242. package/dist/esm/ecs/World.js +358 -370
  243. package/dist/esm/editor/AchievementEditor.js +584 -635
  244. package/dist/esm/editor/AdvancedEditor.js +191 -190
  245. package/dist/esm/editor/AnimationRetargeting.js +700 -740
  246. package/dist/esm/editor/AssetBrowser.js +1078 -1159
  247. package/dist/esm/editor/AssetManager.js +188 -191
  248. package/dist/esm/editor/ConsolePro.js +977 -1060
  249. package/dist/esm/editor/CraftingEditor.js +684 -729
  250. package/dist/esm/editor/DebugTools.js +194 -184
  251. package/dist/esm/editor/DialogueSystem.js +849 -900
  252. package/dist/esm/editor/LeaderboardSystem.js +433 -482
  253. package/dist/esm/editor/NiceGameEditor.js +1908 -1200
  254. package/dist/esm/editor/PrefabSystem.js +777 -837
  255. package/dist/esm/editor/ProfilerPanel.js +654 -699
  256. package/dist/esm/editor/QuestEditor.js +795 -838
  257. package/dist/esm/editor/ReplaySystem.js +591 -633
  258. package/dist/esm/editor/SceneEditor.js +189 -199
  259. package/dist/esm/editor/ScreenshotMode.js +628 -687
  260. package/dist/esm/editor/ShaderGraph.js +1565 -1581
  261. package/dist/esm/editor/TimelineEditor.js +733 -777
  262. package/dist/esm/editor/UndoRedoPro.js +835 -903
  263. package/dist/esm/editor/VRARMode.js +586 -644
  264. package/dist/esm/engine/NiceGameEngine.js +142 -163
  265. package/dist/esm/engine/SaveSystem.js +447 -494
  266. package/dist/esm/enterprise/Enterprise.js +265 -260
  267. package/dist/esm/export/BevyExporter.js +929 -966
  268. package/dist/esm/export/CocosExporter.js +646 -672
  269. package/dist/esm/export/Construct3Exporter.js +785 -793
  270. package/dist/esm/export/DefoldExporter.js +1030 -1068
  271. package/dist/esm/export/GDevelopExporter.js +676 -707
  272. package/dist/esm/export/GameMakerExporter.js +793 -804
  273. package/dist/esm/export/GodotExporter.js +1004 -1044
  274. package/dist/esm/export/MinecraftBedrockExporter.js +575 -577
  275. package/dist/esm/export/MonoGameExporter.js +1267 -1292
  276. package/dist/esm/export/Pico8Exporter.js +768 -811
  277. package/dist/esm/export/PlatformExporter.js +193 -205
  278. package/dist/esm/export/RPGMakerExporter.js +862 -878
  279. package/dist/esm/export/RobloxExporter.js +885 -918
  280. package/dist/esm/export/Solar2DExporter.js +1220 -1255
  281. package/dist/esm/export/ThreeJSExporter.js +1007 -1072
  282. package/dist/esm/export/Tic80Exporter.js +1212 -1266
  283. package/dist/esm/export/UnityExporter.js +1117 -1130
  284. package/dist/esm/export/UnrealExporter.js +891 -918
  285. package/dist/esm/export/WebExporter.js +933 -969
  286. package/dist/esm/export/index.js +86 -71
  287. package/dist/esm/godot-integration/GodotAssetLibrary.js +663 -691
  288. package/dist/esm/godot-integration/GodotImport.js +935 -1026
  289. package/dist/esm/godot-integration/GodotLiveLink.js +918 -944
  290. package/dist/esm/godot-integration/components/GodotProjectBrowser.js +912 -610
  291. package/dist/esm/i18n/I18n.js +871 -899
  292. package/dist/esm/i18n/useTranslation.js +88 -88
  293. package/dist/esm/import/AsepriteImporter.js +637 -715
  294. package/dist/esm/import/DragonBonesImporter.js +417 -460
  295. package/dist/esm/import/GameMakerImporter.js +491 -520
  296. package/dist/esm/import/GodotSceneImporter.js +767 -821
  297. package/dist/esm/import/LDtkImporter.js +415 -453
  298. package/dist/esm/import/Live2DImporter.js +477 -517
  299. package/dist/esm/import/NdgFormat.js +410 -456
  300. package/dist/esm/import/OgmoImporter.js +446 -492
  301. package/dist/esm/import/RPGMakerImporter.js +431 -476
  302. package/dist/esm/import/SceneImporter.js +385 -422
  303. package/dist/esm/import/SpineImporter.js +491 -545
  304. package/dist/esm/import/SpriterImporter.js +570 -612
  305. package/dist/esm/import/TiledMapImporter.js +790 -845
  306. package/dist/esm/import/UnitySceneImporter.js +666 -702
  307. package/dist/esm/import/index.js +247 -258
  308. package/dist/esm/index.js +1332 -188
  309. package/dist/esm/input/GamepadNavigation.js +187 -204
  310. package/dist/esm/input/InputManager.js +401 -420
  311. package/dist/esm/input/useGamepads.js +72 -80
  312. package/dist/esm/integration/IconSprite.js +143 -255
  313. package/dist/esm/inventory/InventorySystem.js +925 -994
  314. package/dist/esm/kids/KidMode.js +1003 -1021
  315. package/dist/esm/kids/KidTools.js +573 -593
  316. package/dist/esm/monetization/Monetization.js +180 -176
  317. package/dist/esm/multiplayer/LocalMultiplayer.js +290 -286
  318. package/dist/esm/multiplayer/MiniGameTypes.js +58 -58
  319. package/dist/esm/native-engine/AnimationSystem.js +735 -805
  320. package/dist/esm/native-engine/AssetPipeline.js +456 -514
  321. package/dist/esm/native-engine/AudioEngine.js +765 -803
  322. package/dist/esm/native-engine/InputSystem.js +1079 -1144
  323. package/dist/esm/native-engine/NetworkingSystem.js +740 -792
  324. package/dist/esm/native-engine/PhysicsEngine.js +585 -606
  325. package/dist/esm/native-engine/RenderingEngine.js +304 -349
  326. package/dist/esm/native-engine/SceneGraph.js +773 -807
  327. package/dist/esm/network/MultiplayerTransport.js +99 -131
  328. package/dist/esm/network/Networking.js +556 -563
  329. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AbortController.js +22 -28
  330. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js +41 -47
  331. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js +36 -42
  332. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js +11 -14
  333. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Errors.js +111 -134
  334. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js +132 -157
  335. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js +40 -52
  336. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js +6 -7
  337. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpClient.js +42 -47
  338. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js +496 -571
  339. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnection.js +828 -952
  340. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js +126 -145
  341. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js +14 -23
  342. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ILogger.js +12 -25
  343. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ITransport.js +15 -24
  344. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js +109 -120
  345. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Loggers.js +11 -13
  346. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js +151 -184
  347. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js +159 -195
  348. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js +91 -104
  349. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Subject.js +31 -34
  350. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js +17 -20
  351. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Utils.js +230 -248
  352. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js +136 -156
  353. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js +77 -84
  354. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/index.js +31 -12
  355. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/pkg-version.js +4 -3
  356. package/dist/esm/pathfinding/Pathfinding.js +249 -262
  357. package/dist/esm/performance/AssetStreaming.js +567 -634
  358. package/dist/esm/performance/BenchmarkSuite.js +285 -310
  359. package/dist/esm/performance/DeltaCompression.js +478 -525
  360. package/dist/esm/performance/DeterministicPhysics.js +479 -497
  361. package/dist/esm/performance/ECSOptimization.js +653 -694
  362. package/dist/esm/performance/GameSaveSystem.js +515 -555
  363. package/dist/esm/performance/HotReload.js +454 -499
  364. package/dist/esm/performance/InputRecording.js +520 -560
  365. package/dist/esm/performance/InterestManagement.js +460 -499
  366. package/dist/esm/performance/LocalizationRuntime.js +382 -422
  367. package/dist/esm/performance/MemoryManagement.js +415 -469
  368. package/dist/esm/performance/ModSupport.js +455 -495
  369. package/dist/esm/performance/MultiThreadedPhysics.js +408 -442
  370. package/dist/esm/performance/NetworkPrediction.js +436 -481
  371. package/dist/esm/performance/ObjectPooling.js +582 -616
  372. package/dist/esm/performance/Performance.js +182 -189
  373. package/dist/esm/performance/PerformanceAdvanced.js +621 -636
  374. package/dist/esm/performance/WebGPURenderer.js +660 -682
  375. package/dist/esm/physics/PhysicsAdvanced.js +359 -378
  376. package/dist/esm/physics/PhysicsEngine2D.js +393 -426
  377. package/dist/esm/plugins/PluginSDK.js +435 -465
  378. package/dist/esm/plugins/PluginTestKit.js +315 -329
  379. package/dist/esm/procedural/Procedural.js +248 -253
  380. package/dist/esm/procedural/WorldBuilding.js +316 -313
  381. package/dist/esm/quest/QuestSystem.js +982 -931
  382. package/dist/esm/release/MobileExport.js +163 -162
  383. package/dist/esm/release/Release.js +234 -237
  384. package/dist/esm/rendering/Camera2D.js +122 -123
  385. package/dist/esm/rendering/Renderer2D.js +323 -334
  386. package/dist/esm/rendering/WebGPURenderPipeline.js +647 -650
  387. package/dist/esm/runtime3d/Runtime3D.js +368 -377
  388. package/dist/esm/runtime3d/SceneEditor3D.js +268 -262
  389. package/dist/esm/scene/SceneManager.js +178 -192
  390. package/dist/esm/scripting/GraphToAST.js +576 -546
  391. package/dist/esm/scripting/LanguageExporter.js +263 -278
  392. package/dist/esm/scripting/NodeGraph.js +550 -569
  393. package/dist/esm/scripting/ScriptAST.js +45 -38
  394. package/dist/esm/scripting/VisualScripting2.js +1025 -1064
  395. package/dist/esm/scripting/exporters/CSharpExporter.js +465 -495
  396. package/dist/esm/scripting/exporters/GDScriptExporter.js +391 -441
  397. package/dist/esm/scripting/exporters/LuaExporter.js +407 -446
  398. package/dist/esm/scripting/exporters/PythonExporter.js +485 -548
  399. package/dist/esm/scripting/exporters/RustExporter.js +460 -511
  400. package/dist/esm/scripting/exporters/TypeScriptExporter.js +506 -555
  401. package/dist/esm/social/Social.js +77 -79
  402. package/dist/esm/systems/LightingSystem.js +855 -930
  403. package/dist/esm/systems/ParticleSystem2.js +1453 -1450
  404. package/dist/esm/systems/PhysicsDebugSystem.js +1078 -1140
  405. package/dist/esm/systems/TerrainSystem.js +996 -1097
  406. package/dist/esm/templates/ActionTemplates.js +307 -308
  407. package/dist/esm/templates/FightingTemplate.js +439 -462
  408. package/dist/esm/templates/MetroidvaniaTemplate.js +391 -404
  409. package/dist/esm/templates/PartyTemplates.js +206 -201
  410. package/dist/esm/templates/PuzzleTemplates.js +260 -260
  411. package/dist/esm/templates/RPGTemplates.js +172 -177
  412. package/dist/esm/templates/SportsTemplates.js +251 -249
  413. package/dist/esm/templates/StrategyTemplates.js +158 -152
  414. package/dist/esm/templates/SurvivalTemplate.js +367 -379
  415. package/dist/esm/templates/WaveDefense.js +501 -525
  416. package/dist/esm/tilemap/Tilemap.js +209 -213
  417. package/dist/esm/tutorials/TutorialBranching.js +399 -416
  418. package/dist/esm/tutorials/TutorialEngine.js +893 -930
  419. package/dist/esm/tutorials/TutorialOverlay.js +603 -639
  420. package/dist/esm/tutorials/content/Tutorials_T05_T08.js +792 -808
  421. package/dist/esm/tutorials/content/Tutorials_T09_T11.js +740 -755
  422. package/dist/esm/tutorials/content/Tutorials_T12_T15.js +759 -777
  423. package/dist/esm/ui/GameUI.js +296 -311
  424. package/dist/esm/unity-integration/UnityAssetStore.js +704 -740
  425. package/dist/esm/unity-integration/UnityImport.js +1071 -1175
  426. package/dist/esm/unity-integration/UnityLiveLink.js +938 -980
  427. package/dist/esm/unity-integration/components/UnityAssetStoreBrowser.js +1234 -756
  428. package/dist/esm/unity-integration/components/UnityProjectBrowser.js +1241 -767
  429. package/dist/esm/unreal-integration/UnrealImport.js +395 -411
  430. package/dist/esm/unreal-integration/UnrealLiveLink.js +1105 -1136
  431. package/dist/esm/unreal-integration/UnrealMarketplace.js +442 -472
  432. package/dist/esm/unreal-integration/components/UnrealProjectBrowser.js +1151 -766
  433. package/dist/esm/xr/ARVR.js +181 -174
  434. package/package.json +7 -12
@@ -1,1501 +1,1504 @@
1
- /**
2
- * @file ParticleSystem2.ts
3
- * @description Advanced Particle System with GPU Compute and Visual Editor
4
- * PRO-1.4: Professional Editor Features
5
- *
6
- * Features:
7
- * - GPU-accelerated particles (WebGPU Compute)
8
- * - Multiple emitter shapes
9
- * - Force fields (gravity, wind, vortex, etc.)
10
- * - Sub-emitters and trails
11
- * - Collision detection
12
- * - LOD system
13
- * - Visual editor integration
14
- */
15
- // ============================================================
16
- // Particle System Class
17
- // ============================================================
18
1
  class ParticleSystem2 {
19
- constructor(def) {
20
- this.useGPU = false;
21
- this.definition = def;
22
- this.state = this.createInitialState();
23
- }
24
- createInitialState() {
25
- const maxParticles = this.definition.main.maxParticles;
26
- const particles = [];
27
- for (let i = 0; i < maxParticles; i++) {
28
- particles.push(this.createDeadParticle());
29
- }
30
- return {
31
- particles,
32
- activeCount: 0,
33
- time: 0,
34
- isPlaying: false,
35
- isPaused: false,
36
- isStopped: true,
37
- isEmitting: false,
38
- randomSeed: (Math.random() * 0xffffffff) | 0,
39
- emitCounter: 0,
40
- distanceTraveled: 0,
41
- lastPosition: new Float32Array([0, 0, 0]),
42
- burstStates: this.definition.emission.bursts.map((b, i) => ({
43
- burstIndex: i,
44
- cycleCount: 0,
45
- nextTime: b.time,
46
- })),
47
- };
48
- }
49
- createDeadParticle() {
50
- return {
51
- position: new Float32Array([0, 0, 0]),
52
- velocity: new Float32Array([0, 0, 0]),
53
- startColor: new Float32Array([1, 1, 1, 1]),
54
- color: new Float32Array([1, 1, 1, 1]),
55
- startSize: new Float32Array([1, 1, 1]),
56
- size: new Float32Array([1, 1, 1]),
57
- rotation: new Float32Array([0, 0, 0]),
58
- angularVelocity: new Float32Array([0, 0, 0]),
59
- startLifetime: 0,
60
- remainingLifetime: -1,
61
- normalizedLifetime: 1,
62
- randomSeed: (Math.random() * 0xffffffff) | 0,
63
- startSpeed: 0,
64
- axisOfRotation: new Float32Array([0, 0, 1]),
65
- meshIndex: 0,
66
- customData1: new Float32Array([0, 0, 0, 0]),
67
- customData2: new Float32Array([0, 0, 0, 0]),
68
- };
69
- }
70
- // ─── Playback Control ──────────────────────────────────
71
- play() {
72
- if (this.state.isPaused) {
73
- this.state.isPaused = false;
74
- }
75
- this.state.isPlaying = true;
76
- this.state.isStopped = false;
77
- this.state.isEmitting = true;
78
- if (this.definition.main.prewarm) {
79
- this.prewarm();
80
- }
2
+ constructor(def) {
3
+ this.useGPU = false;
4
+ this.definition = def;
5
+ this.state = this.createInitialState();
6
+ }
7
+ createInitialState() {
8
+ const maxParticles = this.definition.main.maxParticles;
9
+ const particles = [];
10
+ for (let i = 0; i < maxParticles; i++) {
11
+ particles.push(this.createDeadParticle());
81
12
  }
82
- pause() {
83
- this.state.isPaused = true;
13
+ return {
14
+ particles,
15
+ activeCount: 0,
16
+ time: 0,
17
+ isPlaying: false,
18
+ isPaused: false,
19
+ isStopped: true,
20
+ isEmitting: false,
21
+ randomSeed: Math.random() * 4294967295 | 0,
22
+ emitCounter: 0,
23
+ distanceTraveled: 0,
24
+ lastPosition: new Float32Array([0, 0, 0]),
25
+ burstStates: this.definition.emission.bursts.map((b, i) => ({
26
+ burstIndex: i,
27
+ cycleCount: 0,
28
+ nextTime: b.time
29
+ }))
30
+ };
31
+ }
32
+ createDeadParticle() {
33
+ return {
34
+ position: new Float32Array([0, 0, 0]),
35
+ velocity: new Float32Array([0, 0, 0]),
36
+ startColor: new Float32Array([1, 1, 1, 1]),
37
+ color: new Float32Array([1, 1, 1, 1]),
38
+ startSize: new Float32Array([1, 1, 1]),
39
+ size: new Float32Array([1, 1, 1]),
40
+ rotation: new Float32Array([0, 0, 0]),
41
+ angularVelocity: new Float32Array([0, 0, 0]),
42
+ startLifetime: 0,
43
+ remainingLifetime: -1,
44
+ normalizedLifetime: 1,
45
+ randomSeed: Math.random() * 4294967295 | 0,
46
+ startSpeed: 0,
47
+ axisOfRotation: new Float32Array([0, 0, 1]),
48
+ meshIndex: 0,
49
+ customData1: new Float32Array([0, 0, 0, 0]),
50
+ customData2: new Float32Array([0, 0, 0, 0])
51
+ };
52
+ }
53
+ // ─── Playback Control ──────────────────────────────────
54
+ play() {
55
+ if (this.state.isPaused) {
56
+ this.state.isPaused = false;
84
57
  }
85
- stop(clear = true) {
86
- this.state.isPlaying = false;
87
- this.state.isStopped = true;
88
- this.state.isEmitting = false;
89
- if (clear) {
90
- this.clear();
91
- }
58
+ this.state.isPlaying = true;
59
+ this.state.isStopped = false;
60
+ this.state.isEmitting = true;
61
+ if (this.definition.main.prewarm) {
62
+ this.prewarm();
92
63
  }
93
- clear() {
94
- this.state.activeCount = 0;
95
- this.state.time = 0;
96
- this.state.emitCounter = 0;
97
- this.state.distanceTraveled = 0;
98
- for (const particle of this.state.particles) {
99
- particle.remainingLifetime = -1;
100
- }
101
- this.resetBurstStates();
102
- }
103
- resetBurstStates() {
104
- this.state.burstStates = this.definition.emission.bursts.map((b, i) => ({
105
- burstIndex: i,
106
- cycleCount: 0,
107
- nextTime: b.time,
108
- }));
109
- }
110
- prewarm() {
111
- const duration = this.definition.main.duration;
112
- const dt = 1 / 60;
113
- const steps = Math.ceil(duration / dt);
114
- for (let i = 0; i < steps; i++) {
115
- this.simulate(dt);
116
- }
64
+ }
65
+ pause() {
66
+ this.state.isPaused = true;
67
+ }
68
+ stop(clear = true) {
69
+ this.state.isPlaying = false;
70
+ this.state.isStopped = true;
71
+ this.state.isEmitting = false;
72
+ if (clear) {
73
+ this.clear();
117
74
  }
118
- // ─── Emission ──────────────────────────────────────────
119
- emit(count, position) {
120
- for (let i = 0; i < count; i++) {
121
- this.emitSingleParticle(position);
122
- }
75
+ }
76
+ clear() {
77
+ this.state.activeCount = 0;
78
+ this.state.time = 0;
79
+ this.state.emitCounter = 0;
80
+ this.state.distanceTraveled = 0;
81
+ for (const particle of this.state.particles) {
82
+ particle.remainingLifetime = -1;
123
83
  }
124
- emitSingleParticle(overridePosition) {
125
- // Find dead particle
126
- const deadIndex = this.findDeadParticleIndex();
127
- if (deadIndex === -1) {
128
- return false;
129
- }
130
- const particle = this.state.particles[deadIndex];
131
- this.initializeParticle(particle, overridePosition);
132
- this.state.activeCount++;
133
- return true;
134
- }
135
- findDeadParticleIndex() {
136
- for (let i = 0; i < this.state.particles.length; i++) {
137
- if (this.state.particles[i].remainingLifetime < 0) {
138
- return i;
139
- }
140
- }
141
- return -1;
142
- }
143
- initializeParticle(particle, overridePosition) {
144
- const main = this.definition.main;
145
- // Random seed for this particle
146
- particle.randomSeed = (Math.random() * 0xffffffff) | 0;
147
- // Position and velocity from shape
148
- const { position, velocity } = this.sampleEmitterShape();
149
- if (overridePosition) {
150
- particle.position.set(overridePosition);
151
- }
152
- else {
153
- particle.position.set(position);
154
- }
155
- // Initial velocity
156
- const startSpeed = this.evaluateMinMaxCurve(main.startSpeed, 0, particle.randomSeed);
157
- particle.startSpeed = startSpeed;
158
- particle.velocity[0] = velocity[0] * startSpeed;
159
- particle.velocity[1] = velocity[1] * startSpeed;
160
- particle.velocity[2] = velocity[2] * startSpeed;
161
- // Lifetime
162
- particle.startLifetime = this.evaluateMinMaxCurve(main.startLifetime, 0, particle.randomSeed);
163
- particle.remainingLifetime = particle.startLifetime;
164
- particle.normalizedLifetime = 0;
165
- // Size
166
- if (main.startSize3D) {
167
- particle.startSize[0] = this.evaluateMinMaxCurve(main.startSizeX, 0, particle.randomSeed);
168
- particle.startSize[1] = this.evaluateMinMaxCurve(main.startSizeY, 0, particle.randomSeed);
169
- particle.startSize[2] = this.evaluateMinMaxCurve(main.startSizeZ, 0, particle.randomSeed);
170
- }
171
- else {
172
- const size = this.evaluateMinMaxCurve(main.startSize, 0, particle.randomSeed);
173
- particle.startSize.set([size, size, size]);
174
- }
175
- particle.size.set(particle.startSize);
176
- // Rotation
177
- if (main.startRotation3D) {
178
- particle.rotation[0] = this.evaluateMinMaxCurve(main.startRotationX, 0, particle.randomSeed);
179
- particle.rotation[1] = this.evaluateMinMaxCurve(main.startRotationY, 0, particle.randomSeed);
180
- particle.rotation[2] = this.evaluateMinMaxCurve(main.startRotationZ, 0, particle.randomSeed);
181
- }
182
- else {
183
- particle.rotation[2] = this.evaluateMinMaxCurve(main.startRotation, 0, particle.randomSeed);
184
- }
185
- // Flip rotation
186
- if (main.flipRotation > 0 && Math.random() < main.flipRotation) {
187
- particle.rotation[2] += Math.PI;
188
- }
189
- // Color
190
- const color = this.evaluateMinMaxGradient(main.startColor, 0, particle.randomSeed);
191
- particle.startColor.set(color);
192
- particle.color.set(color);
193
- // Angular velocity (will be set by rotationOverLifetime)
194
- particle.angularVelocity.set([0, 0, 0]);
195
- }
196
- sampleEmitterShape() {
197
- const shape = this.definition.shape;
198
- switch (shape.type) {
199
- case 'sphere':
200
- return this.sampleSphere(shape);
201
- case 'hemisphere':
202
- return this.sampleHemisphere(shape);
203
- case 'cone':
204
- return this.sampleCone(shape);
205
- case 'box':
206
- return this.sampleBox(shape);
207
- case 'circle':
208
- return this.sampleCircle(shape);
209
- default:
210
- return { position: [0, 0, 0], velocity: [0, 1, 0] };
211
- }
84
+ this.resetBurstStates();
85
+ }
86
+ resetBurstStates() {
87
+ this.state.burstStates = this.definition.emission.bursts.map((b, i) => ({
88
+ burstIndex: i,
89
+ cycleCount: 0,
90
+ nextTime: b.time
91
+ }));
92
+ }
93
+ prewarm() {
94
+ const duration = this.definition.main.duration;
95
+ const dt = 1 / 60;
96
+ const steps = Math.ceil(duration / dt);
97
+ for (let i = 0; i < steps; i++) {
98
+ this.simulate(dt);
99
+ }
100
+ }
101
+ // ─── Emission ──────────────────────────────────────────
102
+ emit(count, position) {
103
+ for (let i = 0; i < count; i++) {
104
+ this.emitSingleParticle(position);
105
+ }
106
+ }
107
+ emitSingleParticle(overridePosition) {
108
+ const deadIndex = this.findDeadParticleIndex();
109
+ if (deadIndex === -1) {
110
+ return false;
111
+ }
112
+ const particle = this.state.particles[deadIndex];
113
+ this.initializeParticle(particle, overridePosition);
114
+ this.state.activeCount++;
115
+ return true;
116
+ }
117
+ findDeadParticleIndex() {
118
+ for (let i = 0; i < this.state.particles.length; i++) {
119
+ if (this.state.particles[i].remainingLifetime < 0) {
120
+ return i;
121
+ }
212
122
  }
213
- sampleSphere(shape) {
214
- // Random direction
215
- const theta = Math.random() * 2 * Math.PI;
216
- const phi = Math.acos(2 * Math.random() - 1);
217
- const sinPhi = Math.sin(phi);
218
- const dir = [sinPhi * Math.cos(theta), sinPhi * Math.sin(theta), Math.cos(phi)];
219
- // Position on/in sphere
220
- const radiusFactor = shape.radiusThickness === 1
221
- ? Math.cbrt(Math.random())
222
- : 1 - Math.random() * shape.radiusThickness;
223
- const r = shape.radius * radiusFactor;
224
- const position = [
225
- dir[0] * r + shape.position[0],
226
- dir[1] * r + shape.position[1],
227
- dir[2] * r + shape.position[2],
123
+ return -1;
124
+ }
125
+ initializeParticle(particle, overridePosition) {
126
+ const main = this.definition.main;
127
+ particle.randomSeed = Math.random() * 4294967295 | 0;
128
+ const { position, velocity } = this.sampleEmitterShape();
129
+ if (overridePosition) {
130
+ particle.position.set(overridePosition);
131
+ } else {
132
+ particle.position.set(position);
133
+ }
134
+ const startSpeed = this.evaluateMinMaxCurve(main.startSpeed, 0, particle.randomSeed);
135
+ particle.startSpeed = startSpeed;
136
+ particle.velocity[0] = velocity[0] * startSpeed;
137
+ particle.velocity[1] = velocity[1] * startSpeed;
138
+ particle.velocity[2] = velocity[2] * startSpeed;
139
+ particle.startLifetime = this.evaluateMinMaxCurve(main.startLifetime, 0, particle.randomSeed);
140
+ particle.remainingLifetime = particle.startLifetime;
141
+ particle.normalizedLifetime = 0;
142
+ if (main.startSize3D) {
143
+ particle.startSize[0] = this.evaluateMinMaxCurve(main.startSizeX, 0, particle.randomSeed);
144
+ particle.startSize[1] = this.evaluateMinMaxCurve(main.startSizeY, 0, particle.randomSeed);
145
+ particle.startSize[2] = this.evaluateMinMaxCurve(main.startSizeZ, 0, particle.randomSeed);
146
+ } else {
147
+ const size = this.evaluateMinMaxCurve(main.startSize, 0, particle.randomSeed);
148
+ particle.startSize.set([size, size, size]);
149
+ }
150
+ particle.size.set(particle.startSize);
151
+ if (main.startRotation3D) {
152
+ particle.rotation[0] = this.evaluateMinMaxCurve(main.startRotationX, 0, particle.randomSeed);
153
+ particle.rotation[1] = this.evaluateMinMaxCurve(main.startRotationY, 0, particle.randomSeed);
154
+ particle.rotation[2] = this.evaluateMinMaxCurve(main.startRotationZ, 0, particle.randomSeed);
155
+ } else {
156
+ particle.rotation[2] = this.evaluateMinMaxCurve(main.startRotation, 0, particle.randomSeed);
157
+ }
158
+ if (main.flipRotation > 0 && Math.random() < main.flipRotation) {
159
+ particle.rotation[2] += Math.PI;
160
+ }
161
+ const color = this.evaluateMinMaxGradient(main.startColor, 0, particle.randomSeed);
162
+ particle.startColor.set(color);
163
+ particle.color.set(color);
164
+ particle.angularVelocity.set([0, 0, 0]);
165
+ }
166
+ sampleEmitterShape() {
167
+ const shape = this.definition.shape;
168
+ switch (shape.type) {
169
+ case "sphere":
170
+ return this.sampleSphere(shape);
171
+ case "hemisphere":
172
+ return this.sampleHemisphere(shape);
173
+ case "cone":
174
+ return this.sampleCone(shape);
175
+ case "box":
176
+ return this.sampleBox(shape);
177
+ case "circle":
178
+ return this.sampleCircle(shape);
179
+ default:
180
+ return { position: [0, 0, 0], velocity: [0, 1, 0] };
181
+ }
182
+ }
183
+ sampleSphere(shape) {
184
+ const theta = Math.random() * 2 * Math.PI;
185
+ const phi = Math.acos(2 * Math.random() - 1);
186
+ const sinPhi = Math.sin(phi);
187
+ const dir = [sinPhi * Math.cos(theta), sinPhi * Math.sin(theta), Math.cos(phi)];
188
+ const radiusFactor = shape.radiusThickness === 1 ? Math.cbrt(Math.random()) : 1 - Math.random() * shape.radiusThickness;
189
+ const r = shape.radius * radiusFactor;
190
+ const position = [
191
+ dir[0] * r + shape.position[0],
192
+ dir[1] * r + shape.position[1],
193
+ dir[2] * r + shape.position[2]
194
+ ];
195
+ return { position, velocity: dir };
196
+ }
197
+ sampleHemisphere(shape) {
198
+ const theta = Math.random() * 2 * Math.PI;
199
+ const phi = Math.acos(Math.random());
200
+ const sinPhi = Math.sin(phi);
201
+ const dir = [sinPhi * Math.cos(theta), Math.cos(phi), sinPhi * Math.sin(theta)];
202
+ const radiusFactor = shape.radiusThickness === 1 ? Math.cbrt(Math.random()) : 1 - Math.random() * shape.radiusThickness;
203
+ const r = shape.radius * radiusFactor;
204
+ const position = [
205
+ dir[0] * r + shape.position[0],
206
+ dir[1] * r + shape.position[1],
207
+ dir[2] * r + shape.position[2]
208
+ ];
209
+ return { position, velocity: dir };
210
+ }
211
+ sampleCone(shape) {
212
+ const angleRad = shape.angle * Math.PI / 180;
213
+ const theta = Math.random() * 2 * Math.PI;
214
+ const coneAngle = Math.random() * angleRad;
215
+ const sinAngle = Math.sin(coneAngle);
216
+ const cosAngle = Math.cos(coneAngle);
217
+ const dir = [sinAngle * Math.cos(theta), cosAngle, sinAngle * Math.sin(theta)];
218
+ let position;
219
+ switch (shape.emitFrom) {
220
+ case "base": {
221
+ const r = shape.radius * Math.sqrt(Math.random());
222
+ position = [
223
+ r * Math.cos(theta) + shape.position[0],
224
+ shape.position[1],
225
+ r * Math.sin(theta) + shape.position[2]
228
226
  ];
229
- return { position, velocity: dir };
230
- }
231
- sampleHemisphere(shape) {
232
- const theta = Math.random() * 2 * Math.PI;
233
- const phi = Math.acos(Math.random()); // 0 to PI/2
234
- const sinPhi = Math.sin(phi);
235
- const dir = [sinPhi * Math.cos(theta), Math.cos(phi), sinPhi * Math.sin(theta)];
236
- const radiusFactor = shape.radiusThickness === 1
237
- ? Math.cbrt(Math.random())
238
- : 1 - Math.random() * shape.radiusThickness;
239
- const r = shape.radius * radiusFactor;
240
- const position = [
241
- dir[0] * r + shape.position[0],
242
- dir[1] * r + shape.position[1],
243
- dir[2] * r + shape.position[2],
227
+ break;
228
+ }
229
+ case "volume": {
230
+ const height = Math.random() * shape.length;
231
+ const radiusAtHeight = shape.radius + height * Math.tan(angleRad);
232
+ const rV = radiusAtHeight * Math.sqrt(Math.random());
233
+ position = [
234
+ rV * Math.cos(theta) + shape.position[0],
235
+ height + shape.position[1],
236
+ rV * Math.sin(theta) + shape.position[2]
244
237
  ];
245
- return { position, velocity: dir };
246
- }
247
- sampleCone(shape) {
248
- const angleRad = (shape.angle * Math.PI) / 180;
249
- const theta = Math.random() * 2 * Math.PI;
250
- // Random angle within cone
251
- const coneAngle = Math.random() * angleRad;
252
- const sinAngle = Math.sin(coneAngle);
253
- const cosAngle = Math.cos(coneAngle);
254
- const dir = [sinAngle * Math.cos(theta), cosAngle, sinAngle * Math.sin(theta)];
255
- // Position based on emitFrom
256
- let position;
257
- switch (shape.emitFrom) {
258
- case 'base': {
259
- const r = shape.radius * Math.sqrt(Math.random());
260
- position = [
261
- r * Math.cos(theta) + shape.position[0],
262
- shape.position[1],
263
- r * Math.sin(theta) + shape.position[2],
264
- ];
265
- break;
266
- }
267
- case 'volume': {
268
- const height = Math.random() * shape.length;
269
- const radiusAtHeight = shape.radius + height * Math.tan(angleRad);
270
- const rV = radiusAtHeight * Math.sqrt(Math.random());
271
- position = [
272
- rV * Math.cos(theta) + shape.position[0],
273
- height + shape.position[1],
274
- rV * Math.sin(theta) + shape.position[2],
275
- ];
276
- break;
277
- }
278
- default:
279
- position = [...shape.position];
280
- }
281
- return { position, velocity: dir };
282
- }
283
- sampleBox(shape) {
284
- const hw = shape.scale[0] / 2;
285
- const hh = shape.scale[1] / 2;
286
- const hd = shape.scale[2] / 2;
287
- let position;
288
- switch (shape.emitFrom) {
289
- case 'volume':
290
- position = [
291
- (Math.random() * 2 - 1) * hw + shape.position[0],
292
- (Math.random() * 2 - 1) * hh + shape.position[1],
293
- (Math.random() * 2 - 1) * hd + shape.position[2],
294
- ];
295
- break;
296
- case 'shell': {
297
- // Random face
298
- const face = Math.floor(Math.random() * 6);
299
- const u = Math.random() * 2 - 1;
300
- const v = Math.random() * 2 - 1;
301
- switch (face) {
302
- case 0:
303
- position = [hw, u * hh, v * hd];
304
- break;
305
- case 1:
306
- position = [-hw, u * hh, v * hd];
307
- break;
308
- case 2:
309
- position = [u * hw, hh, v * hd];
310
- break;
311
- case 3:
312
- position = [u * hw, -hh, v * hd];
313
- break;
314
- case 4:
315
- position = [u * hw, v * hh, hd];
316
- break;
317
- default:
318
- position = [u * hw, v * hh, -hd];
319
- break;
320
- }
321
- position[0] += shape.position[0];
322
- position[1] += shape.position[1];
323
- position[2] += shape.position[2];
324
- break;
325
- }
326
- default:
327
- position = [...shape.position];
328
- }
329
- return { position, velocity: [0, 1, 0] };
330
- }
331
- sampleCircle(shape) {
332
- const theta = Math.random() * 2 * Math.PI;
333
- const radiusFactor = shape.radiusThickness === 1
334
- ? Math.sqrt(Math.random())
335
- : 1 - Math.random() * shape.radiusThickness;
336
- const r = shape.radius * radiusFactor;
337
- const position = [
338
- r * Math.cos(theta) + shape.position[0],
339
- shape.position[1],
340
- r * Math.sin(theta) + shape.position[2],
238
+ break;
239
+ }
240
+ default:
241
+ position = [...shape.position];
242
+ }
243
+ return { position, velocity: dir };
244
+ }
245
+ sampleBox(shape) {
246
+ const hw = shape.scale[0] / 2;
247
+ const hh = shape.scale[1] / 2;
248
+ const hd = shape.scale[2] / 2;
249
+ let position;
250
+ switch (shape.emitFrom) {
251
+ case "volume":
252
+ position = [
253
+ (Math.random() * 2 - 1) * hw + shape.position[0],
254
+ (Math.random() * 2 - 1) * hh + shape.position[1],
255
+ (Math.random() * 2 - 1) * hd + shape.position[2]
341
256
  ];
342
- return { position, velocity: [0, 1, 0] };
257
+ break;
258
+ case "shell": {
259
+ const face = Math.floor(Math.random() * 6);
260
+ const u = Math.random() * 2 - 1;
261
+ const v = Math.random() * 2 - 1;
262
+ switch (face) {
263
+ case 0:
264
+ position = [hw, u * hh, v * hd];
265
+ break;
266
+ case 1:
267
+ position = [-hw, u * hh, v * hd];
268
+ break;
269
+ case 2:
270
+ position = [u * hw, hh, v * hd];
271
+ break;
272
+ case 3:
273
+ position = [u * hw, -hh, v * hd];
274
+ break;
275
+ case 4:
276
+ position = [u * hw, v * hh, hd];
277
+ break;
278
+ default:
279
+ position = [u * hw, v * hh, -hd];
280
+ break;
281
+ }
282
+ position[0] += shape.position[0];
283
+ position[1] += shape.position[1];
284
+ position[2] += shape.position[2];
285
+ break;
286
+ }
287
+ default:
288
+ position = [...shape.position];
343
289
  }
344
- // ─── Simulation ────────────────────────────────────────
345
- simulate(deltaTime) {
346
- if (!this.state.isPlaying || this.state.isPaused) {
347
- return;
348
- }
349
- const dt = deltaTime * this.definition.main.simulationSpeed;
350
- // Update time
351
- this.state.time += dt;
352
- // Handle looping
353
- if (this.definition.main.looping && this.state.time >= this.definition.main.duration) {
354
- this.state.time = this.state.time % this.definition.main.duration;
355
- this.resetBurstStates();
356
- }
357
- // Emit particles
358
- if (this.state.isEmitting) {
359
- this.processEmission(dt);
360
- }
361
- // Update particles
362
- for (let i = 0; i < this.state.particles.length; i++) {
363
- const particle = this.state.particles[i];
364
- if (particle.remainingLifetime >= 0) {
365
- this.updateParticle(particle, dt);
366
- }
367
- }
290
+ return { position, velocity: [0, 1, 0] };
291
+ }
292
+ sampleCircle(shape) {
293
+ const theta = Math.random() * 2 * Math.PI;
294
+ const radiusFactor = shape.radiusThickness === 1 ? Math.sqrt(Math.random()) : 1 - Math.random() * shape.radiusThickness;
295
+ const r = shape.radius * radiusFactor;
296
+ const position = [
297
+ r * Math.cos(theta) + shape.position[0],
298
+ shape.position[1],
299
+ r * Math.sin(theta) + shape.position[2]
300
+ ];
301
+ return { position, velocity: [0, 1, 0] };
302
+ }
303
+ // ─── Simulation ────────────────────────────────────────
304
+ simulate(deltaTime) {
305
+ if (!this.state.isPlaying || this.state.isPaused) {
306
+ return;
368
307
  }
369
- processEmission(dt) {
370
- const emission = this.definition.emission;
371
- if (!emission.enabled) {
372
- return;
373
- }
374
- // Rate over time
375
- const rateOverTime = this.evaluateMinMaxCurve(emission.rateOverTime, this.normalizedTime(), 0);
376
- this.state.emitCounter += rateOverTime * dt;
377
- while (this.state.emitCounter >= 1) {
378
- this.emitSingleParticle();
379
- this.state.emitCounter -= 1;
380
- }
381
- // Bursts
382
- for (const burstState of this.state.burstStates) {
383
- const burst = emission.bursts[burstState.burstIndex];
384
- while (this.state.time >= burstState.nextTime && burstState.cycleCount < burst.cycles) {
385
- // Probability check
386
- if (Math.random() < burst.probability) {
387
- const count = Math.round(this.evaluateMinMaxCurve(burst.count, 0, (Math.random() * 0xffffffff) | 0));
388
- this.emit(count);
389
- }
390
- burstState.cycleCount++;
391
- burstState.nextTime += burst.interval;
392
- }
393
- }
308
+ const dt = deltaTime * this.definition.main.simulationSpeed;
309
+ this.state.time += dt;
310
+ if (this.definition.main.looping && this.state.time >= this.definition.main.duration) {
311
+ this.state.time = this.state.time % this.definition.main.duration;
312
+ this.resetBurstStates();
394
313
  }
395
- updateParticle(particle, dt) {
396
- // Update lifetime
397
- particle.remainingLifetime -= dt;
398
- if (particle.remainingLifetime < 0) {
399
- // Particle died
400
- this.state.activeCount--;
401
- // Trigger sub-emitter death
402
- this.triggerSubEmitters('death', particle);
403
- return;
404
- }
405
- particle.normalizedLifetime = 1 - particle.remainingLifetime / particle.startLifetime;
406
- // Apply forces
407
- this.applyForces(particle, dt);
408
- // Apply velocity/force over lifetime modules
409
- this.applyVelocityOverLifetime(particle, dt);
410
- this.applyForceOverLifetime(particle, dt);
411
- this.applyLimitVelocityOverLifetime(particle, dt);
412
- // Integrate position
413
- particle.position[0] += particle.velocity[0] * dt;
414
- particle.position[1] += particle.velocity[1] * dt;
415
- particle.position[2] += particle.velocity[2] * dt;
416
- // Apply noise
417
- this.applyNoise(particle, dt);
418
- // Update visual properties
419
- this.applyColorOverLifetime(particle);
420
- this.applySizeOverLifetime(particle);
421
- this.applyRotationOverLifetime(particle, dt);
422
- // Speed-based modifications
423
- this.applyColorBySpeed(particle);
424
- this.applySizeBySpeed(particle);
425
- this.applyRotationBySpeed(particle, dt);
426
- // Handle collision
427
- if (this.definition.collision.enabled) {
428
- this.handleCollision(particle);
429
- }
314
+ if (this.state.isEmitting) {
315
+ this.processEmission(dt);
430
316
  }
431
- applyForces(particle, dt) {
432
- const main = this.definition.main;
433
- // Gravity
434
- const gravity = this.evaluateMinMaxCurve(main.gravityModifier, particle.normalizedLifetime, particle.randomSeed);
435
- particle.velocity[1] -= 9.81 * gravity * dt;
436
- // External force fields
437
- if (this.definition.externalForces.enabled) {
438
- for (const field of this.definition.externalForces.forceFields) {
439
- this.applyForceField(particle, field, dt);
440
- }
441
- }
317
+ for (let i = 0; i < this.state.particles.length; i++) {
318
+ const particle = this.state.particles[i];
319
+ if (particle.remainingLifetime >= 0) {
320
+ this.updateParticle(particle, dt);
321
+ }
442
322
  }
443
- applyForceField(particle, field, dt) {
444
- switch (field.type) {
445
- case 'gravity': {
446
- const strength = field.strength;
447
- particle.velocity[0] += field.direction[0] * strength * dt;
448
- particle.velocity[1] += field.direction[1] * strength * dt;
449
- particle.velocity[2] += field.direction[2] * strength * dt;
450
- break;
451
- }
452
- case 'directional': {
453
- const dirStrength = this.evaluateMinMaxCurve(field.strength, particle.normalizedLifetime, particle.randomSeed);
454
- particle.velocity[0] += field.direction[0] * dirStrength * dt;
455
- particle.velocity[1] += field.direction[1] * dirStrength * dt;
456
- particle.velocity[2] += field.direction[2] * dirStrength * dt;
457
- break;
458
- }
459
- case 'vortex': {
460
- const vortexStrength = this.evaluateMinMaxCurve(field.strength, particle.normalizedLifetime, particle.randomSeed);
461
- const toCenter = [
462
- field.center[0] - particle.position[0],
463
- field.center[1] - particle.position[1],
464
- field.center[2] - particle.position[2],
465
- ];
466
- const dist = Math.sqrt(toCenter[0] ** 2 + toCenter[1] ** 2 + toCenter[2] ** 2);
467
- if (dist > 0.001 && dist < field.radius) {
468
- // Tangential force (cross product with axis)
469
- const tangent = [
470
- field.axis[1] * toCenter[2] - field.axis[2] * toCenter[1],
471
- field.axis[2] * toCenter[0] - field.axis[0] * toCenter[2],
472
- field.axis[0] * toCenter[1] - field.axis[1] * toCenter[0],
473
- ];
474
- const tangentLen = Math.sqrt(tangent[0] ** 2 + tangent[1] ** 2 + tangent[2] ** 2);
475
- if (tangentLen > 0.001) {
476
- const factor = (vortexStrength * (1 - dist / field.radius)) / tangentLen;
477
- particle.velocity[0] += tangent[0] * factor * dt;
478
- particle.velocity[1] += tangent[1] * factor * dt;
479
- particle.velocity[2] += tangent[2] * factor * dt;
480
- }
481
- // Attraction
482
- if (field.attractionStrength > 0) {
483
- const attractFactor = (field.attractionStrength * (1 - dist / field.radius)) / dist;
484
- particle.velocity[0] += toCenter[0] * attractFactor * dt;
485
- particle.velocity[1] += toCenter[1] * attractFactor * dt;
486
- particle.velocity[2] += toCenter[2] * attractFactor * dt;
487
- }
488
- }
489
- break;
490
- }
491
- case 'drag': {
492
- const dragCoeff = this.evaluateMinMaxCurve(field.coefficient, particle.normalizedLifetime, particle.randomSeed);
493
- let drag = dragCoeff;
494
- if (field.multiplyBySize) {
495
- const sizeMag = Math.sqrt(particle.size[0] ** 2 + particle.size[1] ** 2 + particle.size[2] ** 2);
496
- drag *= sizeMag;
497
- }
498
- if (field.multiplyByVelocity) {
499
- const velMag = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
500
- drag *= velMag;
501
- }
502
- const factor = Math.max(0, 1 - drag * dt);
503
- particle.velocity[0] *= factor;
504
- particle.velocity[1] *= factor;
505
- particle.velocity[2] *= factor;
506
- break;
507
- }
508
- case 'attractor': {
509
- const attractorStrength = this.evaluateMinMaxCurve(field.strength, particle.normalizedLifetime, particle.randomSeed);
510
- const toAttractor = [
511
- field.position[0] - particle.position[0],
512
- field.position[1] - particle.position[1],
513
- field.position[2] - particle.position[2],
514
- ];
515
- const attractorDist = Math.sqrt(toAttractor[0] ** 2 + toAttractor[1] ** 2 + toAttractor[2] ** 2);
516
- if (attractorDist > 0.001 && attractorDist < field.radius) {
517
- const falloff = 1 - attractorDist / field.radius;
518
- const force = (attractorStrength * falloff) / attractorDist;
519
- particle.velocity[0] += toAttractor[0] * force * dt;
520
- particle.velocity[1] += toAttractor[1] * force * dt;
521
- particle.velocity[2] += toAttractor[2] * force * dt;
522
- if (field.killOnArrive && attractorDist < 0.1) {
523
- particle.remainingLifetime = -1;
524
- }
525
- }
526
- break;
527
- }
528
- }
323
+ }
324
+ processEmission(dt) {
325
+ const emission = this.definition.emission;
326
+ if (!emission.enabled) {
327
+ return;
529
328
  }
530
- applyVelocityOverLifetime(particle, dt) {
531
- const vol = this.definition.velocityOverLifetime;
532
- if (!vol.enabled) {
533
- return;
534
- }
535
- // Linear velocity
536
- if (vol.linear.mode !== 'constant' ||
537
- vol.linear.value[0] !== 0 ||
538
- vol.linear.value[1] !== 0 ||
539
- vol.linear.value[2] !== 0) {
540
- const linear = this.evaluateMinMaxVector3(vol.linear, particle.normalizedLifetime, particle.randomSeed);
541
- particle.velocity[0] += linear[0] * dt;
542
- particle.velocity[1] += linear[1] * dt;
543
- particle.velocity[2] += linear[2] * dt;
544
- }
545
- // Radial velocity
546
- const radial = this.evaluateMinMaxCurve(vol.radial, particle.normalizedLifetime, particle.randomSeed);
547
- if (radial !== 0) {
548
- const len = Math.sqrt(particle.position[0] ** 2 + particle.position[1] ** 2 + particle.position[2] ** 2);
549
- if (len > 0.001) {
550
- particle.velocity[0] += (particle.position[0] / len) * radial * dt;
551
- particle.velocity[1] += (particle.position[1] / len) * radial * dt;
552
- particle.velocity[2] += (particle.position[2] / len) * radial * dt;
553
- }
554
- }
555
- // Speed modifier
556
- const speedMod = this.evaluateMinMaxCurve(vol.speedModifier, particle.normalizedLifetime, particle.randomSeed);
557
- particle.velocity[0] *= speedMod;
558
- particle.velocity[1] *= speedMod;
559
- particle.velocity[2] *= speedMod;
560
- }
561
- applyForceOverLifetime(particle, dt) {
562
- const fol = this.definition.forceOverLifetime;
563
- if (!fol.enabled) {
564
- return;
565
- }
566
- const seed = fol.randomize ? (Math.random() * 0xffffffff) | 0 : particle.randomSeed;
567
- particle.velocity[0] += this.evaluateMinMaxCurve(fol.x, particle.normalizedLifetime, seed) * dt;
568
- particle.velocity[1] += this.evaluateMinMaxCurve(fol.y, particle.normalizedLifetime, seed) * dt;
569
- particle.velocity[2] += this.evaluateMinMaxCurve(fol.z, particle.normalizedLifetime, seed) * dt;
570
- }
571
- applyLimitVelocityOverLifetime(particle, dt) {
572
- const lvol = this.definition.limitVelocityOverLifetime;
573
- if (!lvol.enabled) {
574
- return;
575
- }
576
- const velMag = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
577
- let maxSpeed;
578
- if (lvol.separateAxes) {
579
- // Per-axis limit (simplified)
580
- maxSpeed = this.evaluateMinMaxCurve(lvol.speed, particle.normalizedLifetime, particle.randomSeed);
581
- }
582
- else {
583
- maxSpeed = this.evaluateMinMaxCurve(lvol.speed, particle.normalizedLifetime, particle.randomSeed);
584
- }
585
- if (velMag > maxSpeed) {
586
- const dampenFactor = 1 - lvol.dampen;
587
- const targetSpeed = maxSpeed + (velMag - maxSpeed) * dampenFactor;
588
- const scale = targetSpeed / velMag;
589
- particle.velocity[0] *= scale;
590
- particle.velocity[1] *= scale;
591
- particle.velocity[2] *= scale;
592
- }
593
- // Drag
594
- const drag = this.evaluateMinMaxCurve(lvol.drag, particle.normalizedLifetime, particle.randomSeed);
595
- if (drag > 0) {
596
- const dragFactor = Math.max(0, 1 - drag * dt);
597
- particle.velocity[0] *= dragFactor;
598
- particle.velocity[1] *= dragFactor;
599
- particle.velocity[2] *= dragFactor;
600
- }
329
+ const rateOverTime = this.evaluateMinMaxCurve(emission.rateOverTime, this.normalizedTime(), 0);
330
+ this.state.emitCounter += rateOverTime * dt;
331
+ while (this.state.emitCounter >= 1) {
332
+ this.emitSingleParticle();
333
+ this.state.emitCounter -= 1;
601
334
  }
602
- applyNoise(particle, dt) {
603
- const noise = this.definition.noise;
604
- if (!noise.enabled) {
605
- return;
606
- }
607
- const strength = this.evaluateMinMaxCurve(noise.strength, particle.normalizedLifetime, particle.randomSeed);
608
- const freq = noise.frequency;
609
- const scroll = noise.scrollSpeed * this.state.time;
610
- // Simple noise approximation
611
- const nx = particle.position[0] * freq + scroll;
612
- const ny = particle.position[1] * freq + scroll;
613
- const nz = particle.position[2] * freq + scroll;
614
- // Pseudo-noise
615
- const noiseX = (Math.sin(nx * 12.9898 + ny * 78.233) * 43758.5453) % 1;
616
- const noiseY = (Math.sin(ny * 12.9898 + nz * 78.233) * 43758.5453) % 1;
617
- const noiseZ = (Math.sin(nz * 12.9898 + nx * 78.233) * 43758.5453) % 1;
618
- const posAmount = this.evaluateMinMaxCurve(noise.positionAmount, particle.normalizedLifetime, particle.randomSeed);
619
- particle.position[0] += (noiseX * 2 - 1) * strength * posAmount * dt;
620
- particle.position[1] += (noiseY * 2 - 1) * strength * posAmount * dt;
621
- particle.position[2] += (noiseZ * 2 - 1) * strength * posAmount * dt;
622
- }
623
- applyColorOverLifetime(particle) {
624
- const col = this.definition.colorOverLifetime;
625
- if (!col.enabled) {
626
- return;
627
- }
628
- const color = this.evaluateMinMaxGradient(col.color, particle.normalizedLifetime, particle.randomSeed);
629
- // Multiply with start color
630
- particle.color[0] = particle.startColor[0] * color[0];
631
- particle.color[1] = particle.startColor[1] * color[1];
632
- particle.color[2] = particle.startColor[2] * color[2];
633
- particle.color[3] = particle.startColor[3] * color[3];
634
- }
635
- applySizeOverLifetime(particle) {
636
- const sol = this.definition.sizeOverLifetime;
637
- if (!sol.enabled) {
638
- return;
639
- }
640
- if (sol.separateAxes) {
641
- particle.size[0] =
642
- particle.startSize[0] *
643
- this.evaluateMinMaxCurve(sol.sizeX, particle.normalizedLifetime, particle.randomSeed);
644
- particle.size[1] =
645
- particle.startSize[1] *
646
- this.evaluateMinMaxCurve(sol.sizeY, particle.normalizedLifetime, particle.randomSeed);
647
- particle.size[2] =
648
- particle.startSize[2] *
649
- this.evaluateMinMaxCurve(sol.sizeZ, particle.normalizedLifetime, particle.randomSeed);
650
- }
651
- else {
652
- const scale = this.evaluateMinMaxCurve(sol.size, particle.normalizedLifetime, particle.randomSeed);
653
- particle.size[0] = particle.startSize[0] * scale;
654
- particle.size[1] = particle.startSize[1] * scale;
655
- particle.size[2] = particle.startSize[2] * scale;
656
- }
335
+ for (const burstState of this.state.burstStates) {
336
+ const burst = emission.bursts[burstState.burstIndex];
337
+ while (this.state.time >= burstState.nextTime && burstState.cycleCount < burst.cycles) {
338
+ if (Math.random() < burst.probability) {
339
+ const count = Math.round(
340
+ this.evaluateMinMaxCurve(burst.count, 0, Math.random() * 4294967295 | 0)
341
+ );
342
+ this.emit(count);
343
+ }
344
+ burstState.cycleCount++;
345
+ burstState.nextTime += burst.interval;
346
+ }
657
347
  }
658
- applyRotationOverLifetime(particle, dt) {
659
- const rol = this.definition.rotationOverLifetime;
660
- if (!rol.enabled) {
661
- return;
662
- }
663
- if (rol.separateAxes) {
664
- particle.rotation[0] +=
665
- this.evaluateMinMaxCurve(rol.angularVelocityX, particle.normalizedLifetime, particle.randomSeed) * dt;
666
- particle.rotation[1] +=
667
- this.evaluateMinMaxCurve(rol.angularVelocityY, particle.normalizedLifetime, particle.randomSeed) * dt;
668
- particle.rotation[2] +=
669
- this.evaluateMinMaxCurve(rol.angularVelocityZ, particle.normalizedLifetime, particle.randomSeed) * dt;
670
- }
671
- else {
672
- particle.rotation[2] +=
673
- this.evaluateMinMaxCurve(rol.angularVelocity, particle.normalizedLifetime, particle.randomSeed) * dt;
674
- }
348
+ }
349
+ updateParticle(particle, dt) {
350
+ particle.remainingLifetime -= dt;
351
+ if (particle.remainingLifetime < 0) {
352
+ this.state.activeCount--;
353
+ this.triggerSubEmitters("death", particle);
354
+ return;
675
355
  }
676
- applyColorBySpeed(particle) {
677
- const cbs = this.definition.colorBySpeed;
678
- if (!cbs.enabled) {
679
- return;
680
- }
681
- const speed = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
682
- const t = Math.min(1, Math.max(0, (speed - cbs.speedRange[0]) / (cbs.speedRange[1] - cbs.speedRange[0])));
683
- const color = this.evaluateMinMaxGradient(cbs.color, t, particle.randomSeed);
684
- particle.color[0] *= color[0];
685
- particle.color[1] *= color[1];
686
- particle.color[2] *= color[2];
687
- particle.color[3] *= color[3];
688
- }
689
- applySizeBySpeed(particle) {
690
- const sbs = this.definition.sizeBySpeed;
691
- if (!sbs.enabled) {
692
- return;
693
- }
694
- const speed = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
695
- const t = Math.min(1, Math.max(0, (speed - sbs.speedRange[0]) / (sbs.speedRange[1] - sbs.speedRange[0])));
696
- if (sbs.separateAxes) {
697
- particle.size[0] *= this.evaluateMinMaxCurve(sbs.sizeX, t, particle.randomSeed);
698
- particle.size[1] *= this.evaluateMinMaxCurve(sbs.sizeY, t, particle.randomSeed);
699
- particle.size[2] *= this.evaluateMinMaxCurve(sbs.sizeZ, t, particle.randomSeed);
700
- }
701
- else {
702
- const scale = this.evaluateMinMaxCurve(sbs.size, t, particle.randomSeed);
703
- particle.size[0] *= scale;
704
- particle.size[1] *= scale;
705
- particle.size[2] *= scale;
706
- }
356
+ particle.normalizedLifetime = 1 - particle.remainingLifetime / particle.startLifetime;
357
+ this.applyForces(particle, dt);
358
+ this.applyVelocityOverLifetime(particle, dt);
359
+ this.applyForceOverLifetime(particle, dt);
360
+ this.applyLimitVelocityOverLifetime(particle, dt);
361
+ particle.position[0] += particle.velocity[0] * dt;
362
+ particle.position[1] += particle.velocity[1] * dt;
363
+ particle.position[2] += particle.velocity[2] * dt;
364
+ this.applyNoise(particle, dt);
365
+ this.applyColorOverLifetime(particle);
366
+ this.applySizeOverLifetime(particle);
367
+ this.applyRotationOverLifetime(particle, dt);
368
+ this.applyColorBySpeed(particle);
369
+ this.applySizeBySpeed(particle);
370
+ this.applyRotationBySpeed(particle, dt);
371
+ if (this.definition.collision.enabled) {
372
+ this.handleCollision(particle);
707
373
  }
708
- applyRotationBySpeed(particle, dt) {
709
- const rbs = this.definition.rotationBySpeed;
710
- if (!rbs.enabled) {
711
- return;
712
- }
713
- const speed = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
714
- const t = Math.min(1, Math.max(0, (speed - rbs.speedRange[0]) / (rbs.speedRange[1] - rbs.speedRange[0])));
715
- if (rbs.separateAxes) {
716
- particle.rotation[0] +=
717
- this.evaluateMinMaxCurve(rbs.angularVelocityX, t, particle.randomSeed) * dt;
718
- particle.rotation[1] +=
719
- this.evaluateMinMaxCurve(rbs.angularVelocityY, t, particle.randomSeed) * dt;
720
- particle.rotation[2] +=
721
- this.evaluateMinMaxCurve(rbs.angularVelocityZ, t, particle.randomSeed) * dt;
722
- }
723
- else {
724
- particle.rotation[2] +=
725
- this.evaluateMinMaxCurve(rbs.angularVelocity, t, particle.randomSeed) * dt;
726
- }
374
+ }
375
+ applyForces(particle, dt) {
376
+ const main = this.definition.main;
377
+ const gravity = this.evaluateMinMaxCurve(
378
+ main.gravityModifier,
379
+ particle.normalizedLifetime,
380
+ particle.randomSeed
381
+ );
382
+ particle.velocity[1] -= 9.81 * gravity * dt;
383
+ if (this.definition.externalForces.enabled) {
384
+ for (const field of this.definition.externalForces.forceFields) {
385
+ this.applyForceField(particle, field, dt);
386
+ }
727
387
  }
728
- handleCollision(particle) {
729
- const collision = this.definition.collision;
730
- if (collision.type === 'planes') {
731
- for (const plane of collision.planes) {
732
- const dot = particle.position[0] * plane.normal[0] +
733
- particle.position[1] * plane.normal[1] +
734
- particle.position[2] * plane.normal[2];
735
- const planeDist = plane.position[0] * plane.normal[0] +
736
- plane.position[1] * plane.normal[1] +
737
- plane.position[2] * plane.normal[2];
738
- if (dot < planeDist) {
739
- // Below plane - collision occurred
740
- const velDotN = particle.velocity[0] * plane.normal[0] +
741
- particle.velocity[1] * plane.normal[1] +
742
- particle.velocity[2] * plane.normal[2];
743
- const bounce = this.evaluateMinMaxCurve(collision.bounce, particle.normalizedLifetime, particle.randomSeed);
744
- const dampen = this.evaluateMinMaxCurve(collision.dampen, particle.normalizedLifetime, particle.randomSeed);
745
- // Reflect velocity
746
- particle.velocity[0] -= (1 + bounce) * velDotN * plane.normal[0];
747
- particle.velocity[1] -= (1 + bounce) * velDotN * plane.normal[1];
748
- particle.velocity[2] -= (1 + bounce) * velDotN * plane.normal[2];
749
- // Apply damping
750
- particle.velocity[0] *= 1 - dampen;
751
- particle.velocity[1] *= 1 - dampen;
752
- particle.velocity[2] *= 1 - dampen;
753
- // Move back to plane
754
- particle.position[0] += (planeDist - dot) * plane.normal[0];
755
- particle.position[1] += (planeDist - dot) * plane.normal[1];
756
- particle.position[2] += (planeDist - dot) * plane.normal[2];
757
- // Lifetime loss
758
- const lifetimeLoss = this.evaluateMinMaxCurve(collision.lifetimeLoss, particle.normalizedLifetime, particle.randomSeed);
759
- particle.remainingLifetime -= particle.startLifetime * lifetimeLoss;
760
- // Kill if too slow
761
- const speed = Math.sqrt(particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2);
762
- if (speed < collision.minKillSpeed) {
763
- particle.remainingLifetime = -1;
764
- }
765
- // Trigger sub-emitter
766
- this.triggerSubEmitters('collision', particle);
767
- }
768
- }
388
+ }
389
+ applyForceField(particle, field, dt) {
390
+ switch (field.type) {
391
+ case "gravity": {
392
+ const strength = field.strength;
393
+ particle.velocity[0] += field.direction[0] * strength * dt;
394
+ particle.velocity[1] += field.direction[1] * strength * dt;
395
+ particle.velocity[2] += field.direction[2] * strength * dt;
396
+ break;
397
+ }
398
+ case "directional": {
399
+ const dirStrength = this.evaluateMinMaxCurve(
400
+ field.strength,
401
+ particle.normalizedLifetime,
402
+ particle.randomSeed
403
+ );
404
+ particle.velocity[0] += field.direction[0] * dirStrength * dt;
405
+ particle.velocity[1] += field.direction[1] * dirStrength * dt;
406
+ particle.velocity[2] += field.direction[2] * dirStrength * dt;
407
+ break;
408
+ }
409
+ case "vortex": {
410
+ const vortexStrength = this.evaluateMinMaxCurve(
411
+ field.strength,
412
+ particle.normalizedLifetime,
413
+ particle.randomSeed
414
+ );
415
+ const toCenter = [
416
+ field.center[0] - particle.position[0],
417
+ field.center[1] - particle.position[1],
418
+ field.center[2] - particle.position[2]
419
+ ];
420
+ const dist = Math.sqrt(toCenter[0] ** 2 + toCenter[1] ** 2 + toCenter[2] ** 2);
421
+ if (dist > 1e-3 && dist < field.radius) {
422
+ const tangent = [
423
+ field.axis[1] * toCenter[2] - field.axis[2] * toCenter[1],
424
+ field.axis[2] * toCenter[0] - field.axis[0] * toCenter[2],
425
+ field.axis[0] * toCenter[1] - field.axis[1] * toCenter[0]
426
+ ];
427
+ const tangentLen = Math.sqrt(tangent[0] ** 2 + tangent[1] ** 2 + tangent[2] ** 2);
428
+ if (tangentLen > 1e-3) {
429
+ const factor = vortexStrength * (1 - dist / field.radius) / tangentLen;
430
+ particle.velocity[0] += tangent[0] * factor * dt;
431
+ particle.velocity[1] += tangent[1] * factor * dt;
432
+ particle.velocity[2] += tangent[2] * factor * dt;
433
+ }
434
+ if (field.attractionStrength > 0) {
435
+ const attractFactor = field.attractionStrength * (1 - dist / field.radius) / dist;
436
+ particle.velocity[0] += toCenter[0] * attractFactor * dt;
437
+ particle.velocity[1] += toCenter[1] * attractFactor * dt;
438
+ particle.velocity[2] += toCenter[2] * attractFactor * dt;
439
+ }
440
+ }
441
+ break;
442
+ }
443
+ case "drag": {
444
+ const dragCoeff = this.evaluateMinMaxCurve(
445
+ field.coefficient,
446
+ particle.normalizedLifetime,
447
+ particle.randomSeed
448
+ );
449
+ let drag = dragCoeff;
450
+ if (field.multiplyBySize) {
451
+ const sizeMag = Math.sqrt(
452
+ particle.size[0] ** 2 + particle.size[1] ** 2 + particle.size[2] ** 2
453
+ );
454
+ drag *= sizeMag;
455
+ }
456
+ if (field.multiplyByVelocity) {
457
+ const velMag = Math.sqrt(
458
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
459
+ );
460
+ drag *= velMag;
461
+ }
462
+ const factor = Math.max(0, 1 - drag * dt);
463
+ particle.velocity[0] *= factor;
464
+ particle.velocity[1] *= factor;
465
+ particle.velocity[2] *= factor;
466
+ break;
467
+ }
468
+ case "attractor": {
469
+ const attractorStrength = this.evaluateMinMaxCurve(
470
+ field.strength,
471
+ particle.normalizedLifetime,
472
+ particle.randomSeed
473
+ );
474
+ const toAttractor = [
475
+ field.position[0] - particle.position[0],
476
+ field.position[1] - particle.position[1],
477
+ field.position[2] - particle.position[2]
478
+ ];
479
+ const attractorDist = Math.sqrt(
480
+ toAttractor[0] ** 2 + toAttractor[1] ** 2 + toAttractor[2] ** 2
481
+ );
482
+ if (attractorDist > 1e-3 && attractorDist < field.radius) {
483
+ const falloff = 1 - attractorDist / field.radius;
484
+ const force = attractorStrength * falloff / attractorDist;
485
+ particle.velocity[0] += toAttractor[0] * force * dt;
486
+ particle.velocity[1] += toAttractor[1] * force * dt;
487
+ particle.velocity[2] += toAttractor[2] * force * dt;
488
+ if (field.killOnArrive && attractorDist < 0.1) {
489
+ particle.remainingLifetime = -1;
490
+ }
769
491
  }
492
+ break;
493
+ }
770
494
  }
771
- triggerSubEmitters(trigger, _particle) {
772
- for (const subEmitter of this.definition.subEmitters) {
773
- if (subEmitter.trigger === trigger && Math.random() < subEmitter.probability) ;
774
- }
495
+ }
496
+ applyVelocityOverLifetime(particle, dt) {
497
+ const vol = this.definition.velocityOverLifetime;
498
+ if (!vol.enabled) {
499
+ return;
775
500
  }
776
- // ─── Curve/Gradient Evaluation ─────────────────────────
777
- evaluateMinMaxCurve(curve, t, seed) {
778
- switch (curve.mode) {
779
- case 'constant':
780
- return curve.value;
781
- case 'curve':
782
- return this.evaluateAnimationCurve(curve.curve, t) * curve.multiplier;
783
- case 'randomBetweenTwoConstants': {
784
- const rand = this.seededRandom(seed);
785
- return curve.min + (curve.max - curve.min) * rand;
786
- }
787
- case 'randomBetweenTwoCurves': {
788
- const randCurve = this.seededRandom(seed);
789
- const minVal = this.evaluateAnimationCurve(curve.curveMin, t);
790
- const maxVal = this.evaluateAnimationCurve(curve.curveMax, t);
791
- return (minVal + (maxVal - minVal) * randCurve) * curve.multiplier;
792
- }
793
- }
501
+ if (vol.linear.mode !== "constant" || vol.linear.value[0] !== 0 || vol.linear.value[1] !== 0 || vol.linear.value[2] !== 0) {
502
+ const linear = this.evaluateMinMaxVector3(
503
+ vol.linear,
504
+ particle.normalizedLifetime,
505
+ particle.randomSeed
506
+ );
507
+ particle.velocity[0] += linear[0] * dt;
508
+ particle.velocity[1] += linear[1] * dt;
509
+ particle.velocity[2] += linear[2] * dt;
794
510
  }
795
- evaluateAnimationCurve(curve, t) {
796
- const keyframes = curve.keyframes;
797
- if (keyframes.length === 0) {
798
- return 0;
799
- }
800
- if (keyframes.length === 1) {
801
- return keyframes[0].value;
802
- }
803
- // Handle wrap modes
804
- if (t < 0) {
805
- t = this.handleWrapMode(t, curve.preWrapMode);
806
- }
807
- if (t > 1) {
808
- t = this.handleWrapMode(t, curve.postWrapMode);
809
- }
810
- // Find keyframes
811
- for (let i = 0; i < keyframes.length - 1; i++) {
812
- const k0 = keyframes[i];
813
- const k1 = keyframes[i + 1];
814
- if (t >= k0.time && t <= k1.time) {
815
- const localT = (t - k0.time) / (k1.time - k0.time);
816
- // Hermite interpolation
817
- const t2 = localT * localT;
818
- const t3 = t2 * localT;
819
- const h00 = 2 * t3 - 3 * t2 + 1;
820
- const h10 = t3 - 2 * t2 + localT;
821
- const h01 = -2 * t3 + 3 * t2;
822
- const h11 = t3 - t2;
823
- const dt = k1.time - k0.time;
824
- return h00 * k0.value + h10 * dt * k0.outTangent + h01 * k1.value + h11 * dt * k1.inTangent;
825
- }
826
- }
827
- return keyframes[keyframes.length - 1].value;
828
- }
829
- handleWrapMode(t, mode) {
830
- switch (mode) {
831
- case 'clamp':
832
- return Math.max(0, Math.min(1, t));
833
- case 'loop':
834
- return t - Math.floor(t);
835
- case 'pingPong': {
836
- const cycle = Math.floor(t);
837
- const frac = t - cycle;
838
- return cycle % 2 === 0 ? frac : 1 - frac;
839
- }
840
- }
511
+ const radial = this.evaluateMinMaxCurve(
512
+ vol.radial,
513
+ particle.normalizedLifetime,
514
+ particle.randomSeed
515
+ );
516
+ if (radial !== 0) {
517
+ const len = Math.sqrt(
518
+ particle.position[0] ** 2 + particle.position[1] ** 2 + particle.position[2] ** 2
519
+ );
520
+ if (len > 1e-3) {
521
+ particle.velocity[0] += particle.position[0] / len * radial * dt;
522
+ particle.velocity[1] += particle.position[1] / len * radial * dt;
523
+ particle.velocity[2] += particle.position[2] / len * radial * dt;
524
+ }
841
525
  }
842
- evaluateMinMaxGradient(gradient, t, seed) {
843
- switch (gradient.mode) {
844
- case 'color':
845
- return gradient.color;
846
- case 'gradient':
847
- return this.evaluateGradient(gradient.gradient, t);
848
- case 'randomBetweenTwoColors': {
849
- const rand = this.seededRandom(seed);
850
- return [
851
- gradient.colorMin[0] + (gradient.colorMax[0] - gradient.colorMin[0]) * rand,
852
- gradient.colorMin[1] + (gradient.colorMax[1] - gradient.colorMin[1]) * rand,
853
- gradient.colorMin[2] + (gradient.colorMax[2] - gradient.colorMin[2]) * rand,
854
- gradient.colorMin[3] + (gradient.colorMax[3] - gradient.colorMin[3]) * rand,
855
- ];
856
- }
857
- case 'randomBetweenTwoGradients': {
858
- const randG = this.seededRandom(seed);
859
- const minColor = this.evaluateGradient(gradient.gradientMin, t);
860
- const maxColor = this.evaluateGradient(gradient.gradientMax, t);
861
- return [
862
- minColor[0] + (maxColor[0] - minColor[0]) * randG,
863
- minColor[1] + (maxColor[1] - minColor[1]) * randG,
864
- minColor[2] + (maxColor[2] - minColor[2]) * randG,
865
- minColor[3] + (maxColor[3] - minColor[3]) * randG,
866
- ];
867
- }
868
- }
526
+ const speedMod = this.evaluateMinMaxCurve(
527
+ vol.speedModifier,
528
+ particle.normalizedLifetime,
529
+ particle.randomSeed
530
+ );
531
+ particle.velocity[0] *= speedMod;
532
+ particle.velocity[1] *= speedMod;
533
+ particle.velocity[2] *= speedMod;
534
+ }
535
+ applyForceOverLifetime(particle, dt) {
536
+ const fol = this.definition.forceOverLifetime;
537
+ if (!fol.enabled) {
538
+ return;
869
539
  }
870
- evaluateGradient(gradient, t) {
871
- // Evaluate color
872
- let color = [1, 1, 1];
873
- const colorKeys = gradient.colorKeys;
874
- if (colorKeys.length > 0) {
875
- if (t <= colorKeys[0].time) {
876
- color = colorKeys[0].color;
877
- }
878
- else if (t >= colorKeys[colorKeys.length - 1].time) {
879
- color = colorKeys[colorKeys.length - 1].color;
880
- }
881
- else {
882
- for (let i = 0; i < colorKeys.length - 1; i++) {
883
- if (t >= colorKeys[i].time && t <= colorKeys[i + 1].time) {
884
- const localT = (t - colorKeys[i].time) / (colorKeys[i + 1].time - colorKeys[i].time);
885
- color = [
886
- colorKeys[i].color[0] + (colorKeys[i + 1].color[0] - colorKeys[i].color[0]) * localT,
887
- colorKeys[i].color[1] + (colorKeys[i + 1].color[1] - colorKeys[i].color[1]) * localT,
888
- colorKeys[i].color[2] + (colorKeys[i + 1].color[2] - colorKeys[i].color[2]) * localT,
889
- ];
890
- break;
891
- }
892
- }
893
- }
894
- }
895
- // Evaluate alpha
896
- let alpha = 1;
897
- const alphaKeys = gradient.alphaKeys;
898
- if (alphaKeys.length > 0) {
899
- if (t <= alphaKeys[0].time) {
900
- alpha = alphaKeys[0].alpha;
901
- }
902
- else if (t >= alphaKeys[alphaKeys.length - 1].time) {
903
- alpha = alphaKeys[alphaKeys.length - 1].alpha;
904
- }
905
- else {
906
- for (let i = 0; i < alphaKeys.length - 1; i++) {
907
- if (t >= alphaKeys[i].time && t <= alphaKeys[i + 1].time) {
908
- const localT = (t - alphaKeys[i].time) / (alphaKeys[i + 1].time - alphaKeys[i].time);
909
- alpha = alphaKeys[i].alpha + (alphaKeys[i + 1].alpha - alphaKeys[i].alpha) * localT;
910
- break;
911
- }
912
- }
913
- }
914
- }
915
- return [color[0], color[1], color[2], alpha];
916
- }
917
- evaluateMinMaxVector3(vec, t, seed) {
918
- switch (vec.mode) {
919
- case 'constant':
920
- return vec.value;
921
- case 'randomBetweenTwoConstants': {
922
- const rand = this.seededRandom(seed);
923
- return [
924
- vec.min[0] + (vec.max[0] - vec.min[0]) * rand,
925
- vec.min[1] + (vec.max[1] - vec.min[1]) * rand,
926
- vec.min[2] + (vec.max[2] - vec.min[2]) * rand,
927
- ];
928
- }
540
+ const seed = fol.randomize ? Math.random() * 4294967295 | 0 : particle.randomSeed;
541
+ particle.velocity[0] += this.evaluateMinMaxCurve(fol.x, particle.normalizedLifetime, seed) * dt;
542
+ particle.velocity[1] += this.evaluateMinMaxCurve(fol.y, particle.normalizedLifetime, seed) * dt;
543
+ particle.velocity[2] += this.evaluateMinMaxCurve(fol.z, particle.normalizedLifetime, seed) * dt;
544
+ }
545
+ applyLimitVelocityOverLifetime(particle, dt) {
546
+ const lvol = this.definition.limitVelocityOverLifetime;
547
+ if (!lvol.enabled) {
548
+ return;
549
+ }
550
+ const velMag = Math.sqrt(
551
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
552
+ );
553
+ let maxSpeed;
554
+ if (lvol.separateAxes) {
555
+ maxSpeed = this.evaluateMinMaxCurve(
556
+ lvol.speed,
557
+ particle.normalizedLifetime,
558
+ particle.randomSeed
559
+ );
560
+ } else {
561
+ maxSpeed = this.evaluateMinMaxCurve(
562
+ lvol.speed,
563
+ particle.normalizedLifetime,
564
+ particle.randomSeed
565
+ );
566
+ }
567
+ if (velMag > maxSpeed) {
568
+ const dampenFactor = 1 - lvol.dampen;
569
+ const targetSpeed = maxSpeed + (velMag - maxSpeed) * dampenFactor;
570
+ const scale = targetSpeed / velMag;
571
+ particle.velocity[0] *= scale;
572
+ particle.velocity[1] *= scale;
573
+ particle.velocity[2] *= scale;
574
+ }
575
+ const drag = this.evaluateMinMaxCurve(
576
+ lvol.drag,
577
+ particle.normalizedLifetime,
578
+ particle.randomSeed
579
+ );
580
+ if (drag > 0) {
581
+ const dragFactor = Math.max(0, 1 - drag * dt);
582
+ particle.velocity[0] *= dragFactor;
583
+ particle.velocity[1] *= dragFactor;
584
+ particle.velocity[2] *= dragFactor;
585
+ }
586
+ }
587
+ applyNoise(particle, dt) {
588
+ const noise = this.definition.noise;
589
+ if (!noise.enabled) {
590
+ return;
591
+ }
592
+ const strength = this.evaluateMinMaxCurve(
593
+ noise.strength,
594
+ particle.normalizedLifetime,
595
+ particle.randomSeed
596
+ );
597
+ const freq = noise.frequency;
598
+ const scroll = noise.scrollSpeed * this.state.time;
599
+ const nx = particle.position[0] * freq + scroll;
600
+ const ny = particle.position[1] * freq + scroll;
601
+ const nz = particle.position[2] * freq + scroll;
602
+ const noiseX = Math.sin(nx * 12.9898 + ny * 78.233) * 43758.5453 % 1;
603
+ const noiseY = Math.sin(ny * 12.9898 + nz * 78.233) * 43758.5453 % 1;
604
+ const noiseZ = Math.sin(nz * 12.9898 + nx * 78.233) * 43758.5453 % 1;
605
+ const posAmount = this.evaluateMinMaxCurve(
606
+ noise.positionAmount,
607
+ particle.normalizedLifetime,
608
+ particle.randomSeed
609
+ );
610
+ particle.position[0] += (noiseX * 2 - 1) * strength * posAmount * dt;
611
+ particle.position[1] += (noiseY * 2 - 1) * strength * posAmount * dt;
612
+ particle.position[2] += (noiseZ * 2 - 1) * strength * posAmount * dt;
613
+ }
614
+ applyColorOverLifetime(particle) {
615
+ const col = this.definition.colorOverLifetime;
616
+ if (!col.enabled) {
617
+ return;
618
+ }
619
+ const color = this.evaluateMinMaxGradient(
620
+ col.color,
621
+ particle.normalizedLifetime,
622
+ particle.randomSeed
623
+ );
624
+ particle.color[0] = particle.startColor[0] * color[0];
625
+ particle.color[1] = particle.startColor[1] * color[1];
626
+ particle.color[2] = particle.startColor[2] * color[2];
627
+ particle.color[3] = particle.startColor[3] * color[3];
628
+ }
629
+ applySizeOverLifetime(particle) {
630
+ const sol = this.definition.sizeOverLifetime;
631
+ if (!sol.enabled) {
632
+ return;
633
+ }
634
+ if (sol.separateAxes) {
635
+ particle.size[0] = particle.startSize[0] * this.evaluateMinMaxCurve(sol.sizeX, particle.normalizedLifetime, particle.randomSeed);
636
+ particle.size[1] = particle.startSize[1] * this.evaluateMinMaxCurve(sol.sizeY, particle.normalizedLifetime, particle.randomSeed);
637
+ particle.size[2] = particle.startSize[2] * this.evaluateMinMaxCurve(sol.sizeZ, particle.normalizedLifetime, particle.randomSeed);
638
+ } else {
639
+ const scale = this.evaluateMinMaxCurve(
640
+ sol.size,
641
+ particle.normalizedLifetime,
642
+ particle.randomSeed
643
+ );
644
+ particle.size[0] = particle.startSize[0] * scale;
645
+ particle.size[1] = particle.startSize[1] * scale;
646
+ particle.size[2] = particle.startSize[2] * scale;
647
+ }
648
+ }
649
+ applyRotationOverLifetime(particle, dt) {
650
+ const rol = this.definition.rotationOverLifetime;
651
+ if (!rol.enabled) {
652
+ return;
653
+ }
654
+ if (rol.separateAxes) {
655
+ particle.rotation[0] += this.evaluateMinMaxCurve(
656
+ rol.angularVelocityX,
657
+ particle.normalizedLifetime,
658
+ particle.randomSeed
659
+ ) * dt;
660
+ particle.rotation[1] += this.evaluateMinMaxCurve(
661
+ rol.angularVelocityY,
662
+ particle.normalizedLifetime,
663
+ particle.randomSeed
664
+ ) * dt;
665
+ particle.rotation[2] += this.evaluateMinMaxCurve(
666
+ rol.angularVelocityZ,
667
+ particle.normalizedLifetime,
668
+ particle.randomSeed
669
+ ) * dt;
670
+ } else {
671
+ particle.rotation[2] += this.evaluateMinMaxCurve(
672
+ rol.angularVelocity,
673
+ particle.normalizedLifetime,
674
+ particle.randomSeed
675
+ ) * dt;
676
+ }
677
+ }
678
+ applyColorBySpeed(particle) {
679
+ const cbs = this.definition.colorBySpeed;
680
+ if (!cbs.enabled) {
681
+ return;
682
+ }
683
+ const speed = Math.sqrt(
684
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
685
+ );
686
+ const t = Math.min(
687
+ 1,
688
+ Math.max(0, (speed - cbs.speedRange[0]) / (cbs.speedRange[1] - cbs.speedRange[0]))
689
+ );
690
+ const color = this.evaluateMinMaxGradient(cbs.color, t, particle.randomSeed);
691
+ particle.color[0] *= color[0];
692
+ particle.color[1] *= color[1];
693
+ particle.color[2] *= color[2];
694
+ particle.color[3] *= color[3];
695
+ }
696
+ applySizeBySpeed(particle) {
697
+ const sbs = this.definition.sizeBySpeed;
698
+ if (!sbs.enabled) {
699
+ return;
700
+ }
701
+ const speed = Math.sqrt(
702
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
703
+ );
704
+ const t = Math.min(
705
+ 1,
706
+ Math.max(0, (speed - sbs.speedRange[0]) / (sbs.speedRange[1] - sbs.speedRange[0]))
707
+ );
708
+ if (sbs.separateAxes) {
709
+ particle.size[0] *= this.evaluateMinMaxCurve(sbs.sizeX, t, particle.randomSeed);
710
+ particle.size[1] *= this.evaluateMinMaxCurve(sbs.sizeY, t, particle.randomSeed);
711
+ particle.size[2] *= this.evaluateMinMaxCurve(sbs.sizeZ, t, particle.randomSeed);
712
+ } else {
713
+ const scale = this.evaluateMinMaxCurve(sbs.size, t, particle.randomSeed);
714
+ particle.size[0] *= scale;
715
+ particle.size[1] *= scale;
716
+ particle.size[2] *= scale;
717
+ }
718
+ }
719
+ applyRotationBySpeed(particle, dt) {
720
+ const rbs = this.definition.rotationBySpeed;
721
+ if (!rbs.enabled) {
722
+ return;
723
+ }
724
+ const speed = Math.sqrt(
725
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
726
+ );
727
+ const t = Math.min(
728
+ 1,
729
+ Math.max(0, (speed - rbs.speedRange[0]) / (rbs.speedRange[1] - rbs.speedRange[0]))
730
+ );
731
+ if (rbs.separateAxes) {
732
+ particle.rotation[0] += this.evaluateMinMaxCurve(rbs.angularVelocityX, t, particle.randomSeed) * dt;
733
+ particle.rotation[1] += this.evaluateMinMaxCurve(rbs.angularVelocityY, t, particle.randomSeed) * dt;
734
+ particle.rotation[2] += this.evaluateMinMaxCurve(rbs.angularVelocityZ, t, particle.randomSeed) * dt;
735
+ } else {
736
+ particle.rotation[2] += this.evaluateMinMaxCurve(rbs.angularVelocity, t, particle.randomSeed) * dt;
737
+ }
738
+ }
739
+ handleCollision(particle) {
740
+ const collision = this.definition.collision;
741
+ if (collision.type === "planes") {
742
+ for (const plane of collision.planes) {
743
+ const dot = particle.position[0] * plane.normal[0] + particle.position[1] * plane.normal[1] + particle.position[2] * plane.normal[2];
744
+ const planeDist = plane.position[0] * plane.normal[0] + plane.position[1] * plane.normal[1] + plane.position[2] * plane.normal[2];
745
+ if (dot < planeDist) {
746
+ const velDotN = particle.velocity[0] * plane.normal[0] + particle.velocity[1] * plane.normal[1] + particle.velocity[2] * plane.normal[2];
747
+ const bounce = this.evaluateMinMaxCurve(
748
+ collision.bounce,
749
+ particle.normalizedLifetime,
750
+ particle.randomSeed
751
+ );
752
+ const dampen = this.evaluateMinMaxCurve(
753
+ collision.dampen,
754
+ particle.normalizedLifetime,
755
+ particle.randomSeed
756
+ );
757
+ particle.velocity[0] -= (1 + bounce) * velDotN * plane.normal[0];
758
+ particle.velocity[1] -= (1 + bounce) * velDotN * plane.normal[1];
759
+ particle.velocity[2] -= (1 + bounce) * velDotN * plane.normal[2];
760
+ particle.velocity[0] *= 1 - dampen;
761
+ particle.velocity[1] *= 1 - dampen;
762
+ particle.velocity[2] *= 1 - dampen;
763
+ particle.position[0] += (planeDist - dot) * plane.normal[0];
764
+ particle.position[1] += (planeDist - dot) * plane.normal[1];
765
+ particle.position[2] += (planeDist - dot) * plane.normal[2];
766
+ const lifetimeLoss = this.evaluateMinMaxCurve(
767
+ collision.lifetimeLoss,
768
+ particle.normalizedLifetime,
769
+ particle.randomSeed
770
+ );
771
+ particle.remainingLifetime -= particle.startLifetime * lifetimeLoss;
772
+ const speed = Math.sqrt(
773
+ particle.velocity[0] ** 2 + particle.velocity[1] ** 2 + particle.velocity[2] ** 2
774
+ );
775
+ if (speed < collision.minKillSpeed) {
776
+ particle.remainingLifetime = -1;
777
+ }
778
+ this.triggerSubEmitters("collision", particle);
929
779
  }
780
+ }
930
781
  }
931
- seededRandom(seed) {
932
- const x = Math.sin(seed * 12.9898 + 78.233) * 43758.5453;
933
- return x - Math.floor(x);
782
+ }
783
+ triggerSubEmitters(trigger, _particle) {
784
+ for (const subEmitter of this.definition.subEmitters) {
785
+ if (subEmitter.trigger === trigger && Math.random() < subEmitter.probability) ;
934
786
  }
935
- normalizedTime() {
936
- return this.state.time / this.definition.main.duration;
787
+ }
788
+ // ─── Curve/Gradient Evaluation ─────────────────────────
789
+ evaluateMinMaxCurve(curve, t, seed) {
790
+ switch (curve.mode) {
791
+ case "constant":
792
+ return curve.value;
793
+ case "curve":
794
+ return this.evaluateAnimationCurve(curve.curve, t) * curve.multiplier;
795
+ case "randomBetweenTwoConstants": {
796
+ const rand = this.seededRandom(seed);
797
+ return curve.min + (curve.max - curve.min) * rand;
798
+ }
799
+ case "randomBetweenTwoCurves": {
800
+ const randCurve = this.seededRandom(seed);
801
+ const minVal = this.evaluateAnimationCurve(curve.curveMin, t);
802
+ const maxVal = this.evaluateAnimationCurve(curve.curveMax, t);
803
+ return (minVal + (maxVal - minVal) * randCurve) * curve.multiplier;
804
+ }
937
805
  }
938
- // ─── Public API ────────────────────────────────────────
939
- get particleCount() {
940
- return this.state.activeCount;
806
+ }
807
+ evaluateAnimationCurve(curve, t) {
808
+ const keyframes = curve.keyframes;
809
+ if (keyframes.length === 0) {
810
+ return 0;
941
811
  }
942
- get isPlaying() {
943
- return this.state.isPlaying;
812
+ if (keyframes.length === 1) {
813
+ return keyframes[0].value;
944
814
  }
945
- get isPaused() {
946
- return this.state.isPaused;
815
+ if (t < 0) {
816
+ t = this.handleWrapMode(t, curve.preWrapMode);
947
817
  }
948
- get isStopped() {
949
- return this.state.isStopped;
818
+ if (t > 1) {
819
+ t = this.handleWrapMode(t, curve.postWrapMode);
950
820
  }
951
- get time() {
952
- return this.state.time;
821
+ for (let i = 0; i < keyframes.length - 1; i++) {
822
+ const k0 = keyframes[i];
823
+ const k1 = keyframes[i + 1];
824
+ if (t >= k0.time && t <= k1.time) {
825
+ const localT = (t - k0.time) / (k1.time - k0.time);
826
+ const t2 = localT * localT;
827
+ const t3 = t2 * localT;
828
+ const h00 = 2 * t3 - 3 * t2 + 1;
829
+ const h10 = t3 - 2 * t2 + localT;
830
+ const h01 = -2 * t3 + 3 * t2;
831
+ const h11 = t3 - t2;
832
+ const dt = k1.time - k0.time;
833
+ return h00 * k0.value + h10 * dt * k0.outTangent + h01 * k1.value + h11 * dt * k1.inTangent;
834
+ }
953
835
  }
954
- get duration() {
955
- return this.definition.main.duration;
836
+ return keyframes[keyframes.length - 1].value;
837
+ }
838
+ handleWrapMode(t, mode) {
839
+ switch (mode) {
840
+ case "clamp":
841
+ return Math.max(0, Math.min(1, t));
842
+ case "loop":
843
+ return t - Math.floor(t);
844
+ case "pingPong": {
845
+ const cycle = Math.floor(t);
846
+ const frac = t - cycle;
847
+ return cycle % 2 === 0 ? frac : 1 - frac;
848
+ }
849
+ }
850
+ }
851
+ evaluateMinMaxGradient(gradient, t, seed) {
852
+ switch (gradient.mode) {
853
+ case "color":
854
+ return gradient.color;
855
+ case "gradient":
856
+ return this.evaluateGradient(gradient.gradient, t);
857
+ case "randomBetweenTwoColors": {
858
+ const rand = this.seededRandom(seed);
859
+ return [
860
+ gradient.colorMin[0] + (gradient.colorMax[0] - gradient.colorMin[0]) * rand,
861
+ gradient.colorMin[1] + (gradient.colorMax[1] - gradient.colorMin[1]) * rand,
862
+ gradient.colorMin[2] + (gradient.colorMax[2] - gradient.colorMin[2]) * rand,
863
+ gradient.colorMin[3] + (gradient.colorMax[3] - gradient.colorMin[3]) * rand
864
+ ];
865
+ }
866
+ case "randomBetweenTwoGradients": {
867
+ const randG = this.seededRandom(seed);
868
+ const minColor = this.evaluateGradient(gradient.gradientMin, t);
869
+ const maxColor = this.evaluateGradient(gradient.gradientMax, t);
870
+ return [
871
+ minColor[0] + (maxColor[0] - minColor[0]) * randG,
872
+ minColor[1] + (maxColor[1] - minColor[1]) * randG,
873
+ minColor[2] + (maxColor[2] - minColor[2]) * randG,
874
+ minColor[3] + (maxColor[3] - minColor[3]) * randG
875
+ ];
876
+ }
956
877
  }
957
- getParticles() {
958
- return this.state.particles.filter((p) => p.remainingLifetime >= 0);
878
+ }
879
+ evaluateGradient(gradient, t) {
880
+ let color = [1, 1, 1];
881
+ const colorKeys = gradient.colorKeys;
882
+ if (colorKeys.length > 0) {
883
+ if (t <= colorKeys[0].time) {
884
+ color = colorKeys[0].color;
885
+ } else if (t >= colorKeys[colorKeys.length - 1].time) {
886
+ color = colorKeys[colorKeys.length - 1].color;
887
+ } else {
888
+ for (let i = 0; i < colorKeys.length - 1; i++) {
889
+ if (t >= colorKeys[i].time && t <= colorKeys[i + 1].time) {
890
+ const localT = (t - colorKeys[i].time) / (colorKeys[i + 1].time - colorKeys[i].time);
891
+ color = [
892
+ colorKeys[i].color[0] + (colorKeys[i + 1].color[0] - colorKeys[i].color[0]) * localT,
893
+ colorKeys[i].color[1] + (colorKeys[i + 1].color[1] - colorKeys[i].color[1]) * localT,
894
+ colorKeys[i].color[2] + (colorKeys[i + 1].color[2] - colorKeys[i].color[2]) * localT
895
+ ];
896
+ break;
897
+ }
898
+ }
899
+ }
959
900
  }
960
- getDefinition() {
961
- return this.definition;
901
+ let alpha = 1;
902
+ const alphaKeys = gradient.alphaKeys;
903
+ if (alphaKeys.length > 0) {
904
+ if (t <= alphaKeys[0].time) {
905
+ alpha = alphaKeys[0].alpha;
906
+ } else if (t >= alphaKeys[alphaKeys.length - 1].time) {
907
+ alpha = alphaKeys[alphaKeys.length - 1].alpha;
908
+ } else {
909
+ for (let i = 0; i < alphaKeys.length - 1; i++) {
910
+ if (t >= alphaKeys[i].time && t <= alphaKeys[i + 1].time) {
911
+ const localT = (t - alphaKeys[i].time) / (alphaKeys[i + 1].time - alphaKeys[i].time);
912
+ alpha = alphaKeys[i].alpha + (alphaKeys[i + 1].alpha - alphaKeys[i].alpha) * localT;
913
+ break;
914
+ }
915
+ }
916
+ }
962
917
  }
963
- setDefinition(def) {
964
- this.definition = def;
965
- this.state = this.createInitialState();
918
+ return [color[0], color[1], color[2], alpha];
919
+ }
920
+ evaluateMinMaxVector3(vec, t, seed) {
921
+ switch (vec.mode) {
922
+ case "constant":
923
+ return vec.value;
924
+ case "randomBetweenTwoConstants": {
925
+ const rand = this.seededRandom(seed);
926
+ return [
927
+ vec.min[0] + (vec.max[0] - vec.min[0]) * rand,
928
+ vec.min[1] + (vec.max[1] - vec.min[1]) * rand,
929
+ vec.min[2] + (vec.max[2] - vec.min[2]) * rand
930
+ ];
931
+ }
966
932
  }
933
+ }
934
+ seededRandom(seed) {
935
+ const x = Math.sin(seed * 12.9898 + 78.233) * 43758.5453;
936
+ return x - Math.floor(x);
937
+ }
938
+ normalizedTime() {
939
+ return this.state.time / this.definition.main.duration;
940
+ }
941
+ // ─── Public API ────────────────────────────────────────
942
+ get particleCount() {
943
+ return this.state.activeCount;
944
+ }
945
+ get isPlaying() {
946
+ return this.state.isPlaying;
947
+ }
948
+ get isPaused() {
949
+ return this.state.isPaused;
950
+ }
951
+ get isStopped() {
952
+ return this.state.isStopped;
953
+ }
954
+ get time() {
955
+ return this.state.time;
956
+ }
957
+ get duration() {
958
+ return this.definition.main.duration;
959
+ }
960
+ getParticles() {
961
+ return this.state.particles.filter((p) => p.remainingLifetime >= 0);
962
+ }
963
+ getDefinition() {
964
+ return this.definition;
965
+ }
966
+ setDefinition(def) {
967
+ this.definition = def;
968
+ this.state = this.createInitialState();
969
+ }
967
970
  }
968
- // ============================================================
969
- // Factory Functions
970
- // ============================================================
971
971
  function createDefaultParticleSystem(name) {
972
- return {
973
- id: `ps_${Date.now()}`,
974
- name,
975
- main: {
976
- duration: 5,
977
- looping: true,
978
- prewarm: false,
979
- startDelay: { mode: 'constant', value: 0 },
980
- startLifetime: { mode: 'constant', value: 5 },
981
- startSpeed: { mode: 'constant', value: 5 },
982
- startSize3D: false,
983
- startSize: { mode: 'constant', value: 1 },
984
- startRotation3D: false,
985
- startRotation: { mode: 'constant', value: 0 },
986
- flipRotation: 0,
987
- startColor: { mode: 'color', color: [1, 1, 1, 1] },
988
- gravityModifier: { mode: 'constant', value: 0 },
989
- simulationSpace: 'local',
990
- simulationSpeed: 1,
991
- deltaTime: 'scaled',
992
- scalingMode: 'local',
993
- playOnAwake: true,
994
- emitterVelocityMode: 'transform',
995
- maxParticles: 1000,
996
- stopAction: 'none',
997
- cullingMode: 'automatic',
998
- ringBufferMode: 'disabled',
999
- ringBufferLoopRange: [0, 1],
1000
- },
1001
- emission: {
1002
- enabled: true,
1003
- rateOverTime: { mode: 'constant', value: 10 },
1004
- rateOverDistance: { mode: 'constant', value: 0 },
1005
- bursts: [],
1006
- },
1007
- shape: {
1008
- type: 'cone',
1009
- angle: 25,
1010
- radius: 1,
1011
- radiusThickness: 1,
1012
- arc: 360,
1013
- arcMode: 'random',
1014
- arcSpeed: 1,
1015
- length: 5,
1016
- emitFrom: 'base',
1017
- position: [0, 0, 0],
1018
- rotation: [0, 0, 0],
1019
- scale: [1, 1, 1],
1020
- },
1021
- velocityOverLifetime: {
1022
- enabled: false,
1023
- space: 'local',
1024
- linear: { mode: 'constant', value: [0, 0, 0] },
1025
- orbital: { mode: 'constant', value: [0, 0, 0] },
1026
- offset: { mode: 'constant', value: [0, 0, 0] },
1027
- radial: { mode: 'constant', value: 0 },
1028
- speedModifier: { mode: 'constant', value: 1 },
1029
- },
1030
- limitVelocityOverLifetime: {
1031
- enabled: false,
1032
- separateAxes: false,
1033
- speed: { mode: 'constant', value: 1 },
1034
- space: 'local',
1035
- dampen: 0.1,
1036
- drag: { mode: 'constant', value: 0 },
1037
- multiplyDragBySize: false,
1038
- multiplyDragByVelocity: false,
1039
- },
1040
- inheritVelocity: {
1041
- enabled: false,
1042
- mode: 'initial',
1043
- curve: { mode: 'constant', value: 0 },
1044
- },
1045
- lifetimeByEmitterSpeed: {
1046
- enabled: false,
1047
- curve: { mode: 'constant', value: 1 },
1048
- speedRange: [0, 1],
1049
- },
1050
- forceOverLifetime: {
1051
- enabled: false,
1052
- space: 'local',
1053
- x: { mode: 'constant', value: 0 },
1054
- y: { mode: 'constant', value: 0 },
1055
- z: { mode: 'constant', value: 0 },
1056
- randomize: false,
1057
- },
1058
- colorOverLifetime: {
1059
- enabled: false,
1060
- color: { mode: 'color', color: [1, 1, 1, 1] },
1061
- },
1062
- colorBySpeed: {
1063
- enabled: false,
1064
- color: { mode: 'color', color: [1, 1, 1, 1] },
1065
- speedRange: [0, 1],
1066
- },
1067
- sizeOverLifetime: {
1068
- enabled: false,
1069
- separateAxes: false,
1070
- size: { mode: 'constant', value: 1 },
1071
- },
1072
- sizeBySpeed: {
1073
- enabled: false,
1074
- separateAxes: false,
1075
- size: { mode: 'constant', value: 1 },
1076
- speedRange: [0, 1],
1077
- },
1078
- rotationOverLifetime: {
1079
- enabled: false,
1080
- separateAxes: false,
1081
- angularVelocity: { mode: 'constant', value: 0 },
1082
- },
1083
- rotationBySpeed: {
1084
- enabled: false,
1085
- separateAxes: false,
1086
- angularVelocity: { mode: 'constant', value: 0 },
1087
- speedRange: [0, 1],
1088
- },
1089
- externalForces: {
1090
- enabled: false,
1091
- multiplier: { mode: 'constant', value: 1 },
1092
- influenceFilter: 'layerMask',
1093
- influenceMask: 0xffffffff,
1094
- forceFields: [],
1095
- },
1096
- noise: {
1097
- enabled: false,
1098
- separateAxes: false,
1099
- strength: { mode: 'constant', value: 1 },
1100
- frequency: 0.5,
1101
- scrollSpeed: 0,
1102
- damping: true,
1103
- octaves: 1,
1104
- octaveMultiplier: 0.5,
1105
- octaveScale: 2,
1106
- quality: 'medium',
1107
- remapEnabled: false,
1108
- remapCurve: { keyframes: [], preWrapMode: 'clamp', postWrapMode: 'clamp' },
1109
- positionAmount: { mode: 'constant', value: 1 },
1110
- rotationAmount: { mode: 'constant', value: 0 },
1111
- sizeAmount: { mode: 'constant', value: 0 },
1112
- },
1113
- collision: {
1114
- enabled: false,
1115
- type: 'planes',
1116
- planes: [{ normal: [0, 1, 0], position: [0, 0, 0] }],
1117
- bounce: { mode: 'constant', value: 0.5 },
1118
- lifetimeLoss: { mode: 'constant', value: 0 },
1119
- minKillSpeed: 0,
1120
- radiusScale: 1,
1121
- dampen: { mode: 'constant', value: 0 },
1122
- enableDynamicColliders: false,
1123
- colliderForce: 0,
1124
- multiplyByCollisionAngle: true,
1125
- multiplyByParticleSpeed: true,
1126
- multiplyByParticleSize: false,
1127
- sendCollisionMessages: false,
1128
- },
1129
- triggers: {
1130
- enabled: false,
1131
- inside: 'ignore',
1132
- outside: 'ignore',
1133
- enter: 'ignore',
1134
- exit: 'ignore',
1135
- colliderQueryMode: 'disabled',
1136
- radiusScale: 1,
1137
- },
1138
- subEmitters: [],
1139
- textureSheetAnimation: {
1140
- enabled: false,
1141
- mode: 'grid',
1142
- tilesX: 1,
1143
- tilesY: 1,
1144
- animation: 'wholeSheet',
1145
- rowIndex: 0,
1146
- rowMode: 'custom',
1147
- timeMode: 'lifetime',
1148
- frameOverTime: { mode: 'constant', value: 0 },
1149
- startFrame: { mode: 'constant', value: 0 },
1150
- cycleCount: 1,
1151
- speedRange: [0, 1],
1152
- fps: 30,
1153
- sprites: [],
1154
- },
1155
- lights: {
1156
- enabled: false,
1157
- ratio: 1,
1158
- randomDistribution: true,
1159
- light: {
1160
- type: 'point',
1161
- color: { mode: 'color', color: [1, 1, 1, 1] },
1162
- intensity: { mode: 'constant', value: 1 },
1163
- range: { mode: 'constant', value: 1 },
1164
- },
1165
- useParticleColor: true,
1166
- sizeAffectsRange: true,
1167
- alphaAffectsIntensity: true,
1168
- maxLights: 20,
1169
- },
1170
- trails: {
1171
- enabled: false,
1172
- ratio: 1,
1173
- lifetime: { mode: 'constant', value: 1 },
1174
- minVertexDistance: 0.2,
1175
- textureMode: 'stretch',
1176
- worldSpace: false,
1177
- dieWithParticle: true,
1178
- sizeAffectsWidth: true,
1179
- sizeAffectsLifetime: false,
1180
- inheritParticleColor: true,
1181
- colorOverLifetime: { mode: 'color', color: [1, 1, 1, 1] },
1182
- widthOverTrail: { mode: 'constant', value: 1 },
1183
- colorOverTrail: { mode: 'color', color: [1, 1, 1, 1] },
1184
- generateLightingData: false,
1185
- shadowBias: 0.5,
1186
- },
1187
- customData: {
1188
- enabled: false,
1189
- stream1: { mode: 'disabled', components: 4 },
1190
- stream2: { mode: 'disabled', components: 4 },
1191
- },
1192
- renderer: {
1193
- enabled: true,
1194
- renderMode: 'billboard',
1195
- material: '',
1196
- sortMode: 'none',
1197
- sortingFudge: 0,
1198
- minParticleSize: 0,
1199
- maxParticleSize: 0.5,
1200
- cameraVelocityScale: 0,
1201
- velocityScale: 0,
1202
- lengthScale: 2,
1203
- normalDirection: 1,
1204
- shadowCastingMode: 'off',
1205
- receiveShadows: false,
1206
- motionVectors: 'cameraMotionOnly',
1207
- lightProbes: 'off',
1208
- reflectionProbes: 'off',
1209
- maskInteraction: 'none',
1210
- pivot: [0, 0, 0],
1211
- flip: [0, 0, 0],
1212
- allowRoll: true,
1213
- freeformStretching: false,
1214
- rotateWithStretchDirection: true,
1215
- enableGPUInstancing: false,
1216
- },
1217
- };
972
+ return {
973
+ id: `ps_${Date.now()}`,
974
+ name,
975
+ main: {
976
+ duration: 5,
977
+ looping: true,
978
+ prewarm: false,
979
+ startDelay: { mode: "constant", value: 0 },
980
+ startLifetime: { mode: "constant", value: 5 },
981
+ startSpeed: { mode: "constant", value: 5 },
982
+ startSize3D: false,
983
+ startSize: { mode: "constant", value: 1 },
984
+ startRotation3D: false,
985
+ startRotation: { mode: "constant", value: 0 },
986
+ flipRotation: 0,
987
+ startColor: { mode: "color", color: [1, 1, 1, 1] },
988
+ gravityModifier: { mode: "constant", value: 0 },
989
+ simulationSpace: "local",
990
+ simulationSpeed: 1,
991
+ deltaTime: "scaled",
992
+ scalingMode: "local",
993
+ playOnAwake: true,
994
+ emitterVelocityMode: "transform",
995
+ maxParticles: 1e3,
996
+ stopAction: "none",
997
+ cullingMode: "automatic",
998
+ ringBufferMode: "disabled",
999
+ ringBufferLoopRange: [0, 1]
1000
+ },
1001
+ emission: {
1002
+ enabled: true,
1003
+ rateOverTime: { mode: "constant", value: 10 },
1004
+ rateOverDistance: { mode: "constant", value: 0 },
1005
+ bursts: []
1006
+ },
1007
+ shape: {
1008
+ type: "cone",
1009
+ angle: 25,
1010
+ radius: 1,
1011
+ radiusThickness: 1,
1012
+ arc: 360,
1013
+ arcMode: "random",
1014
+ arcSpeed: 1,
1015
+ length: 5,
1016
+ emitFrom: "base",
1017
+ position: [0, 0, 0],
1018
+ rotation: [0, 0, 0],
1019
+ scale: [1, 1, 1]
1020
+ },
1021
+ velocityOverLifetime: {
1022
+ enabled: false,
1023
+ space: "local",
1024
+ linear: { mode: "constant", value: [0, 0, 0] },
1025
+ orbital: { mode: "constant", value: [0, 0, 0] },
1026
+ offset: { mode: "constant", value: [0, 0, 0] },
1027
+ radial: { mode: "constant", value: 0 },
1028
+ speedModifier: { mode: "constant", value: 1 }
1029
+ },
1030
+ limitVelocityOverLifetime: {
1031
+ enabled: false,
1032
+ separateAxes: false,
1033
+ speed: { mode: "constant", value: 1 },
1034
+ space: "local",
1035
+ dampen: 0.1,
1036
+ drag: { mode: "constant", value: 0 },
1037
+ multiplyDragBySize: false,
1038
+ multiplyDragByVelocity: false
1039
+ },
1040
+ inheritVelocity: {
1041
+ enabled: false,
1042
+ mode: "initial",
1043
+ curve: { mode: "constant", value: 0 }
1044
+ },
1045
+ lifetimeByEmitterSpeed: {
1046
+ enabled: false,
1047
+ curve: { mode: "constant", value: 1 },
1048
+ speedRange: [0, 1]
1049
+ },
1050
+ forceOverLifetime: {
1051
+ enabled: false,
1052
+ space: "local",
1053
+ x: { mode: "constant", value: 0 },
1054
+ y: { mode: "constant", value: 0 },
1055
+ z: { mode: "constant", value: 0 },
1056
+ randomize: false
1057
+ },
1058
+ colorOverLifetime: {
1059
+ enabled: false,
1060
+ color: { mode: "color", color: [1, 1, 1, 1] }
1061
+ },
1062
+ colorBySpeed: {
1063
+ enabled: false,
1064
+ color: { mode: "color", color: [1, 1, 1, 1] },
1065
+ speedRange: [0, 1]
1066
+ },
1067
+ sizeOverLifetime: {
1068
+ enabled: false,
1069
+ separateAxes: false,
1070
+ size: { mode: "constant", value: 1 }
1071
+ },
1072
+ sizeBySpeed: {
1073
+ enabled: false,
1074
+ separateAxes: false,
1075
+ size: { mode: "constant", value: 1 },
1076
+ speedRange: [0, 1]
1077
+ },
1078
+ rotationOverLifetime: {
1079
+ enabled: false,
1080
+ separateAxes: false,
1081
+ angularVelocity: { mode: "constant", value: 0 }
1082
+ },
1083
+ rotationBySpeed: {
1084
+ enabled: false,
1085
+ separateAxes: false,
1086
+ angularVelocity: { mode: "constant", value: 0 },
1087
+ speedRange: [0, 1]
1088
+ },
1089
+ externalForces: {
1090
+ enabled: false,
1091
+ multiplier: { mode: "constant", value: 1 },
1092
+ influenceFilter: "layerMask",
1093
+ influenceMask: 4294967295,
1094
+ forceFields: []
1095
+ },
1096
+ noise: {
1097
+ enabled: false,
1098
+ separateAxes: false,
1099
+ strength: { mode: "constant", value: 1 },
1100
+ frequency: 0.5,
1101
+ scrollSpeed: 0,
1102
+ damping: true,
1103
+ octaves: 1,
1104
+ octaveMultiplier: 0.5,
1105
+ octaveScale: 2,
1106
+ quality: "medium",
1107
+ remapEnabled: false,
1108
+ remapCurve: { keyframes: [], preWrapMode: "clamp", postWrapMode: "clamp" },
1109
+ positionAmount: { mode: "constant", value: 1 },
1110
+ rotationAmount: { mode: "constant", value: 0 },
1111
+ sizeAmount: { mode: "constant", value: 0 }
1112
+ },
1113
+ collision: {
1114
+ enabled: false,
1115
+ type: "planes",
1116
+ planes: [{ normal: [0, 1, 0], position: [0, 0, 0] }],
1117
+ bounce: { mode: "constant", value: 0.5 },
1118
+ lifetimeLoss: { mode: "constant", value: 0 },
1119
+ minKillSpeed: 0,
1120
+ radiusScale: 1,
1121
+ dampen: { mode: "constant", value: 0 },
1122
+ enableDynamicColliders: false,
1123
+ colliderForce: 0,
1124
+ multiplyByCollisionAngle: true,
1125
+ multiplyByParticleSpeed: true,
1126
+ multiplyByParticleSize: false,
1127
+ sendCollisionMessages: false
1128
+ },
1129
+ triggers: {
1130
+ enabled: false,
1131
+ inside: "ignore",
1132
+ outside: "ignore",
1133
+ enter: "ignore",
1134
+ exit: "ignore",
1135
+ colliderQueryMode: "disabled",
1136
+ radiusScale: 1
1137
+ },
1138
+ subEmitters: [],
1139
+ textureSheetAnimation: {
1140
+ enabled: false,
1141
+ mode: "grid",
1142
+ tilesX: 1,
1143
+ tilesY: 1,
1144
+ animation: "wholeSheet",
1145
+ rowIndex: 0,
1146
+ rowMode: "custom",
1147
+ timeMode: "lifetime",
1148
+ frameOverTime: { mode: "constant", value: 0 },
1149
+ startFrame: { mode: "constant", value: 0 },
1150
+ cycleCount: 1,
1151
+ speedRange: [0, 1],
1152
+ fps: 30,
1153
+ sprites: []
1154
+ },
1155
+ lights: {
1156
+ enabled: false,
1157
+ ratio: 1,
1158
+ randomDistribution: true,
1159
+ light: {
1160
+ type: "point",
1161
+ color: { mode: "color", color: [1, 1, 1, 1] },
1162
+ intensity: { mode: "constant", value: 1 },
1163
+ range: { mode: "constant", value: 1 }
1164
+ },
1165
+ useParticleColor: true,
1166
+ sizeAffectsRange: true,
1167
+ alphaAffectsIntensity: true,
1168
+ maxLights: 20
1169
+ },
1170
+ trails: {
1171
+ enabled: false,
1172
+ ratio: 1,
1173
+ lifetime: { mode: "constant", value: 1 },
1174
+ minVertexDistance: 0.2,
1175
+ textureMode: "stretch",
1176
+ worldSpace: false,
1177
+ dieWithParticle: true,
1178
+ sizeAffectsWidth: true,
1179
+ sizeAffectsLifetime: false,
1180
+ inheritParticleColor: true,
1181
+ colorOverLifetime: { mode: "color", color: [1, 1, 1, 1] },
1182
+ widthOverTrail: { mode: "constant", value: 1 },
1183
+ colorOverTrail: { mode: "color", color: [1, 1, 1, 1] },
1184
+ generateLightingData: false,
1185
+ shadowBias: 0.5
1186
+ },
1187
+ customData: {
1188
+ enabled: false,
1189
+ stream1: { mode: "disabled", components: 4 },
1190
+ stream2: { mode: "disabled", components: 4 }
1191
+ },
1192
+ renderer: {
1193
+ enabled: true,
1194
+ renderMode: "billboard",
1195
+ material: "",
1196
+ sortMode: "none",
1197
+ sortingFudge: 0,
1198
+ minParticleSize: 0,
1199
+ maxParticleSize: 0.5,
1200
+ cameraVelocityScale: 0,
1201
+ velocityScale: 0,
1202
+ lengthScale: 2,
1203
+ normalDirection: 1,
1204
+ shadowCastingMode: "off",
1205
+ receiveShadows: false,
1206
+ motionVectors: "cameraMotionOnly",
1207
+ lightProbes: "off",
1208
+ reflectionProbes: "off",
1209
+ maskInteraction: "none",
1210
+ pivot: [0, 0, 0],
1211
+ flip: [0, 0, 0],
1212
+ allowRoll: true,
1213
+ freeformStretching: false,
1214
+ rotateWithStretchDirection: true,
1215
+ enableGPUInstancing: false
1216
+ }
1217
+ };
1218
1218
  }
1219
1219
  function createParticleSystem(def) {
1220
- return new ParticleSystem2(def);
1220
+ return new ParticleSystem2(def);
1221
1221
  }
1222
- // ============================================================
1223
- // Preset Systems
1224
- // ============================================================
1225
1222
  function createFireParticleSystem() {
1226
- const def = createDefaultParticleSystem('Fire');
1227
- def.main.startLifetime = { mode: 'randomBetweenTwoConstants', min: 0.5, max: 1.5 };
1228
- def.main.startSize = { mode: 'randomBetweenTwoConstants', min: 0.1, max: 0.5 };
1229
- def.main.startColor = {
1230
- mode: 'randomBetweenTwoColors',
1231
- colorMin: [1, 0.5, 0, 1],
1232
- colorMax: [1, 0.2, 0, 1],
1233
- };
1234
- def.main.gravityModifier = { mode: 'constant', value: -0.5 };
1235
- def.emission.rateOverTime = { mode: 'constant', value: 50 };
1236
- def.shape = {
1237
- type: 'cone',
1238
- angle: 15,
1239
- radius: 0.2,
1240
- radiusThickness: 1,
1241
- arc: 360,
1242
- arcMode: 'random',
1243
- arcSpeed: 1,
1244
- length: 1,
1245
- emitFrom: 'base',
1246
- position: [0, 0, 0],
1247
- rotation: [0, 0, 0],
1248
- scale: [1, 1, 1],
1249
- };
1250
- def.colorOverLifetime = {
1251
- enabled: true,
1252
- color: {
1253
- mode: 'gradient',
1254
- gradient: {
1255
- colorKeys: [
1256
- { time: 0, color: [1, 0.8, 0] },
1257
- { time: 0.3, color: [1, 0.3, 0] },
1258
- { time: 1, color: [0.2, 0, 0] },
1259
- ],
1260
- alphaKeys: [
1261
- { time: 0, alpha: 1 },
1262
- { time: 0.7, alpha: 0.8 },
1263
- { time: 1, alpha: 0 },
1264
- ],
1265
- mode: 'blend',
1266
- },
1267
- },
1268
- };
1269
- def.sizeOverLifetime = {
1270
- enabled: true,
1271
- separateAxes: false,
1272
- size: {
1273
- mode: 'curve',
1274
- curve: {
1275
- keyframes: [
1276
- { time: 0, value: 0.5, inTangent: 0, outTangent: 1, mode: 'auto' },
1277
- { time: 0.3, value: 1, inTangent: 0, outTangent: 0, mode: 'auto' },
1278
- { time: 1, value: 0, inTangent: -1, outTangent: 0, mode: 'auto' },
1279
- ],
1280
- preWrapMode: 'clamp',
1281
- postWrapMode: 'clamp',
1282
- },
1283
- multiplier: 1,
1284
- },
1285
- };
1286
- def.noise = {
1287
- enabled: true,
1288
- separateAxes: false,
1289
- strength: { mode: 'constant', value: 0.3 },
1290
- frequency: 1,
1291
- scrollSpeed: 1,
1292
- damping: true,
1293
- octaves: 2,
1294
- octaveMultiplier: 0.5,
1295
- octaveScale: 2,
1296
- quality: 'medium',
1297
- remapEnabled: false,
1298
- remapCurve: { keyframes: [], preWrapMode: 'clamp', postWrapMode: 'clamp' },
1299
- positionAmount: { mode: 'constant', value: 1 },
1300
- rotationAmount: { mode: 'constant', value: 0 },
1301
- sizeAmount: { mode: 'constant', value: 0 },
1302
- };
1303
- def.renderer.renderMode = 'billboard';
1304
- return def;
1223
+ const def = createDefaultParticleSystem("Fire");
1224
+ def.main.startLifetime = { mode: "randomBetweenTwoConstants", min: 0.5, max: 1.5 };
1225
+ def.main.startSize = { mode: "randomBetweenTwoConstants", min: 0.1, max: 0.5 };
1226
+ def.main.startColor = {
1227
+ mode: "randomBetweenTwoColors",
1228
+ colorMin: [1, 0.5, 0, 1],
1229
+ colorMax: [1, 0.2, 0, 1]
1230
+ };
1231
+ def.main.gravityModifier = { mode: "constant", value: -0.5 };
1232
+ def.emission.rateOverTime = { mode: "constant", value: 50 };
1233
+ def.shape = {
1234
+ type: "cone",
1235
+ angle: 15,
1236
+ radius: 0.2,
1237
+ radiusThickness: 1,
1238
+ arc: 360,
1239
+ arcMode: "random",
1240
+ arcSpeed: 1,
1241
+ length: 1,
1242
+ emitFrom: "base",
1243
+ position: [0, 0, 0],
1244
+ rotation: [0, 0, 0],
1245
+ scale: [1, 1, 1]
1246
+ };
1247
+ def.colorOverLifetime = {
1248
+ enabled: true,
1249
+ color: {
1250
+ mode: "gradient",
1251
+ gradient: {
1252
+ colorKeys: [
1253
+ { time: 0, color: [1, 0.8, 0] },
1254
+ { time: 0.3, color: [1, 0.3, 0] },
1255
+ { time: 1, color: [0.2, 0, 0] }
1256
+ ],
1257
+ alphaKeys: [
1258
+ { time: 0, alpha: 1 },
1259
+ { time: 0.7, alpha: 0.8 },
1260
+ { time: 1, alpha: 0 }
1261
+ ],
1262
+ mode: "blend"
1263
+ }
1264
+ }
1265
+ };
1266
+ def.sizeOverLifetime = {
1267
+ enabled: true,
1268
+ separateAxes: false,
1269
+ size: {
1270
+ mode: "curve",
1271
+ curve: {
1272
+ keyframes: [
1273
+ { time: 0, value: 0.5, inTangent: 0, outTangent: 1, mode: "auto" },
1274
+ { time: 0.3, value: 1, inTangent: 0, outTangent: 0, mode: "auto" },
1275
+ { time: 1, value: 0, inTangent: -1, outTangent: 0, mode: "auto" }
1276
+ ],
1277
+ preWrapMode: "clamp",
1278
+ postWrapMode: "clamp"
1279
+ },
1280
+ multiplier: 1
1281
+ }
1282
+ };
1283
+ def.noise = {
1284
+ enabled: true,
1285
+ separateAxes: false,
1286
+ strength: { mode: "constant", value: 0.3 },
1287
+ frequency: 1,
1288
+ scrollSpeed: 1,
1289
+ damping: true,
1290
+ octaves: 2,
1291
+ octaveMultiplier: 0.5,
1292
+ octaveScale: 2,
1293
+ quality: "medium",
1294
+ remapEnabled: false,
1295
+ remapCurve: { keyframes: [], preWrapMode: "clamp", postWrapMode: "clamp" },
1296
+ positionAmount: { mode: "constant", value: 1 },
1297
+ rotationAmount: { mode: "constant", value: 0 },
1298
+ sizeAmount: { mode: "constant", value: 0 }
1299
+ };
1300
+ def.renderer.renderMode = "billboard";
1301
+ return def;
1305
1302
  }
1306
1303
  function createSmokeParticleSystem() {
1307
- const def = createDefaultParticleSystem('Smoke');
1308
- def.main.startLifetime = { mode: 'randomBetweenTwoConstants', min: 3, max: 5 };
1309
- def.main.startSize = { mode: 'randomBetweenTwoConstants', min: 0.5, max: 1 };
1310
- def.main.startColor = {
1311
- mode: 'randomBetweenTwoColors',
1312
- colorMin: [0.5, 0.5, 0.5, 0.5],
1313
- colorMax: [0.3, 0.3, 0.3, 0.3],
1314
- };
1315
- def.main.gravityModifier = { mode: 'constant', value: -0.1 };
1316
- def.emission.rateOverTime = { mode: 'constant', value: 20 };
1317
- def.shape = {
1318
- type: 'circle',
1319
- radius: 0.3,
1320
- radiusThickness: 1,
1321
- arc: 360,
1322
- arcMode: 'random',
1323
- arcSpeed: 1,
1324
- position: [0, 0, 0],
1325
- rotation: [0, 0, 0],
1326
- scale: [1, 1, 1],
1327
- };
1328
- def.sizeOverLifetime = {
1329
- enabled: true,
1330
- separateAxes: false,
1331
- size: {
1332
- mode: 'curve',
1333
- curve: {
1334
- keyframes: [
1335
- { time: 0, value: 0.3, inTangent: 0, outTangent: 2, mode: 'auto' },
1336
- { time: 1, value: 3, inTangent: 0.5, outTangent: 0, mode: 'auto' },
1337
- ],
1338
- preWrapMode: 'clamp',
1339
- postWrapMode: 'clamp',
1340
- },
1341
- multiplier: 1,
1342
- },
1343
- };
1344
- def.colorOverLifetime = {
1345
- enabled: true,
1346
- color: {
1347
- mode: 'gradient',
1348
- gradient: {
1349
- colorKeys: [
1350
- { time: 0, color: [0.4, 0.4, 0.4] },
1351
- { time: 1, color: [0.6, 0.6, 0.6] },
1352
- ],
1353
- alphaKeys: [
1354
- { time: 0, alpha: 0 },
1355
- { time: 0.1, alpha: 0.5 },
1356
- { time: 0.8, alpha: 0.3 },
1357
- { time: 1, alpha: 0 },
1358
- ],
1359
- mode: 'blend',
1360
- },
1361
- },
1362
- };
1363
- def.rotationOverLifetime = {
1364
- enabled: true,
1365
- separateAxes: false,
1366
- angularVelocity: { mode: 'randomBetweenTwoConstants', min: -0.5, max: 0.5 },
1367
- };
1368
- def.noise = {
1369
- enabled: true,
1370
- separateAxes: false,
1371
- strength: { mode: 'constant', value: 0.5 },
1372
- frequency: 0.3,
1373
- scrollSpeed: 0.2,
1374
- damping: true,
1375
- octaves: 2,
1376
- octaveMultiplier: 0.5,
1377
- octaveScale: 2,
1378
- quality: 'medium',
1379
- remapEnabled: false,
1380
- remapCurve: { keyframes: [], preWrapMode: 'clamp', postWrapMode: 'clamp' },
1381
- positionAmount: { mode: 'constant', value: 1 },
1382
- rotationAmount: { mode: 'constant', value: 0 },
1383
- sizeAmount: { mode: 'constant', value: 0 },
1384
- };
1385
- return def;
1304
+ const def = createDefaultParticleSystem("Smoke");
1305
+ def.main.startLifetime = { mode: "randomBetweenTwoConstants", min: 3, max: 5 };
1306
+ def.main.startSize = { mode: "randomBetweenTwoConstants", min: 0.5, max: 1 };
1307
+ def.main.startColor = {
1308
+ mode: "randomBetweenTwoColors",
1309
+ colorMin: [0.5, 0.5, 0.5, 0.5],
1310
+ colorMax: [0.3, 0.3, 0.3, 0.3]
1311
+ };
1312
+ def.main.gravityModifier = { mode: "constant", value: -0.1 };
1313
+ def.emission.rateOverTime = { mode: "constant", value: 20 };
1314
+ def.shape = {
1315
+ type: "circle",
1316
+ radius: 0.3,
1317
+ radiusThickness: 1,
1318
+ arc: 360,
1319
+ arcMode: "random",
1320
+ arcSpeed: 1,
1321
+ position: [0, 0, 0],
1322
+ rotation: [0, 0, 0],
1323
+ scale: [1, 1, 1]
1324
+ };
1325
+ def.sizeOverLifetime = {
1326
+ enabled: true,
1327
+ separateAxes: false,
1328
+ size: {
1329
+ mode: "curve",
1330
+ curve: {
1331
+ keyframes: [
1332
+ { time: 0, value: 0.3, inTangent: 0, outTangent: 2, mode: "auto" },
1333
+ { time: 1, value: 3, inTangent: 0.5, outTangent: 0, mode: "auto" }
1334
+ ],
1335
+ preWrapMode: "clamp",
1336
+ postWrapMode: "clamp"
1337
+ },
1338
+ multiplier: 1
1339
+ }
1340
+ };
1341
+ def.colorOverLifetime = {
1342
+ enabled: true,
1343
+ color: {
1344
+ mode: "gradient",
1345
+ gradient: {
1346
+ colorKeys: [
1347
+ { time: 0, color: [0.4, 0.4, 0.4] },
1348
+ { time: 1, color: [0.6, 0.6, 0.6] }
1349
+ ],
1350
+ alphaKeys: [
1351
+ { time: 0, alpha: 0 },
1352
+ { time: 0.1, alpha: 0.5 },
1353
+ { time: 0.8, alpha: 0.3 },
1354
+ { time: 1, alpha: 0 }
1355
+ ],
1356
+ mode: "blend"
1357
+ }
1358
+ }
1359
+ };
1360
+ def.rotationOverLifetime = {
1361
+ enabled: true,
1362
+ separateAxes: false,
1363
+ angularVelocity: { mode: "randomBetweenTwoConstants", min: -0.5, max: 0.5 }
1364
+ };
1365
+ def.noise = {
1366
+ enabled: true,
1367
+ separateAxes: false,
1368
+ strength: { mode: "constant", value: 0.5 },
1369
+ frequency: 0.3,
1370
+ scrollSpeed: 0.2,
1371
+ damping: true,
1372
+ octaves: 2,
1373
+ octaveMultiplier: 0.5,
1374
+ octaveScale: 2,
1375
+ quality: "medium",
1376
+ remapEnabled: false,
1377
+ remapCurve: { keyframes: [], preWrapMode: "clamp", postWrapMode: "clamp" },
1378
+ positionAmount: { mode: "constant", value: 1 },
1379
+ rotationAmount: { mode: "constant", value: 0 },
1380
+ sizeAmount: { mode: "constant", value: 0 }
1381
+ };
1382
+ return def;
1386
1383
  }
1387
1384
  function createSparkParticleSystem() {
1388
- const def = createDefaultParticleSystem('Sparks');
1389
- def.main.startLifetime = { mode: 'randomBetweenTwoConstants', min: 0.2, max: 0.5 };
1390
- def.main.startSize = { mode: 'randomBetweenTwoConstants', min: 0.02, max: 0.05 };
1391
- def.main.startSpeed = { mode: 'randomBetweenTwoConstants', min: 3, max: 8 };
1392
- def.main.startColor = {
1393
- mode: 'randomBetweenTwoColors',
1394
- colorMin: [1, 0.8, 0, 1],
1395
- colorMax: [1, 0.5, 0, 1],
1396
- };
1397
- def.main.gravityModifier = { mode: 'constant', value: 1 };
1398
- def.emission.rateOverTime = { mode: 'constant', value: 0 };
1399
- def.emission.bursts = [
1400
- {
1401
- time: 0,
1402
- count: { mode: 'randomBetweenTwoConstants', min: 10, max: 30 },
1403
- cycles: 1,
1404
- interval: 0.01,
1405
- probability: 1,
1406
- },
1407
- ];
1408
- def.shape = {
1409
- type: 'sphere',
1410
- radius: 0.1,
1411
- radiusThickness: 0,
1412
- arc: 360,
1413
- arcMode: 'random',
1414
- arcSpeed: 1,
1415
- position: [0, 0, 0],
1416
- rotation: [0, 0, 0],
1417
- scale: [1, 1, 1],
1418
- };
1419
- def.colorOverLifetime = {
1420
- enabled: true,
1421
- color: {
1422
- mode: 'gradient',
1423
- gradient: {
1424
- colorKeys: [
1425
- { time: 0, color: [1, 0.9, 0.5] },
1426
- { time: 0.5, color: [1, 0.5, 0] },
1427
- { time: 1, color: [0.5, 0, 0] },
1428
- ],
1429
- alphaKeys: [
1430
- { time: 0, alpha: 1 },
1431
- { time: 0.8, alpha: 1 },
1432
- { time: 1, alpha: 0 },
1433
- ],
1434
- mode: 'blend',
1435
- },
1436
- },
1437
- };
1438
- def.trails = {
1439
- enabled: true,
1440
- ratio: 1,
1441
- lifetime: { mode: 'constant', value: 0.1 },
1442
- minVertexDistance: 0.01,
1443
- textureMode: 'stretch',
1444
- worldSpace: true,
1445
- dieWithParticle: true,
1446
- sizeAffectsWidth: true,
1447
- sizeAffectsLifetime: false,
1448
- inheritParticleColor: true,
1449
- colorOverLifetime: { mode: 'color', color: [1, 1, 1, 1] },
1450
- widthOverTrail: {
1451
- mode: 'curve',
1452
- curve: {
1453
- keyframes: [
1454
- { time: 0, value: 1, inTangent: 0, outTangent: 0, mode: 'auto' },
1455
- { time: 1, value: 0, inTangent: -1, outTangent: 0, mode: 'auto' },
1456
- ],
1457
- preWrapMode: 'clamp',
1458
- postWrapMode: 'clamp',
1459
- },
1460
- multiplier: 1,
1461
- },
1462
- colorOverTrail: {
1463
- mode: 'gradient',
1464
- gradient: {
1465
- colorKeys: [
1466
- { time: 0, color: [1, 0.8, 0] },
1467
- { time: 1, color: [1, 0.2, 0] },
1468
- ],
1469
- alphaKeys: [
1470
- { time: 0, alpha: 1 },
1471
- { time: 1, alpha: 0 },
1472
- ],
1473
- mode: 'blend',
1474
- },
1475
- },
1476
- generateLightingData: false,
1477
- shadowBias: 0.5,
1478
- };
1479
- def.collision = {
1480
- enabled: true,
1481
- type: 'planes',
1482
- planes: [{ normal: [0, 1, 0], position: [0, 0, 0] }],
1483
- bounce: { mode: 'randomBetweenTwoConstants', min: 0.2, max: 0.6 },
1484
- lifetimeLoss: { mode: 'constant', value: 0.2 },
1485
- minKillSpeed: 0.1,
1486
- radiusScale: 1,
1487
- dampen: { mode: 'constant', value: 0.3 },
1488
- enableDynamicColliders: false,
1489
- colliderForce: 0,
1490
- multiplyByCollisionAngle: true,
1491
- multiplyByParticleSpeed: true,
1492
- multiplyByParticleSize: false,
1493
- sendCollisionMessages: false,
1494
- };
1495
- def.renderer.renderMode = 'stretchedBillboard';
1496
- def.renderer.velocityScale = 0.1;
1497
- def.renderer.lengthScale = 5;
1498
- return def;
1385
+ const def = createDefaultParticleSystem("Sparks");
1386
+ def.main.startLifetime = { mode: "randomBetweenTwoConstants", min: 0.2, max: 0.5 };
1387
+ def.main.startSize = { mode: "randomBetweenTwoConstants", min: 0.02, max: 0.05 };
1388
+ def.main.startSpeed = { mode: "randomBetweenTwoConstants", min: 3, max: 8 };
1389
+ def.main.startColor = {
1390
+ mode: "randomBetweenTwoColors",
1391
+ colorMin: [1, 0.8, 0, 1],
1392
+ colorMax: [1, 0.5, 0, 1]
1393
+ };
1394
+ def.main.gravityModifier = { mode: "constant", value: 1 };
1395
+ def.emission.rateOverTime = { mode: "constant", value: 0 };
1396
+ def.emission.bursts = [
1397
+ {
1398
+ time: 0,
1399
+ count: { mode: "randomBetweenTwoConstants", min: 10, max: 30 },
1400
+ cycles: 1,
1401
+ interval: 0.01,
1402
+ probability: 1
1403
+ }
1404
+ ];
1405
+ def.shape = {
1406
+ type: "sphere",
1407
+ radius: 0.1,
1408
+ radiusThickness: 0,
1409
+ arc: 360,
1410
+ arcMode: "random",
1411
+ arcSpeed: 1,
1412
+ position: [0, 0, 0],
1413
+ rotation: [0, 0, 0],
1414
+ scale: [1, 1, 1]
1415
+ };
1416
+ def.colorOverLifetime = {
1417
+ enabled: true,
1418
+ color: {
1419
+ mode: "gradient",
1420
+ gradient: {
1421
+ colorKeys: [
1422
+ { time: 0, color: [1, 0.9, 0.5] },
1423
+ { time: 0.5, color: [1, 0.5, 0] },
1424
+ { time: 1, color: [0.5, 0, 0] }
1425
+ ],
1426
+ alphaKeys: [
1427
+ { time: 0, alpha: 1 },
1428
+ { time: 0.8, alpha: 1 },
1429
+ { time: 1, alpha: 0 }
1430
+ ],
1431
+ mode: "blend"
1432
+ }
1433
+ }
1434
+ };
1435
+ def.trails = {
1436
+ enabled: true,
1437
+ ratio: 1,
1438
+ lifetime: { mode: "constant", value: 0.1 },
1439
+ minVertexDistance: 0.01,
1440
+ textureMode: "stretch",
1441
+ worldSpace: true,
1442
+ dieWithParticle: true,
1443
+ sizeAffectsWidth: true,
1444
+ sizeAffectsLifetime: false,
1445
+ inheritParticleColor: true,
1446
+ colorOverLifetime: { mode: "color", color: [1, 1, 1, 1] },
1447
+ widthOverTrail: {
1448
+ mode: "curve",
1449
+ curve: {
1450
+ keyframes: [
1451
+ { time: 0, value: 1, inTangent: 0, outTangent: 0, mode: "auto" },
1452
+ { time: 1, value: 0, inTangent: -1, outTangent: 0, mode: "auto" }
1453
+ ],
1454
+ preWrapMode: "clamp",
1455
+ postWrapMode: "clamp"
1456
+ },
1457
+ multiplier: 1
1458
+ },
1459
+ colorOverTrail: {
1460
+ mode: "gradient",
1461
+ gradient: {
1462
+ colorKeys: [
1463
+ { time: 0, color: [1, 0.8, 0] },
1464
+ { time: 1, color: [1, 0.2, 0] }
1465
+ ],
1466
+ alphaKeys: [
1467
+ { time: 0, alpha: 1 },
1468
+ { time: 1, alpha: 0 }
1469
+ ],
1470
+ mode: "blend"
1471
+ }
1472
+ },
1473
+ generateLightingData: false,
1474
+ shadowBias: 0.5
1475
+ };
1476
+ def.collision = {
1477
+ enabled: true,
1478
+ type: "planes",
1479
+ planes: [{ normal: [0, 1, 0], position: [0, 0, 0] }],
1480
+ bounce: { mode: "randomBetweenTwoConstants", min: 0.2, max: 0.6 },
1481
+ lifetimeLoss: { mode: "constant", value: 0.2 },
1482
+ minKillSpeed: 0.1,
1483
+ radiusScale: 1,
1484
+ dampen: { mode: "constant", value: 0.3 },
1485
+ enableDynamicColliders: false,
1486
+ colliderForce: 0,
1487
+ multiplyByCollisionAngle: true,
1488
+ multiplyByParticleSpeed: true,
1489
+ multiplyByParticleSize: false,
1490
+ sendCollisionMessages: false
1491
+ };
1492
+ def.renderer.renderMode = "stretchedBillboard";
1493
+ def.renderer.velocityScale = 0.1;
1494
+ def.renderer.lengthScale = 5;
1495
+ return def;
1499
1496
  }
1500
-
1501
- export { ParticleSystem2, createDefaultParticleSystem, createFireParticleSystem, createParticleSystem, createSmokeParticleSystem, createSparkParticleSystem };
1497
+ export {
1498
+ ParticleSystem2,
1499
+ createDefaultParticleSystem,
1500
+ createFireParticleSystem,
1501
+ createParticleSystem,
1502
+ createSmokeParticleSystem,
1503
+ createSparkParticleSystem
1504
+ };