@nice2dev/game-engine 0.1.0 → 1.0.3

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