lecodes-cli 0.18.2 → 0.19.2

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 (224) hide show
  1. package/dist/index.js +1866 -301
  2. package/package.json +14 -12
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk-types.json +1 -1
  5. package/src/api.ts +302 -0
  6. package/src/browserAuth.ts +87 -0
  7. package/src/cmgenTool.ts +104 -0
  8. package/src/commands/app.ts +891 -0
  9. package/src/commands/appAndroid.ts +603 -0
  10. package/src/commands/appDesktop.ts +326 -0
  11. package/src/commands/appDesktopMac.ts +470 -0
  12. package/src/commands/appIcon.ts +187 -0
  13. package/src/commands/appShared.ts +448 -0
  14. package/src/commands/appTemplates.ts +883 -0
  15. package/src/commands/appTemplatesAndroid.ts +599 -0
  16. package/src/commands/appTemplatesGradlew.ts +9 -0
  17. package/src/commands/assets.ts +28 -0
  18. package/src/commands/clone.ts +60 -0
  19. package/src/commands/compile.ts +253 -0
  20. package/src/commands/create.ts +59 -0
  21. package/src/commands/design.ts +863 -0
  22. package/src/commands/designTemplates.ts +11 -0
  23. package/src/commands/desktop.ts +122 -0
  24. package/src/commands/dev.ts +214 -0
  25. package/src/commands/diff.ts +59 -0
  26. package/src/commands/init.ts +191 -0
  27. package/src/commands/install.ts +198 -0
  28. package/src/commands/lightmap.ts +290 -0
  29. package/src/commands/link.ts +147 -0
  30. package/src/commands/login.ts +24 -0
  31. package/src/commands/navmesh.ts +225 -0
  32. package/src/commands/pn.ts +305 -0
  33. package/src/commands/projectTemplates.ts +129 -0
  34. package/src/commands/pull.ts +109 -0
  35. package/src/commands/push.ts +173 -0
  36. package/src/commands/render.ts +414 -0
  37. package/src/commands/scene.ts +148 -0
  38. package/src/commands/shaders.ts +157 -0
  39. package/src/commands/status.ts +29 -0
  40. package/src/commands/test.ts +370 -0
  41. package/src/commands/thumbs.ts +178 -0
  42. package/src/commands/types.ts +87 -0
  43. package/src/commands/update.ts +190 -0
  44. package/src/compile/assetIcons.ts +214 -0
  45. package/src/compile/collect.ts +46 -0
  46. package/src/compile/collectLocal.ts +37 -0
  47. package/src/compile/designCompile.ts +104 -0
  48. package/src/compile/fonts.ts +156 -0
  49. package/src/compile/headlessBundle.ts +129 -0
  50. package/src/compile/nativeStack.ts +21 -0
  51. package/src/compile/projectCompile.ts +109 -0
  52. package/src/compile/sceneCompile.ts +113 -0
  53. package/src/compile/screenEntry.ts +127 -0
  54. package/src/compile/shaders.ts +245 -0
  55. package/src/config.ts +42 -0
  56. package/src/designMeta.ts +35 -0
  57. package/src/desktopRenderer.ts +532 -0
  58. package/src/desktopScript.ts +276 -0
  59. package/src/dev/clientTemplates.ts +160 -0
  60. package/src/dev/devServer.ts +292 -0
  61. package/src/dev/wsServer.ts +144 -0
  62. package/src/distRoot.ts +20 -0
  63. package/src/ignore.ts +163 -0
  64. package/src/index.ts +491 -0
  65. package/src/lecodes-3d-editor.d.ts +41 -0
  66. package/src/lecodes-assets.d.ts +7 -0
  67. package/src/lecodes-design.d.ts +191 -0
  68. package/src/lecodes-renderer.d.ts +131 -0
  69. package/src/localFiles.ts +144 -0
  70. package/src/manifest.ts +46 -0
  71. package/src/matcTool.ts +137 -0
  72. package/src/peers.ts +40 -0
  73. package/src/project.ts +61 -0
  74. package/src/projectEnv.ts +94 -0
  75. package/src/qrcode-terminal.d.ts +9 -0
  76. package/src/releases.ts +125 -0
  77. package/src/serverDiff.ts +78 -0
  78. package/src/textDiff.ts +103 -0
  79. package/src/types.ts +0 -0
  80. package/src/util.ts +146 -0
  81. package/runtime/sdk/animate/animate.ts +0 -238
  82. package/runtime/sdk/animate/bezier.ts +0 -138
  83. package/runtime/sdk/animate/easings.ts +0 -126
  84. package/runtime/sdk/canvas/Canvas.ts +0 -305
  85. package/runtime/sdk/core/Aspect.ts +0 -512
  86. package/runtime/sdk/core/InspectorUI.ts +0 -212
  87. package/runtime/sdk/core/color.ts +0 -66
  88. package/runtime/sdk/core/compWrite.ts +0 -42
  89. package/runtime/sdk/core/events.ts +0 -38
  90. package/runtime/sdk/core/fields.ts +0 -120
  91. package/runtime/sdk/core/registry.ts +0 -23
  92. package/runtime/sdk/core/signals.ts +0 -277
  93. package/runtime/sdk/core/time.ts +0 -81
  94. package/runtime/sdk/g2/Camera2D.ts +0 -40
  95. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  96. package/runtime/sdk/g2/Node2D.ts +0 -267
  97. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  98. package/runtime/sdk/g2/Physics2D.ts +0 -346
  99. package/runtime/sdk/g2/Scene2D.ts +0 -209
  100. package/runtime/sdk/g2/Shape2D.ts +0 -259
  101. package/runtime/sdk/g2/Sprite.ts +0 -89
  102. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  103. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  104. package/runtime/sdk/g2/Texture2D.ts +0 -47
  105. package/runtime/sdk/g2/Tilemap.ts +0 -41
  106. package/runtime/sdk/g2/Tileset.ts +0 -71
  107. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  108. package/runtime/sdk/g2/autotile.ts +0 -433
  109. package/runtime/sdk/g2/cells.ts +0 -91
  110. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  111. package/runtime/sdk/g2/groups2d.ts +0 -106
  112. package/runtime/sdk/g2/loop.ts +0 -50
  113. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  114. package/runtime/sdk/g2/touch.ts +0 -83
  115. package/runtime/sdk/gl/Camera.ts +0 -160
  116. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  117. package/runtime/sdk/gl/CharacterController.ts +0 -238
  118. package/runtime/sdk/gl/Gearbox.ts +0 -212
  119. package/runtime/sdk/gl/Geometry.ts +0 -279
  120. package/runtime/sdk/gl/IK.ts +0 -193
  121. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  122. package/runtime/sdk/gl/Light.ts +0 -99
  123. package/runtime/sdk/gl/Lightmap.ts +0 -179
  124. package/runtime/sdk/gl/Material.ts +0 -245
  125. package/runtime/sdk/gl/Mesh.ts +0 -83
  126. package/runtime/sdk/gl/Model.ts +0 -64
  127. package/runtime/sdk/gl/Node.ts +0 -350
  128. package/runtime/sdk/gl/Noise.ts +0 -30
  129. package/runtime/sdk/gl/Particles.ts +0 -676
  130. package/runtime/sdk/gl/Physics.ts +0 -222
  131. package/runtime/sdk/gl/Plane.ts +0 -53
  132. package/runtime/sdk/gl/Ray.ts +0 -16
  133. package/runtime/sdk/gl/Scene.ts +0 -479
  134. package/runtime/sdk/gl/Shape.ts +0 -377
  135. package/runtime/sdk/gl/Texture.ts +0 -46
  136. package/runtime/sdk/gl/Trigger.ts +0 -45
  137. package/runtime/sdk/gl/Vehicle.ts +0 -473
  138. package/runtime/sdk/gl/Wheel.ts +0 -240
  139. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  140. package/runtime/sdk/gl/animation/Animator.ts +0 -87
  141. package/runtime/sdk/gl/animation/Layer.ts +0 -29
  142. package/runtime/sdk/gl/animation/Loop.ts +0 -25
  143. package/runtime/sdk/gl/animation/Playback.ts +0 -43
  144. package/runtime/sdk/gl/animation/core.ts +0 -294
  145. package/runtime/sdk/gl/controls.ts +0 -95
  146. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  147. package/runtime/sdk/gl/scenarios.ts +0 -291
  148. package/runtime/sdk/gl/state.ts +0 -6
  149. package/runtime/sdk/gl/touch.ts +0 -68
  150. package/runtime/sdk/inject.ts +0 -186
  151. package/runtime/sdk/math/Mathf.ts +0 -118
  152. package/runtime/sdk/math/mat4.ts +0 -278
  153. package/runtime/sdk/math/quat.ts +0 -232
  154. package/runtime/sdk/math/vec.ts +0 -255
  155. package/runtime/sdk/plugins/camera.ts +0 -81
  156. package/runtime/sdk/plugins/geolocation.ts +0 -123
  157. package/runtime/sdk/plugins/oauth.ts +0 -61
  158. package/runtime/sdk/plugins/permission.ts +0 -7
  159. package/runtime/sdk/plugins/push.ts +0 -132
  160. package/runtime/sdk/plugins/qr.ts +0 -73
  161. package/runtime/sdk/plugins/service.ts +0 -47
  162. package/runtime/sdk/runtime/app.ts +0 -101
  163. package/runtime/sdk/runtime/appEvents.ts +0 -54
  164. package/runtime/sdk/runtime/channel.ts +0 -61
  165. package/runtime/sdk/runtime/clipboard.ts +0 -20
  166. package/runtime/sdk/runtime/datetime.ts +0 -329
  167. package/runtime/sdk/runtime/device.ts +0 -293
  168. package/runtime/sdk/runtime/fetch.ts +0 -77
  169. package/runtime/sdk/runtime/files.ts +0 -23
  170. package/runtime/sdk/runtime/input.ts +0 -175
  171. package/runtime/sdk/runtime/media.ts +0 -111
  172. package/runtime/sdk/runtime/misc.ts +0 -16
  173. package/runtime/sdk/runtime/net.ts +0 -36
  174. package/runtime/sdk/runtime/rpc.ts +0 -218
  175. package/runtime/sdk/runtime/service.ts +0 -83
  176. package/runtime/sdk/runtime/share.ts +0 -9
  177. package/runtime/sdk/runtime/storage.ts +0 -13
  178. package/runtime/sdk/runtime/touch.ts +0 -76
  179. package/runtime/sdk/scene/defineScene.ts +0 -1227
  180. package/runtime/sdk/scene/editorPlugins.ts +0 -92
  181. package/runtime/sdk/scene/gizmos.ts +0 -148
  182. package/runtime/sdk/scene/grammar.ts +0 -120
  183. package/runtime/sdk/scene/material.ts +0 -188
  184. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  185. package/runtime/sdk/server/auth/global.ts +0 -80
  186. package/runtime/sdk/server/auth/host.ts +0 -318
  187. package/runtime/sdk/server/auth/models.ts +0 -83
  188. package/runtime/sdk/server/auth/types.ts +0 -50
  189. package/runtime/sdk/server/channel.ts +0 -56
  190. package/runtime/sdk/server/context.ts +0 -36
  191. package/runtime/sdk/server/db/defineDb.ts +0 -237
  192. package/runtime/sdk/server/db/fields.ts +0 -132
  193. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  194. package/runtime/sdk/server/db/index.ts +0 -7
  195. package/runtime/sdk/server/db/marci/query.ts +0 -412
  196. package/runtime/sdk/server/db/types.ts +0 -202
  197. package/runtime/sdk/server/errors.ts +0 -12
  198. package/runtime/sdk/server/host.ts +0 -74
  199. package/runtime/sdk/server/inject.ts +0 -13
  200. package/runtime/sdk/server/runtime.ts +0 -133
  201. package/runtime/sdk/server/validate.ts +0 -87
  202. package/runtime/sdk/ui/NativeView.ts +0 -142
  203. package/runtime/sdk/ui/UI.ts +0 -39
  204. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  205. package/runtime/sdk/ui/UIButton.ts +0 -101
  206. package/runtime/sdk/ui/UIContainer.ts +0 -60
  207. package/runtime/sdk/ui/UIImage.ts +0 -83
  208. package/runtime/sdk/ui/UIInput.ts +0 -185
  209. package/runtime/sdk/ui/UIModal.ts +0 -139
  210. package/runtime/sdk/ui/UINode.ts +0 -826
  211. package/runtime/sdk/ui/UIPager.ts +0 -362
  212. package/runtime/sdk/ui/UIPopover.ts +0 -100
  213. package/runtime/sdk/ui/UIScreen.ts +0 -123
  214. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  215. package/runtime/sdk/ui/UISpacer.ts +0 -14
  216. package/runtime/sdk/ui/UITabs.ts +0 -236
  217. package/runtime/sdk/ui/UIText.ts +0 -51
  218. package/runtime/sdk/ui/UIVideo.ts +0 -88
  219. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  220. package/runtime/sdk/ui/UIWidget.ts +0 -127
  221. package/runtime/sdk/ui/fonts.ts +0 -13
  222. package/runtime/sdk/ui/presentable.ts +0 -117
  223. package/runtime/sdk/ui/router.ts +0 -132
  224. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,676 +0,0 @@
