@nice2dev/game-engine 1.3.15 → 1.3.17

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