1
- // GPU particle system, as a Node. Draws with the default point-sprite material (Material.particles)
2
- // unless a custom one is passed; properties drive the native emitter.
3
- //
4
- // All emitter/curve config crosses the bridge as ONE Float32Array of [tag, payloadLen, ...payload]
5
- // records (_creator.setParticleSystemConfig) — the constructor batches every option into a single
6
- // call, live setters send a one-record buffer. The tag values mirror PsTag in
7
- // creator-gl/src/particles.h (guarded by tests/particles-tags.test.ts).
8
- //
9
- // Curves over a particle's lifetime are built with curve()/colorCurve() chains — see the builders
10
- // below. A builder's whole state is the flat `_data` array (the exact record payload), so the
11
- // chisel compiler can constant-fold literal chains into the buffer at compile time.
12
-
13
- import { Color, type ColorInput } from "../core/color"
14
- import { Vec3, cx, cy, cz, type Vec3Like } from "../math/vec"
15
- import type { Geometry } from "./Geometry"
16
- import { Material, _sheetColsRows, type ParticlesMaterialOptions } from "./Material"
17
- import { Node } from "./Node"
18
-
19
- type Range<T> = T | { min: T, max: T }
20
-
21
- // --- config record tags (keep in sync with PsTag in creator-gl/src/particles.h) -----------------
22
-
23
- const TAG_SHAPE = 1
24
- const TAG_LIFETIME = 2
25
- const TAG_RATE = 3
26
- const TAG_VELOCITY = 4
27
- const TAG_VELOCITY_RND = 5
28
- const TAG_ACCELERATION = 6
29
- const TAG_DRAG = 7
30
- const TAG_NOISE = 8
31
- const TAG_SEED = 9
32
- const TAG_PARAM_CURVE = 10
33
- const TAG_COLOR_CURVE = 11
34
- const TAG_SPACE = 12
35
- const TAG_INHERIT_VELOCITY = 13
36
- const TAG_RATE_DISTANCE = 14
37
- const TAG_RENDER_MODE = 15
38
- const TAG_MESH_GEOMETRY = 16
39
- const TAG_ANGULAR_VELOCITY = 17
40
- const TAG_GROUND = 18
41
- const TAG_VELOCITY_LIFE = 19
42
- const TAG_ORDER = 20
43
-
44
- /** @internal — exported for the tag-sync test only. */
45
- export const _particleTags = {
46
- SHAPE: TAG_SHAPE, LIFETIME: TAG_LIFETIME, RATE: TAG_RATE, VELOCITY: TAG_VELOCITY,
47
- VELOCITY_RND: TAG_VELOCITY_RND, ACCELERATION: TAG_ACCELERATION, DRAG: TAG_DRAG,
48
- NOISE: TAG_NOISE, SEED: TAG_SEED, PARAM_CURVE: TAG_PARAM_CURVE, COLOR_CURVE: TAG_COLOR_CURVE,
49
- SPACE: TAG_SPACE, INHERIT_VELOCITY: TAG_INHERIT_VELOCITY, RATE_DISTANCE: TAG_RATE_DISTANCE,
50
- RENDER_MODE: TAG_RENDER_MODE, MESH_GEOMETRY: TAG_MESH_GEOMETRY,
51
- ANGULAR_VELOCITY: TAG_ANGULAR_VELOCITY, GROUND: TAG_GROUND, VELOCITY_LIFE: TAG_VELOCITY_LIFE,
52
- ORDER: TAG_ORDER,
53
- }
54
-
55
- // --- curve builders -----------------------------------------------------------------------------
56
-
57
- // A value curve reads as a journey over the particle's life:
58
- // curve(0.3).from(0).via(0.15, 1).via(0.6, 1).to(0) // fade in, hold, fade out — or:
59
- // curve(0.3).fade(0.15, 0.4)
60
- // The curve multiplies the base value (or adds to it with mode 'add'); the base and any stop can be
61
- // a { min, max } range, randomized per particle. With no `.from` the curve starts at the identity
62
- // (1 for multiply, 0 for add); with no `.to` it holds its last value to end of life.
63
- //
64
- // chisel constant-folds a literal chain into its `_data` buffer at compile time and validates it
65
- // (bad stop order, >8 stops, unparsable color → a build diagnostic): chisel-core/src/curve.rs mirrors
66
- // these method bodies op-for-op, against a verbatim copy of them in chisel's fixtures/curve-sdk/. Keep
67
- // the two in step — a change here means a change there (chisel's scripts/curve-exact.mjs compares the
68
- // fused payload against the real builder, buffer for buffer).
69
-
70
- const lohi = (v: Range<number>): [number, number] =>
71
- typeof v === "number" ? [ v, v ] : [ v.min, v.max ]
72
-
73
- export class CurveBuilder {
74
- private _kind: 0 | 1
75
- private _lo: number
76
- private _hi: number
77
- private _stops: number[] = [] // flat (t, lo, hi)
78
-
79
- constructor(base: Range<number>, mode: "multiply" | "add") {
80
- this._kind = mode === "add" ? 1 : 0
81
- ;[ this._lo, this._hi ] = lohi(base)
82
- }
83
-
84
- /** Curve value at birth (t = 0). Must come before any other stop. */
85
- from(v: Range<number>): this {
86
- if (this._stops.length > 0) throw new Error("curve: .from() must come first")
87
- return this.via(0, v)
88
- }
89
-
90
- /** Curve value at `t` (0..1 of the particle's life). Stops must be added in ascending t order. */
91
- via(t: number, v: Range<number>): this {
92
- const [ lo, hi ] = lohi(v)
93
- this._stops.push(t, lo, hi)
94
- return this
95
- }
96
-
97
- /** Curve value at death (t = 1). */
98
- to(v: Range<number>): this {
99
- return this.via(1, v)
100
- }
101
-
102
- /** The classic opacity envelope: rise over the first `fadeIn`, fall over the last `fadeOut`. */
103
- fade(fadeIn = 0.15, fadeOut = fadeIn): this {
104
- const up = Math.min(Math.max(fadeIn, 0), 1)
105
- const down = Math.min(Math.max(fadeOut, 0), 1 - up)
106
- if (up > 0) this.via(0, 0).via(up, 1)
107
- else this.via(0, 1)
108
- if (down > 0) {
109
- if (1 - down > up) this.via(1 - down, 1)
110
- this.via(1, 0)
111
- }
112
- return this
113
- }
114
-
115
- /** The PARAM_CURVE record payload (sans slot): [kind, baseLo, baseHi, n, (t, lo, hi)×n]. */
116
- get _data(): Float32Array {
117
- const stops = this._stops
118
- const prepend = stops.length > 0 && stops[0] > 0
119
- const n = stops.length / 3 + (prepend ? 1 : 0)
120
- const out = new Float32Array(4 + n * 3)
121
- out[0] = this._kind
122
- out[1] = this._lo
123
- out[2] = this._hi
124
- out[3] = n
125
- let o = 4
126
- if (prepend) {
127
- const identity = this._kind === 1 ? 0 : 1
128
- out[o + 1] = identity
129
- out[o + 2] = identity
130
- o += 3
131
- }
132
- out.set(stops, o)
133
- return out
134
- }
135
- }
136
-
137
- export class ColorCurveBuilder {
138
- private _lo: [number, number, number, number]
139
- private _hi: [number, number, number, number]
140
- private _stops: number[] = [] // flat (t, lo rgba, hi rgba)
141
-
142
- constructor(base: Range<ColorInput>) {
143
- if (isColorRange(base)) {
144
- this._lo = Color.toRgba01(base.min)
145
- this._hi = Color.toRgba01(base.max)
146
- } else {
147
- this._lo = Color.toRgba01(base)
148
- this._hi = this._lo
149
- }
150
- }
151
-
152
- /** Curve color at birth (t = 0). Must come first. The curve multiplies the base color. */
153
- from(c: Range<ColorInput>): this {
154
- if (this._stops.length > 0) throw new Error("colorCurve: .from() must come first")
155
- return this.via(0, c)
156
- }
157
-
158
- /** Curve color at `t` (0..1 of the particle's life), in ascending t order. */
159
- via(t: number, c: Range<ColorInput>): this {
160
- let lo: number[], hi: number[]
161
- if (isColorRange(c)) {
162
- lo = Color.toRgba01(c.min)
163
- hi = Color.toRgba01(c.max)
164
- } else {
165
- lo = hi = Color.toRgba01(c)
166
- }
167
- this._stops.push(t, ...lo, ...hi)
168
- return this
169
- }
170
-
171
- /** Curve color at death (t = 1). */
172
- to(c: Range<ColorInput>): this {
173
- return this.via(1, c)
174
- }
175
-
176
- /** The COLOR_CURVE record payload: [baseLo rgba, baseHi rgba, n, (t, lo rgba, hi rgba)×n]. */
177
- get _data(): Float32Array {
178
- const stops = this._stops
179
- const prepend = stops.length > 0 && stops[0] > 0
180
- const n = stops.length / 9 + (prepend ? 1 : 0)
181
- const out = new Float32Array(9 + n * 9)
182
- out.set(this._lo, 0)
183
- out.set(this._hi, 4)
184
- out[8] = n
185
- let o = 9
186
- if (prepend) {
187
- for (let i = 1; i < 9; i++) out[o + i] = 1 // identity multiplier: white, t = 0
188
- o += 9
189
- }
190
- out.set(stops, o)
191
- return out
192
- }
193
- }
194
-
195
- /** A value curve over each particle's lifetime. Default mode `multiply` (the curve scales the
196
- * base); `add` offsets it instead. */
197
- export const curve = (base: Range<number> = 1, mode: "multiply" | "add" = "multiply"): CurveBuilder =>
198
- new CurveBuilder(base, mode)
199
-
200
- /** A color curve over each particle's lifetime — always multiplies the base color. */
201
- export const colorCurve = (base: Range<ColorInput> = "#ffffff"): ColorCurveBuilder =>
202
- new ColorCurveBuilder(base)
203
-
204
- // --- option types -------------------------------------------------------------------------------
205
-
206
- export type ParticleValue = Range<number> | CurveBuilder | { _data: Float32Array }
207
- export type ParticleColor = ColorInput | { min: ColorInput, max: ColorInput } | ColorCurveBuilder | { _data: Float32Array }
208
-
209
- type VelocityValue = {
210
- speed?: Range<number>
211
- /** Cone half-angle in radians — jitter the launch direction uniformly within it. */
212
- spread?: number
213
- /** Asymmetric jitter: two angles (u, v — the x/y components) in radians. */
214
- randomizeAngle?: { min: Vec3Like, max: Vec3Like }
215
- } & ({ from: Vec3Like } | { to: Vec3Like } | { dir: Vec3Like } | {})
216
-
217
- /** Where particles are born, in emitter space. Sphere / hemisphere / cone have an axis of +Y (the
218
- * hemisphere is the +Y half, the cone opens along +Y from its base disc at `center`) — aim the
219
- * node with `lookAt(target, 'y')`. `thickness` 0 emits from the surface only, 1 (default) from the
220
- * whole volume. A sphere or cone also sets the LAUNCH DIRECTION (outward / from the cone's apex)
221
- * when `startVelocity` gives a speed but no direction, like Unity's shapes. */
222
- type Shape =
223
- | { type: "point", v: Vec3Like }
224
- | { type: "box", min: Vec3Like, max: Vec3Like }
225
- | { type: "circle", center: Vec3Like, radius: number }
226
- | { type: "sphere", center?: Vec3Like, radius: number, thickness?: number }
227
- | { type: "hemisphere", center?: Vec3Like, radius: number, thickness?: number }
228
- | { type: "cone", center?: Vec3Like, radius: number, /** half-angle, radians */ angle?: number, /** emit this far up the axis too */ length?: number, thickness?: number }
229
- /** Extra velocity over each particle's life — evaluated from the curves every frame and ADDED as a
230
- * displacement (not accumulated like `acceleration`): `radial` pushes outward along the particle's
231
- * birth direction (a burst that decays: `curve(4).to(0)`), `x`/`y`/`z` drift in the simulation
232
- * space (rising smoke: `y: curve(2).from(0).to(1)`). Unity's velocityOverLifetime. */
233
- export type VelocityOverLife = { radial?: ParticleValue, x?: ParticleValue, y?: ParticleValue, z?: ParticleValue }
234
- type Noise = { strength?: number, frequency?: number, speed?: number }
235
-
236
- /** Ground plane for bouncing particles (all render modes). `height` is in the simulation space:
237
- * world y with `space: 'world'`, emitter-local y otherwise. */
238
- export type ParticleGround = {
239
- /** Plane height (default 0). */
240
- height?: number
241
- /** Contact radius — particles stop `radius` above the plane (a mesh's half-extent; default 0). */
242
- radius?: number
243
- /** Fraction of the vertical speed kept on impact, 0..1 (default 0.3). */
244
- bounce?: number
245
- /** Tangential damping while in contact, per second — like `drag` (default 8). */
246
- friction?: number
247
- }
248
-
249
- export type ParticlesOptions = ParticlesMaterialOptions & {
250
- /** Draw every particle as this mesh — lit, tumbling 3D debris (shell casings, rubble, leaves,
251
- * shrapnel) in one draw call. The system switches to the mesh render mode; the default
252
- * material becomes `Material.lit()` (pass `material` for color/roughness/metallic — the
253
- * sprite look options are ignored). `size` scales the mesh (default 1); `rotation`/`opacity`/
254
- * `frame` don't apply. See `angularVelocity` and `ground`. */
255
- mesh?: Geometry
256
- /** (mesh) Spin, radians per second about the particle's own axes — a vector, or a per-component
257
- * `{ min, max }` range randomized at spawn. Particles are born in the emitter's orientation. */
258
- angularVelocity?: Vec3Like | { min: Vec3Like, max: Vec3Like }
259
- /** A ground plane the particles bounce on and come to rest on (any render mode); `null`
260
- * disables it. */
261
- ground?: ParticleGround | null
262
- /** Custom draw material. Unset = the default point-sprite material, configured by the
263
- * `ParticlesMaterialOptions` sugar (map/sheet/emissive/blend/soft) — which is ignored
264
- * when an explicit material is passed. */
265
- material?: Material
266
- /** Particle pool capacity (default 1000). */
267
- maxParticles?: number
268
- /** Particles emitted per second. */
269
- rate?: number
270
- /** Simulation space. `'local'` (default) — particles ride the node's transform; `'world'` —
271
- * each particle stays where it was born as the emitter moves on (trails: drift smoke, mud,
272
- * wake). Switching at runtime resets live particles. */
273
- space?: "local" | "world"
274
- /** Fraction (usually 0..1) of the emitter's own velocity added to each particle at spawn —
275
- * smoke "thrown" off a moving car. Takes effect with `space: 'world'`. */
276
- inheritVelocity?: number
277
- /** Particles emitted per world unit the emitter MOVES, on top of `rate`, with spawn points
278
- * spread evenly along the path — trail density independent of speed, no per-frame clumps.
279
- * Meant for `space: 'world'`. */
280
- rateOverDistance?: number
281
- /** Draw order among blended systems at the same depth — higher draws later, i.e. on top (Unity's
282
- * sortingOrder). Systems are depth-sorted by their node, so the emitters of one effect tie and
283
- * without this the engine picks who covers whom per frame (smoke popping over a fireball).
284
- * Default 0; a fireball wants 2, its smoke 1, a smoke trail -1. */
285
- order?: number
286
- shape?: Shape
287
- /** Extra velocity over life (radial burst that decays, axis drift) — see `VelocityOverLife`. */
288
- velocityOverLife?: VelocityOverLife
289
- startVelocity?: Vec3Like | VelocityValue
290
- /** Downward acceleration, world units/s² — positive pulls particles DOWN (sugar for
291
- * `acceleration: [0, -g, 0]`). */
292
- gravity?: number
293
- /** Constant acceleration vector (gravity, wind, …). Wins over `gravity` when both are set. */
294
- acceleration?: Vec3Like
295
- drag?: number
296
- lifetime?: Range<number>
297
- color?: ParticleColor
298
- /** World-unit diameter (alias of custom[0]). Default 1. */
299
- size?: ParticleValue
300
- /** Radians (alias of custom[1]). */
301
- rotation?: ParticleValue
302
- /** Per-particle opacity 0..1 (alias of custom[2]). Default 1. */
303
- opacity?: ParticleValue
304
- /** Flipbook frame index (alias of custom[3]). With `sheet` set and no explicit frame, the
305
- * default is one full sheet cycle over each particle's lifetime, starting at `startFrame`. */
306
- frame?: ParticleValue
307
- /** Where the default flipbook cycle begins: `'random'` (default — desynchronized loops like
308
- * fire/smoke) or a frame index (`0` plays the sheet in order — explosions, one-shot bursts).
309
- * Only shapes the default; an explicit `frame` wins. */
310
- startFrame?: number | "random"
311
- noise?: Noise | null
312
- /** Fixed random seed for deterministic tests. */
313
- seed?: number
314
- }
315
-
316
- // --- record assembly ----------------------------------------------------------------------------
317
-
318
- const isColorRange = (v: unknown): v is { min: ColorInput, max: ColorInput } =>
319
- typeof v === "object" && v !== null && !Array.isArray(v) && "min" in v
320
-
321
- const hasData = (v: unknown): v is { _data: Float32Array } =>
322
- typeof v === "object" && v !== null && "_data" in v
323
-
324
- const pushParam = (out: number[], slot: number, value: ParticleValue): void => {
325
- if (typeof value === "number") {
326
- out.push(TAG_PARAM_CURVE, 5, slot, 0, value, value, 0)
327
- } else if (hasData(value)) {
328
- const d = value._data
329
- out.push(TAG_PARAM_CURVE, d.length + 1, slot)
330
- for (let i = 0; i < d.length; i++) out.push(d[i])
331
- } else {
332
- out.push(TAG_PARAM_CURVE, 5, slot, 0, value.min, value.max, 0)
333
- }
334
- }
335
-
336
- const pushColor = (out: number[], value: ParticleColor): void => {
337
- let d: Float32Array
338
- if (hasData(value)) {
339
- d = value._data
340
- } else if (isColorRange(value)) {
341
- d = new ColorCurveBuilder(value)._data
342
- } else {
343
- d = new ColorCurveBuilder(value as ColorInput)._data
344
- }
345
- out.push(TAG_COLOR_CURVE, d.length)
346
- for (let i = 0; i < d.length; i++) out.push(d[i])
347
- }
348
-
349
- const pushMesh = (out: number[], g: Geometry): void => {
350
- const nv = g.vertices.length / 3
351
- const ni = g.indices.length
352
- out.push(TAG_MESH_GEOMETRY, 2 + nv * 8 + ni, nv, ni)
353
- for (let i = 0; i < nv * 3; i++) out.push(g.vertices[i])
354
- for (let i = 0; i < nv * 3; i++) out.push(g.normals[i])
355
- for (let i = 0; i < nv * 2; i++) out.push(g.uv[i])
356
- for (let i = 0; i < ni; i++) out.push(g.indices[i])
357
- }
358
-
359
- const pushAngularVelocity = (out: number[], v: Vec3Like | { min: Vec3Like, max: Vec3Like }): void => {
360
- const r = v as { min: Vec3Like, max: Vec3Like }
361
- if (typeof r === "object" && r !== null && !Array.isArray(r) && "min" in r && !(v instanceof Float32Array) && !(v instanceof Vec3)) {
362
- out.push(TAG_ANGULAR_VELOCITY, 6, cx(r.min), cy(r.min), cz(r.min), cx(r.max), cy(r.max), cz(r.max))
363
- } else {
364
- const a = v as Vec3Like
365
- out.push(TAG_ANGULAR_VELOCITY, 6, cx(a), cy(a), cz(a), cx(a), cy(a), cz(a))
366
- }
367
- }
368
-
369
- const pushGround = (out: number[], g: ParticleGround | null): void => {
370
- if (g === null) out.push(TAG_GROUND, 5, 0, 0, 0, 0, 0)
371
- else out.push(TAG_GROUND, 5, 1, g.height ?? 0, g.radius ?? 0, g.bounce ?? 0.3, g.friction ?? 8)
372
- }
373
-
374
- const pushShape = (out: number[], shape: Shape): void => {
375
- if (shape.type === "point") out.push(TAG_SHAPE, 4, 0, cx(shape.v), cy(shape.v), cz(shape.v))
376
- else if (shape.type === "box") out.push(TAG_SHAPE, 7, 1, cx(shape.min), cy(shape.min), cz(shape.min), cx(shape.max), cy(shape.max), cz(shape.max))
377
- else if (shape.type === "circle") out.push(TAG_SHAPE, 5, 2, cx(shape.center), cy(shape.center), cz(shape.center), shape.radius)
378
- else {
379
- const c = shape.center ?? [ 0, 0, 0 ]
380
- const thickness = shape.thickness ?? 1
381
- if (shape.type === "cone") out.push(TAG_SHAPE, 8, 5, cx(c), cy(c), cz(c), shape.radius, shape.angle ?? 0, shape.length ?? 0, thickness)
382
- else out.push(TAG_SHAPE, 6, shape.type === "sphere" ? 3 : 4, cx(c), cy(c), cz(c), shape.radius, thickness)
383
- }
384
- }
385
-
386
- const pushVelocityLife = (out: number[], v: VelocityOverLife): void => {
387
- const axes: Array<[number, ParticleValue | undefined]> = [ [ 0, v.radial ], [ 1, v.x ], [ 2, v.y ], [ 3, v.z ] ]
388
- for (const [ axis, value ] of axes) {
389
- if (value === undefined) continue
390
- if (typeof value === "number") out.push(TAG_VELOCITY_LIFE, 5, axis, 0, value, value, 0)
391
- else if (hasData(value)) {
392
- const d = value._data
393
- out.push(TAG_VELOCITY_LIFE, d.length + 1, axis)
394
- for (let i = 0; i < d.length; i++) out.push(d[i])
395
- } else out.push(TAG_VELOCITY_LIFE, 5, axis, 0, value.min, value.max, 0)
396
- }
397
- }
398
-
399
- const pushVelocity = (out: number[], val: Vec3Like | VelocityValue): void => {
400
- if (Array.isArray(val) || val instanceof Float32Array || val instanceof Vec3) {
401
- // a plain vector: launch along it, its length is the speed (native derives it)
402
- out.push(TAG_VELOCITY, 6, 1, cx(val), cy(val), cz(val), 0, 0)
403
- out.push(TAG_VELOCITY_RND, 5, 0, 0, 0, 0, 0)
404
- return
405
- }
406
- const v = val as VelocityValue
407
- let mode = 0
408
- let p: Vec3Like = [ 0, 0, 0 ]
409
- if ("dir" in v) { mode = 1; p = v.dir }
410
- else if ("from" in v) { mode = 2; p = v.from }
411
- else if ("to" in v) { mode = 3; p = v.to }
412
- let lo = 0, hi = 0
413
- if (v.speed !== undefined) {
414
- if (typeof v.speed === "number") lo = hi = v.speed
415
- else { lo = v.speed.min; hi = v.speed.max }
416
- }
417
- out.push(TAG_VELOCITY, 6, mode, cx(p), cy(p), cz(p), lo, hi)
418
- if (v.spread !== undefined) {
419
- out.push(TAG_VELOCITY_RND, 5, 1, -v.spread, -v.spread, v.spread, v.spread)
420
- } else if (v.randomizeAngle) {
421
- const a = v.randomizeAngle
422
- out.push(TAG_VELOCITY_RND, 5, 1, cx(a.min), cy(a.min), cx(a.max), cy(a.max))
423
- } else {
424
- out.push(TAG_VELOCITY_RND, 5, 0, 0, 0, 0, 0)
425
- }
426
- }
427
-
428
- // --- the node -----------------------------------------------------------------------------------
429
-
430
- export class Particles extends Node {
431
- private _material: Material
432
-
433
- /** Custom curve-param slots (0..3); size/rotation/opacity/frame alias slots 0-3. */
434
- readonly custom: ParticleValue[]
435
-
436
- constructor(options: ParticlesOptions = {}) {
437
- super()
438
- this._material = options.material ?? (options.mesh ? Material.lit() : Material.particles(options))
439
- _creator.createParticleSystem(this.id, Material.idOf(this._material), options.maxParticles ?? 0)
440
-
441
- const id = this.id
442
- this.custom = new Proxy([] as ParticleValue[], {
443
- set(target, prop: string, value: ParticleValue) {
444
- const i = parseInt(prop)
445
- if (Number.isNaN(i)) { (target as any)[prop] = value; return true }
446
- target[i] = value
447
- const out: number[] = []
448
- pushParam(out, i, value)
449
- _creator.setParticleSystemConfig(id, Float32Array.from(out))
450
- return true
451
- },
452
- })
453
-
454
- const out: number[] = []
455
- if (options.mesh) {
456
- out.push(TAG_RENDER_MODE, 1, 3)
457
- pushMesh(out, options.mesh)
458
- } else if (options.render !== undefined && options.render !== "point") {
459
- out.push(TAG_RENDER_MODE, 1, options.render === "ribbon" ? 2 : 1)
460
- }
461
- if (options.angularVelocity !== undefined) pushAngularVelocity(out, options.angularVelocity)
462
- if (options.ground !== undefined) pushGround(out, options.ground)
463
- if (options.space !== undefined) out.push(TAG_SPACE, 1, options.space === "world" ? 1 : 0)
464
- if (options.inheritVelocity !== undefined) out.push(TAG_INHERIT_VELOCITY, 1, options.inheritVelocity)
465
- if (options.rateOverDistance !== undefined) out.push(TAG_RATE_DISTANCE, 1, options.rateOverDistance)
466
- if (options.order !== undefined) out.push(TAG_ORDER, 1, options.order)
467
- if (options.rate !== undefined) out.push(TAG_RATE, 1, options.rate)
468
- if (options.shape !== undefined) pushShape(out, options.shape)
469
- if (options.velocityOverLife !== undefined) pushVelocityLife(out, options.velocityOverLife)
470
- if (options.startVelocity !== undefined) pushVelocity(out, options.startVelocity)
471
- if (options.acceleration !== undefined) {
472
- const a = options.acceleration
473
- out.push(TAG_ACCELERATION, 3, cx(a), cy(a), cz(a))
474
- } else if (options.gravity !== undefined) {
475
- out.push(TAG_ACCELERATION, 3, 0, -options.gravity, 0)
476
- }
477
- if (options.drag !== undefined) out.push(TAG_DRAG, 1, options.drag)
478
- if (options.lifetime !== undefined) {
479
- const [ lo, hi ] = lohi(options.lifetime)
480
- out.push(TAG_LIFETIME, 2, lo, hi)
481
- }
482
- if (options.color !== undefined) pushColor(out, options.color)
483
- if (options.size !== undefined) pushParam(out, 0, options.size)
484
- if (options.rotation !== undefined) pushParam(out, 1, options.rotation)
485
- if (options.opacity !== undefined) pushParam(out, 2, options.opacity)
486
- if (options.frame !== undefined) {
487
- pushParam(out, 3, options.frame)
488
- } else if (options.material === undefined && !options.mesh) {
489
- const [ cols, rows ] = _sheetColsRows(options.sheet)
490
- const frames = cols * rows
491
- if (frames > 1) {
492
- const start = options.startFrame ?? "random"
493
- const base = start === "random" ? { min: 0, max: frames - 1 } : start
494
- pushParam(out, 3, curve(base, "add").from(0).to(frames))
495
- }
496
- }
497
- if (options.noise !== undefined) {
498
- const n = options.noise
499
- out.push(TAG_NOISE, 3, n ? n.strength ?? 1 : 0, n ? n.frequency ?? 1 : 0, n ? n.speed ?? 1 : 0)
500
- }
501
- if (options.seed !== undefined) out.push(TAG_SEED, 1, options.seed)
502
-
503
- if (out.length > 0) _creator.setParticleSystemConfig(this.id, Float32Array.from(out))
504
- }
505
-
506
- /** Emit `count` particles immediately (a burst). */
507
- spawn(count: number): this { _creator.spawnParticles(this.id, count); return this }
508
-
509
- private _send(out: number[]): void {
510
- _creator.setParticleSystemConfig(this.id, Float32Array.from(out))
511
- }
512
-
513
- set rate(val: number) { this._send([ TAG_RATE, 1, val ]) }
514
-
515
- /** `'world'` leaves particles behind where they were born (trails). Switching resets live ones. */
516
- set space(val: "local" | "world") { this._send([ TAG_SPACE, 1, val === "world" ? 1 : 0 ]) }
517
- set inheritVelocity(val: number) { this._send([ TAG_INHERIT_VELOCITY, 1, val ]) }
518
- set rateOverDistance(val: number) { this._send([ TAG_RATE_DISTANCE, 1, val ]) }
519
- set order(val: number) { this._send([ TAG_ORDER, 1, val ]) }
520
-
521
- set velocityOverLife(v: VelocityOverLife) {
522
- const out: number[] = []
523
- pushVelocityLife(out, v)
524
- this._send(out)
525
- }
526
-
527
- set shape(shape: Shape) {
528
- const out: number[] = []
529
- pushShape(out, shape)
530
- this._send(out)
531
- }
532
-
533
- /** (mesh mode) Replace the particle mesh; live particles keep flying as the new shape. */
534
- set mesh(g: Geometry) {
535
- const out: number[] = [ TAG_RENDER_MODE, 1, 3 ]
536
- pushMesh(out, g)
537
- this._send(out)
538
- }
539
-
540
- set angularVelocity(v: Vec3Like | { min: Vec3Like, max: Vec3Like }) {
541
- const out: number[] = []
542
- pushAngularVelocity(out, v)
543
- this._send(out)
544
- }
545
-
546
- set ground(g: ParticleGround | null) {
547
- const out: number[] = []
548
- pushGround(out, g)
549
- this._send(out)
550
- }
551
-
552
- set startVelocity(val: Vec3Like | VelocityValue) {
553
- const out: number[] = []
554
- pushVelocity(out, val)
555
- this._send(out)
556
- }
557
-
558
- /** Positive pulls particles down — sugar for `acceleration = [0, -g, 0]`. */
559
- set gravity(val: number) { this._send([ TAG_ACCELERATION, 3, 0, -val, 0 ]) }
560
- set acceleration(a: Vec3Like) { this._send([ TAG_ACCELERATION, 3, cx(a), cy(a), cz(a) ]) }
561
- set drag(val: number) { this._send([ TAG_DRAG, 1, val ]) }
562
- set seed(val: number) { this._send([ TAG_SEED, 1, val ]) }
563
-
564
- set lifetime(val: Range<number>) {
565
- const [ lo, hi ] = lohi(val)
566
- this._send([ TAG_LIFETIME, 2, lo, hi ])
567
- }
568
-
569
- set size(val: ParticleValue) { this.custom[0] = val }
570
- set rotation(val: ParticleValue) { this.custom[1] = val }
571
- set opacity(val: ParticleValue) { this.custom[2] = val }
572
- set frame(val: ParticleValue) { this.custom[3] = val }
573
-
574
- set color(value: ParticleColor) {
575
- const out: number[] = []
576
- pushColor(out, value)
577
- this._send(out)
578
- }
579
-
580
- get material(): Material { return this._material }
581
- set material(material: Material) {
582
- this._material = material
583
- _creator.setMaterial(this.id, Material.idOf(material), 0)
584
- }
585
-
586
- set noise(noise: Noise | null) {
587
- this._send(noise
588
- ? [ TAG_NOISE, 3, noise.strength ?? 1, noise.frequency ?? 1, noise.speed ?? 1 ]
589
- : [ TAG_NOISE, 3, 0, 0, 0 ])
590
- }
591
- }
592
-
593
- // --- ribbon trail -------------------------------------------------------------------------------
594
-
595
- /** `Trail` options. The look options (map/sheet/emissive/blend/soft) come from
596
- * `ParticlesMaterialOptions`, same as `Particles`. */
597
- export type TrailOptions = Omit<ParticlesMaterialOptions, "render" | "stretch"> & {
598
- /** Seconds each trail point lives — the trail's length in time. Default 0.5. */
599
- time?: number
600
- /** Strip width in world units; a curve tapers it over each point's life (head → tail).
601
- * Default 0.1. */
602
- width?: ParticleValue
603
- /** Minimum emitter movement (world units) between recorded points. Default 0.05. */
604
- minDistance?: number
605
- /** Point capacity (default 128). */
606
- maxPoints?: number
607
- color?: ParticleColor
608
- /** Opacity along the trail. Defaults to fading the tail out (`curve().to(0)`). */
609
- opacity?: ParticleValue
610
- }
611
-
612
- /** A ribbon strip that follows this node through the world — sword swings, skid marks, missile
613
- * trails. Attach it to (or under) the moving node and just move; points are laid down per
614
- * `minDistance` of movement, live for `time` seconds, and the strip stays glued to the node at
615
- * its head. Width/opacity/color accept the same curves as `Particles`, evaluated over each
616
- * point's life — i.e. along the trail from head (fresh) to tail (dying). */
617
- export class Trail extends Node {
618
- private _material: Material
619
- private _rateDistance: number
620
-
621
- constructor(options: TrailOptions = {}) {
622
- super()
623
- this._material = Material.particles({ ...options, render: "ribbon" })
624
- _creator.createParticleSystem(this.id, Material.idOf(this._material), options.maxPoints ?? 128)
625
-
626
- const time = options.time ?? 0.5
627
- this._rateDistance = 1 / Math.max(options.minDistance ?? 0.05, 1e-3)
628
- const out: number[] = [
629
- TAG_RENDER_MODE, 1, 2,
630
- TAG_SPACE, 1, 1, // a trail is only meaningful in world space
631
- TAG_RATE, 1, 0, // points come from movement, never from time
632
- TAG_RATE_DISTANCE, 1, this._rateDistance,
633
- TAG_LIFETIME, 2, time, time,
634
- ]
635
- pushParam(out, 0, options.width ?? 0.1)
636
- pushParam(out, 2, options.opacity ?? curve().to(0))
637
- if (options.color !== undefined) pushColor(out, options.color)
638
- _creator.setParticleSystemConfig(this.id, Float32Array.from(out))
639
- }
640
-
641
- private _send(out: number[]): void {
642
- _creator.setParticleSystemConfig(this.id, Float32Array.from(out))
643
- }
644
-
645
- /** Trail length in seconds — how long each laid-down point lives. */
646
- set time(val: number) { this._send([ TAG_LIFETIME, 2, val, val ]) }
647
-
648
- set width(val: ParticleValue) {
649
- const out: number[] = []
650
- pushParam(out, 0, val)
651
- this._send(out)
652
- }
653
-
654
- set opacity(val: ParticleValue) {
655
- const out: number[] = []
656
- pushParam(out, 2, val)
657
- this._send(out)
658
- }
659
-
660
- set color(value: ParticleColor) {
661
- const out: number[] = []
662
- pushColor(out, value)
663
- this._send(out)
664
- }
665
-
666
- set minDistance(val: number) {
667
- this._rateDistance = 1 / Math.max(val, 1e-3)
668
- this._send([ TAG_RATE_DISTANCE, 1, this._rateDistance ])
669
- }
670
-
671
- /** Pause/resume laying down points — existing ones still age out, so the trail fades naturally
672
- * after e.g. a sword swing ends. */
673
- set emitting(val: boolean) { this._send([ TAG_RATE_DISTANCE, 1, val ? this._rateDistance : 0 ]) }
674
-
675
- get material(): Material { return this._material }
676
- }