@nayan-ui/engine 0.0.0-stage → 1.0.0-beta.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.
- package/LICENSE +21 -0
- package/NayanEngine.podspec +28 -0
- package/README.md +210 -2
- package/lib/module/assets/AssetBytesCache.js +76 -0
- package/lib/module/assets/AssetCache.js +19 -0
- package/lib/module/assets/AssetLoader.js +52 -0
- package/lib/module/assets/CacheGeneration.js +7 -0
- package/lib/module/assets/IndexedDbCache.web.js +149 -0
- package/lib/module/assets/ProceduralCache.js +222 -0
- package/lib/module/assets/font.js +136 -0
- package/lib/module/assets/loft.js +142 -0
- package/lib/module/assets/model.js +74 -0
- package/lib/module/assets/modelling.js +254 -0
- package/lib/module/assets/shape.js +926 -0
- package/lib/module/assets/source.js +58 -0
- package/lib/module/assets/source.web.js +22 -0
- package/lib/module/assets/storage.js +4 -0
- package/lib/module/assets/storage.shared.js +36 -0
- package/lib/module/assets/storage.web.js +37 -0
- package/lib/module/assets/surface.js +47 -0
- package/lib/module/assets/texture.js +66 -0
- package/lib/module/drawing/DrawingCanvas.js +38 -0
- package/lib/module/drawing/DrawingCanvas.web.js +206 -0
- package/lib/module/drawing/DrawingDocument.js +256 -0
- package/lib/module/drawing/types.js +4 -0
- package/lib/module/globals.d.ts +12 -0
- package/lib/module/index.js +36 -0
- package/lib/module/input/Joystick.js +84 -0
- package/lib/module/input/Joystick.web.js +93 -0
- package/lib/module/input/keyboard.js +250 -0
- package/lib/module/media/audio-worklet.js +34 -0
- package/lib/module/media/audio.js +94 -0
- package/lib/module/media/haptics.js +82 -0
- package/lib/module/media/output.web.js +66 -0
- package/lib/module/native/BrowserEngine.js +551 -0
- package/lib/module/native/EngineBridge.js +4 -0
- package/lib/module/native/NativeNayanEngine.js +7 -0
- package/lib/module/native/NativeNayanEngine.web.js +62 -0
- package/lib/module/native/prepare.js +7 -0
- package/lib/module/native/prepare.web.js +17 -0
- package/lib/module/native/spec/NativeNayanEngine.js +17 -0
- package/lib/module/package.json +1 -0
- package/lib/module/render/GameView.js +249 -0
- package/lib/module/render/camera.js +34 -0
- package/lib/module/render/host.js +39 -0
- package/lib/module/render/host.web.js +104 -0
- package/lib/module/render/meshes.js +159 -0
- package/lib/module/render/quality.js +112 -0
- package/lib/module/render/responder.js +4 -0
- package/lib/module/render/responder.web.js +97 -0
- package/lib/module/render/touch.js +185 -0
- package/lib/module/types.js +30 -0
- package/lib/module/world/Scoreboard.js +143 -0
- package/lib/module/world/TransformBatch.js +126 -0
- package/lib/module/world/World.js +690 -0
- package/lib/module/world/desc.js +186 -0
- package/lib/module/world/effects.js +451 -0
- package/lib/module/world/quat.js +86 -0
- package/lib/module/world/types.js +4 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/assets/AssetBytesCache.d.ts +7 -0
- package/lib/typescript/src/assets/AssetCache.d.ts +9 -0
- package/lib/typescript/src/assets/AssetLoader.d.ts +11 -0
- package/lib/typescript/src/assets/CacheGeneration.d.ts +3 -0
- package/lib/typescript/src/assets/IndexedDbCache.web.d.ts +20 -0
- package/lib/typescript/src/assets/ProceduralCache.d.ts +33 -0
- package/lib/typescript/src/assets/font.d.ts +53 -0
- package/lib/typescript/src/assets/loft.d.ts +31 -0
- package/lib/typescript/src/assets/model.d.ts +48 -0
- package/lib/typescript/src/assets/modelling.d.ts +157 -0
- package/lib/typescript/src/assets/shape.d.ts +203 -0
- package/lib/typescript/src/assets/source.d.ts +10 -0
- package/lib/typescript/src/assets/source.web.d.ts +8 -0
- package/lib/typescript/src/assets/storage.d.ts +2 -0
- package/lib/typescript/src/assets/storage.shared.d.ts +7 -0
- package/lib/typescript/src/assets/storage.web.d.ts +7 -0
- package/lib/typescript/src/assets/surface.d.ts +17 -0
- package/lib/typescript/src/assets/texture.d.ts +30 -0
- package/lib/typescript/src/drawing/DrawingCanvas.d.ts +5 -0
- package/lib/typescript/src/drawing/DrawingCanvas.web.d.ts +4 -0
- package/lib/typescript/src/drawing/DrawingDocument.d.ts +210 -0
- package/lib/typescript/src/drawing/types.d.ts +12 -0
- package/lib/typescript/src/index.d.ts +42 -0
- package/lib/typescript/src/input/Joystick.d.ts +20 -0
- package/lib/typescript/src/input/Joystick.web.d.ts +13 -0
- package/lib/typescript/src/input/keyboard.d.ts +17 -0
- package/lib/typescript/src/media/audio.d.ts +46 -0
- package/lib/typescript/src/media/haptics.d.ts +22 -0
- package/lib/typescript/src/media/output.web.d.ts +4 -0
- package/lib/typescript/src/native/BrowserEngine.d.ts +8 -0
- package/lib/typescript/src/native/EngineBridge.d.ts +22 -0
- package/lib/typescript/src/native/NativeNayanEngine.d.ts +5 -0
- package/lib/typescript/src/native/NativeNayanEngine.web.d.ts +10 -0
- package/lib/typescript/src/native/prepare.d.ts +3 -0
- package/lib/typescript/src/native/prepare.web.d.ts +2 -0
- package/lib/typescript/src/native/spec/NativeNayanEngine.d.ts +182 -0
- package/lib/typescript/src/render/GameView.d.ts +95 -0
- package/lib/typescript/src/render/camera.d.ts +6 -0
- package/lib/typescript/src/render/host.d.ts +29 -0
- package/lib/typescript/src/render/host.web.d.ts +16 -0
- package/lib/typescript/src/render/meshes.d.ts +45 -0
- package/lib/typescript/src/render/quality.d.ts +46 -0
- package/lib/typescript/src/render/responder.d.ts +2 -0
- package/lib/typescript/src/render/responder.web.d.ts +40 -0
- package/lib/typescript/src/render/touch.d.ts +26 -0
- package/lib/typescript/src/types.d.ts +72 -0
- package/lib/typescript/src/world/Scoreboard.d.ts +65 -0
- package/lib/typescript/src/world/TransformBatch.d.ts +39 -0
- package/lib/typescript/src/world/World.d.ts +199 -0
- package/lib/typescript/src/world/desc.d.ts +17 -0
- package/lib/typescript/src/world/effects.d.ts +156 -0
- package/lib/typescript/src/world/quat.d.ts +31 -0
- package/lib/typescript/src/world/types.d.ts +233 -0
- package/lib/web/audio-worklet.js +29 -0
- package/lib/web/engine_core_bg.wasm +0 -0
- package/lib/web/index.js +7974 -0
- package/lib/web/types/src/assets/AssetBytesCache.d.ts +6 -0
- package/lib/web/types/src/assets/AssetCache.d.ts +8 -0
- package/lib/web/types/src/assets/AssetLoader.d.ts +10 -0
- package/lib/web/types/src/assets/CacheGeneration.d.ts +2 -0
- package/lib/web/types/src/assets/IndexedDbCache.d.ts +19 -0
- package/lib/web/types/src/assets/ProceduralCache.d.ts +32 -0
- package/lib/web/types/src/assets/font.d.ts +52 -0
- package/lib/web/types/src/assets/loft.d.ts +30 -0
- package/lib/web/types/src/assets/model.d.ts +47 -0
- package/lib/web/types/src/assets/modelling.d.ts +156 -0
- package/lib/web/types/src/assets/shape.d.ts +202 -0
- package/lib/web/types/src/assets/source.d.ts +7 -0
- package/lib/web/types/src/assets/storage.d.ts +6 -0
- package/lib/web/types/src/assets/storage.shared.d.ts +6 -0
- package/lib/web/types/src/assets/surface.d.ts +16 -0
- package/lib/web/types/src/assets/texture.d.ts +29 -0
- package/lib/web/types/src/drawing/DrawingCanvas.d.ts +3 -0
- package/lib/web/types/src/drawing/DrawingDocument.d.ts +209 -0
- package/lib/web/types/src/drawing/types.d.ts +11 -0
- package/lib/web/types/src/index.d.ts +41 -0
- package/lib/web/types/src/input/Joystick.d.ts +12 -0
- package/lib/web/types/src/input/keyboard.d.ts +16 -0
- package/lib/web/types/src/media/audio.d.ts +45 -0
- package/lib/web/types/src/media/haptics.d.ts +21 -0
- package/lib/web/types/src/media/output.d.ts +3 -0
- package/lib/web/types/src/native/BrowserEngine.d.ts +7 -0
- package/lib/web/types/src/native/EngineBridge.d.ts +21 -0
- package/lib/web/types/src/native/NativeNayanEngine.d.ts +9 -0
- package/lib/web/types/src/native/prepare.d.ts +1 -0
- package/lib/web/types/src/native/spec/NativeNayanEngine.d.ts +181 -0
- package/lib/web/types/src/render/GameView.d.ts +94 -0
- package/lib/web/types/src/render/camera.d.ts +5 -0
- package/lib/web/types/src/render/host.d.ts +15 -0
- package/lib/web/types/src/render/meshes.d.ts +44 -0
- package/lib/web/types/src/render/quality.d.ts +45 -0
- package/lib/web/types/src/render/responder.d.ts +39 -0
- package/lib/web/types/src/render/touch.d.ts +25 -0
- package/lib/web/types/src/types.d.ts +71 -0
- package/lib/web/types/src/world/Scoreboard.d.ts +64 -0
- package/lib/web/types/src/world/TransformBatch.d.ts +38 -0
- package/lib/web/types/src/world/World.d.ts +198 -0
- package/lib/web/types/src/world/desc.d.ts +16 -0
- package/lib/web/types/src/world/effects.d.ts +155 -0
- package/lib/web/types/src/world/quat.d.ts +30 -0
- package/lib/web/types/src/world/types.d.ts +232 -0
- package/native/android/CMakeLists.txt +34 -0
- package/native/android/NayanEngineSpec.h +15 -0
- package/native/android/build.gradle +26 -0
- package/native/android/codegen/CMakeLists.txt +3 -0
- package/native/android/proguard-rules.pro +2 -0
- package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceView.java +234 -0
- package/native/android/src/main/java/com/nayanui/engine/EngineSurfaceViewManager.java +45 -0
- package/native/android/src/main/java/com/nayanui/engine/NayanEnginePackage.java +24 -0
- package/native/core/build/EngineCore.xcframework/Info.plist +43 -0
- package/native/core/build/EngineCore.xcframework/ios-arm64/libengine_core.a +0 -0
- package/native/core/build/EngineCore.xcframework/ios-arm64-simulator/libengine_core.a +0 -0
- package/native/core/build/android/arm64-v8a/libengine_core.a +0 -0
- package/native/core/build/android/armeabi-v7a/libengine_core.a +0 -0
- package/native/core/build/android/x86/libengine_core.a +0 -0
- package/native/core/build/android/x86_64/libengine_core.a +0 -0
- package/native/core/include/drawing-licenses.txt +58 -0
- package/native/core/include/engine_core.h +322 -0
- package/native/cpp/NayanEngineModule.cpp +827 -0
- package/native/cpp/NayanEngineModule.h +144 -0
- package/native/cpp/generated/NayanEngineSpecJSI.cpp +2 -0
- package/native/cpp/generated/NayanEngineSpecJSI.h +921 -0
- package/native/ios/NayanEngineProvider.h +5 -0
- package/native/ios/NayanEngineProvider.mm +21 -0
- package/native/ios/NayanEngineSurface.mm +223 -0
- package/native/scripts/check-package.mjs +39 -0
- package/package.json +162 -4
- package/react-native.config.js +18 -0
- package/src/native/spec/NativeNayanEngine.ts +226 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Encodes entity options into the flat layout the Rust core decodes (native/core/include/engine_core.h,
|
|
4
|
+
// native/core/src/world/desc.rs). One reusable Float64Array per world: spawning or changing an entity is
|
|
5
|
+
// a single native call, and doubles keep entity ids and layer bits exact.
|
|
6
|
+
|
|
7
|
+
export const DESC_LEN = 64;
|
|
8
|
+
const FLAG = {
|
|
9
|
+
MESH: 1 << 0,
|
|
10
|
+
POSITION: 1 << 1,
|
|
11
|
+
ROTATION: 1 << 2,
|
|
12
|
+
SCALE: 1 << 3,
|
|
13
|
+
COLOR: 1 << 4,
|
|
14
|
+
VELOCITY: 1 << 5,
|
|
15
|
+
GROUND_VELOCITY: 1 << 6,
|
|
16
|
+
SPIN: 1 << 7,
|
|
17
|
+
BOB: 1 << 8,
|
|
18
|
+
FOLLOW: 1 << 9,
|
|
19
|
+
LIFETIME: 1 << 10,
|
|
20
|
+
PARENT: 1 << 11,
|
|
21
|
+
PHYSICS: 1 << 12,
|
|
22
|
+
IMPACT: 1 << 13,
|
|
23
|
+
ACCELERATION: 1 << 14,
|
|
24
|
+
PICKABLE: 1 << 15,
|
|
25
|
+
REGION: 1 << 16
|
|
26
|
+
};
|
|
27
|
+
const SLOT = {
|
|
28
|
+
FLAGS: 0,
|
|
29
|
+
MESH: 1,
|
|
30
|
+
POSITION: 2,
|
|
31
|
+
ROTATION: 5,
|
|
32
|
+
SCALE: 9,
|
|
33
|
+
COLOR: 12,
|
|
34
|
+
VELOCITY: 16,
|
|
35
|
+
GROUND_VELOCITY: 19,
|
|
36
|
+
SPIN: 21,
|
|
37
|
+
BOB: 24,
|
|
38
|
+
FOLLOW: 29,
|
|
39
|
+
LIFETIME: 31,
|
|
40
|
+
PARENT: 32,
|
|
41
|
+
PHYSICS: 33,
|
|
42
|
+
IMPACT: 50,
|
|
43
|
+
ACCELERATION: 56,
|
|
44
|
+
PICKABLE: 59,
|
|
45
|
+
REGION: 60
|
|
46
|
+
};
|
|
47
|
+
const BODY_KIND = {
|
|
48
|
+
dynamic: 1,
|
|
49
|
+
kinematic: 2,
|
|
50
|
+
fixed: 3
|
|
51
|
+
};
|
|
52
|
+
const SHAPE = {
|
|
53
|
+
ball: 0,
|
|
54
|
+
box: 1,
|
|
55
|
+
fromMesh: 2,
|
|
56
|
+
cylinder: 3,
|
|
57
|
+
capsule: 4,
|
|
58
|
+
cone: 5
|
|
59
|
+
};
|
|
60
|
+
function encodePhysics(d, physics) {
|
|
61
|
+
const p = SLOT.PHYSICS;
|
|
62
|
+
const shape = physics.shape !== undefined ? SHAPE[physics.shape] : physics.size !== undefined ? SHAPE.box : physics.radius !== undefined ? SHAPE.ball : SHAPE.fromMesh;
|
|
63
|
+
// Sizes <= 0 tell the core to size the collider from the entity's mesh and scale.
|
|
64
|
+
const [sx, sy, sz] = shape === SHAPE.box ? physics.size ? [physics.size[0] / 2, physics.size[1] / 2, physics.size[2] / 2] : [0, 0, 0] : [physics.radius ?? 0, (physics.height ?? 0) / 2, 0];
|
|
65
|
+
d[p] = BODY_KIND[physics.type];
|
|
66
|
+
d[p + 1] = shape;
|
|
67
|
+
d[p + 2] = sx;
|
|
68
|
+
d[p + 3] = sy;
|
|
69
|
+
d[p + 4] = sz;
|
|
70
|
+
d[p + 5] = physics.layer ?? 1;
|
|
71
|
+
d[p + 6] = physics.mask ?? -1; // all layers
|
|
72
|
+
d[p + 7] = physics.sensor ? 1 : 0;
|
|
73
|
+
d[p + 8] = physics.friction ?? 0.5;
|
|
74
|
+
d[p + 9] = physics.bounce ?? 0;
|
|
75
|
+
d[p + 10] = physics.density ?? 1;
|
|
76
|
+
d[p + 11] = physics.drag ?? 0;
|
|
77
|
+
d[p + 12] = physics.angularDrag ?? 0.05;
|
|
78
|
+
d[p + 13] = physics.gravityScale ?? 1;
|
|
79
|
+
d[p + 14] = physics.upright ? 1 : 0;
|
|
80
|
+
d[p + 15] = physics.ccd ? 1 : 0;
|
|
81
|
+
d[p + 16] = physics.planar ? 1 : 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Writes `options` into `d`, with the mesh id already resolved. Only the fields present are flagged. */
|
|
85
|
+
export function encode(d, o, mesh) {
|
|
86
|
+
let flags = 0;
|
|
87
|
+
if (mesh !== undefined) {
|
|
88
|
+
flags |= FLAG.MESH;
|
|
89
|
+
d[SLOT.MESH] = mesh;
|
|
90
|
+
}
|
|
91
|
+
if (o.position) {
|
|
92
|
+
flags |= FLAG.POSITION;
|
|
93
|
+
d.set(o.position, SLOT.POSITION);
|
|
94
|
+
}
|
|
95
|
+
if (o.rotation) {
|
|
96
|
+
flags |= FLAG.ROTATION;
|
|
97
|
+
d.set(o.rotation, SLOT.ROTATION);
|
|
98
|
+
}
|
|
99
|
+
if (o.scale !== undefined) {
|
|
100
|
+
flags |= FLAG.SCALE;
|
|
101
|
+
if (typeof o.scale === 'number') d.fill(o.scale, SLOT.SCALE, SLOT.SCALE + 3);else d.set(o.scale, SLOT.SCALE);
|
|
102
|
+
}
|
|
103
|
+
if (o.color) {
|
|
104
|
+
flags |= FLAG.COLOR;
|
|
105
|
+
d[SLOT.COLOR] = o.color[0];
|
|
106
|
+
d[SLOT.COLOR + 1] = o.color[1];
|
|
107
|
+
d[SLOT.COLOR + 2] = o.color[2];
|
|
108
|
+
d[SLOT.COLOR + 3] = o.color[3] ?? 1;
|
|
109
|
+
}
|
|
110
|
+
if (o.velocity) {
|
|
111
|
+
flags |= FLAG.VELOCITY;
|
|
112
|
+
d.set(o.velocity, SLOT.VELOCITY);
|
|
113
|
+
}
|
|
114
|
+
if (o.groundVelocity) {
|
|
115
|
+
flags |= FLAG.GROUND_VELOCITY;
|
|
116
|
+
d.set(o.groundVelocity, SLOT.GROUND_VELOCITY);
|
|
117
|
+
}
|
|
118
|
+
if (o.spin) {
|
|
119
|
+
flags |= FLAG.SPIN;
|
|
120
|
+
d.set(o.spin, SLOT.SPIN);
|
|
121
|
+
}
|
|
122
|
+
if (o.bob !== undefined) {
|
|
123
|
+
flags |= FLAG.BOB;
|
|
124
|
+
d.set(o.bob?.amplitude ?? [0, 0, 0], SLOT.BOB);
|
|
125
|
+
d[SLOT.BOB + 3] = o.bob?.speed ?? 0;
|
|
126
|
+
d[SLOT.BOB + 4] = o.bob?.phase ?? 0;
|
|
127
|
+
}
|
|
128
|
+
if (o.follow !== undefined) {
|
|
129
|
+
flags |= FLAG.FOLLOW;
|
|
130
|
+
d[SLOT.FOLLOW] = o.follow?.target ?? -1;
|
|
131
|
+
d[SLOT.FOLLOW + 1] = o.follow?.speed ?? 0;
|
|
132
|
+
}
|
|
133
|
+
if (o.lifetime !== undefined) {
|
|
134
|
+
flags |= FLAG.LIFETIME;
|
|
135
|
+
d[SLOT.LIFETIME] = o.lifetime ?? 0;
|
|
136
|
+
}
|
|
137
|
+
if (o.parent !== undefined) {
|
|
138
|
+
flags |= FLAG.PARENT;
|
|
139
|
+
d[SLOT.PARENT] = o.parent ?? -1;
|
|
140
|
+
}
|
|
141
|
+
if (o.physics !== undefined) {
|
|
142
|
+
flags |= FLAG.PHYSICS;
|
|
143
|
+
if (o.physics === null) d[SLOT.PHYSICS] = 0;else encodePhysics(d, typeof o.physics === 'string' ? {
|
|
144
|
+
type: o.physics
|
|
145
|
+
} : o.physics);
|
|
146
|
+
}
|
|
147
|
+
if (o.impact !== undefined) {
|
|
148
|
+
flags |= FLAG.IMPACT;
|
|
149
|
+
const i = SLOT.IMPACT;
|
|
150
|
+
d[i] = o.impact ? 1 : 0;
|
|
151
|
+
d[i + 1] = o.impact?.sound ?? -1;
|
|
152
|
+
d[i + 2] = o.impact?.minSpeed ?? 1;
|
|
153
|
+
d[i + 3] = o.impact?.maxSpeed ?? 10;
|
|
154
|
+
d[i + 4] = o.impact?.volume ?? 1;
|
|
155
|
+
d[i + 5] = o.impact?.haptic ?? 0;
|
|
156
|
+
}
|
|
157
|
+
if (o.acceleration) {
|
|
158
|
+
flags |= FLAG.ACCELERATION;
|
|
159
|
+
d.set(o.acceleration, SLOT.ACCELERATION);
|
|
160
|
+
}
|
|
161
|
+
if (o.pickable !== undefined) {
|
|
162
|
+
flags |= FLAG.PICKABLE;
|
|
163
|
+
d[SLOT.PICKABLE] = o.pickable ? 1 : 0;
|
|
164
|
+
}
|
|
165
|
+
if (o.textureRegion) {
|
|
166
|
+
flags |= FLAG.REGION;
|
|
167
|
+
d.set(o.textureRegion, SLOT.REGION);
|
|
168
|
+
}
|
|
169
|
+
d[SLOT.FLAGS] = flags;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── Animations and bursts (layouts in native/core/include/engine_core.h) ─────────
|
|
173
|
+
|
|
174
|
+
export const ANIM_HEADER = 12;
|
|
175
|
+
export const ANIM_KEY_LEN = 23;
|
|
176
|
+
export const BURST_LEN = 36;
|
|
177
|
+
export const EASING = {
|
|
178
|
+
linear: 0,
|
|
179
|
+
easeIn: 1,
|
|
180
|
+
easeOut: 2,
|
|
181
|
+
easeInOut: 3,
|
|
182
|
+
back: 4,
|
|
183
|
+
bounce: 5,
|
|
184
|
+
elastic: 6
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=desc.js.map
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Ready-made particle effects and the cleared-piece pop, built from `world.burst` and `world.animate`.
|
|
4
|
+
//
|
|
5
|
+
// A single burst gives every particle 50..100% of one top speed, so on its own it flies out as a
|
|
6
|
+
// hollow shell of same-sized bits that pop out of existence. These effects mix a few bursts instead:
|
|
7
|
+
// slow big pieces that fill the middle and fast small ones at the edge, an additive glow that flashes
|
|
8
|
+
// at the source (visible on dark scenes, harmless on light ones), and soft puffs that linger and fade.
|
|
9
|
+
// Every particle takes a world slot, out of the capacity shared with the game's own entities, so the
|
|
10
|
+
// counts stay close to what was passed in. Effects run entirely in the Rust core after one call each.
|
|
11
|
+
|
|
12
|
+
/** Shared options of the layered effects (confetti, sparkle, shatter, splash, sparks, explosion). */
|
|
13
|
+
|
|
14
|
+
/** A soft cloud of puffs that swell and fade: landing dust, kicked-up sand, sawdust, steam, muzzle smoke. */
|
|
15
|
+
|
|
16
|
+
/** One glow that swells and fades at a point: a hit, a strike, a muzzle flash. */
|
|
17
|
+
|
|
18
|
+
/** The cleared-piece pop: swell, flash white, settle, shrink away. */
|
|
19
|
+
|
|
20
|
+
const rgba = c => [c[0], c[1], c[2], c[3] ?? 1];
|
|
21
|
+
const list = colors => typeof colors[0] === 'number' ? [rgba(colors)] : colors.map(rgba);
|
|
22
|
+
const part = (count, share) => Math.max(1, Math.round(count * share));
|
|
23
|
+
const fade = c => [c[0], c[1], c[2], 0];
|
|
24
|
+
|
|
25
|
+
/** The colors' average. */
|
|
26
|
+
function mean(colors) {
|
|
27
|
+
const sum = [0, 0, 0, 0];
|
|
28
|
+
for (const c of colors) for (let i = 0; i < 4; i++) sum[i] += c[i];
|
|
29
|
+
return sum.map(v => v / colors.length);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The same hue, self-lit: its brightest channel lifted to `peak` (above 1 glows, through light, shadow and fog). */
|
|
33
|
+
function hot(c, peak = 2.2, alpha = c[3] ?? 1) {
|
|
34
|
+
const k = peak / Math.max(c[0], c[1], c[2], 0.25);
|
|
35
|
+
return [c[0] * k, c[1] * k, c[2] * k, alpha];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Part way (`t`, 0..1) to white, with `alpha`. */
|
|
39
|
+
function pale(c, t, alpha = c[3] ?? 1) {
|
|
40
|
+
return [c[0] + (1 - c[0]) * t, c[1] + (1 - c[1]) * t, c[2] + (1 - c[2]) * t, alpha];
|
|
41
|
+
}
|
|
42
|
+
function aim(e) {
|
|
43
|
+
return {
|
|
44
|
+
position: e.position,
|
|
45
|
+
direction: e.direction ?? [0, 1, 0],
|
|
46
|
+
spread: e.spread ?? Math.PI
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function flash(world, o) {
|
|
50
|
+
return world.burst({
|
|
51
|
+
position: o.position,
|
|
52
|
+
count: o.count ?? 1,
|
|
53
|
+
mesh: o.mesh ?? 'flame',
|
|
54
|
+
size: o.size,
|
|
55
|
+
speed: o.speed ?? 0,
|
|
56
|
+
lifetime: o.lifetime ?? 0.22,
|
|
57
|
+
gravity: 0,
|
|
58
|
+
color: o.color,
|
|
59
|
+
endSize: o.endSize ?? 2.2,
|
|
60
|
+
endColor: o.endColor ?? fade(o.color)
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A glow that swells and fades at the source, in the effect's own hue. */
|
|
65
|
+
function glowFlash(world, e, colors, size) {
|
|
66
|
+
flash(world, {
|
|
67
|
+
position: e.position,
|
|
68
|
+
color: hot(mean(colors), 2, 0.85),
|
|
69
|
+
size
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function dust(world, o) {
|
|
73
|
+
const single = typeof o.color[0] === 'number';
|
|
74
|
+
return world.burst({
|
|
75
|
+
position: o.position,
|
|
76
|
+
count: o.count,
|
|
77
|
+
mesh: o.mesh ?? 'puff',
|
|
78
|
+
size: o.size,
|
|
79
|
+
speed: o.speed,
|
|
80
|
+
lifetime: o.lifetime,
|
|
81
|
+
gravity: o.gravity ?? 0,
|
|
82
|
+
direction: o.direction,
|
|
83
|
+
spread: o.spread,
|
|
84
|
+
color: o.color,
|
|
85
|
+
endSize: o.endSize ?? 2,
|
|
86
|
+
endColor: o.endColor ?? (single ? fade(o.color) : undefined)
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Celebration confetti: tumbling paper in two layers, a few glinting sparkles, and a pop of light. */
|
|
91
|
+
function confetti(world, e) {
|
|
92
|
+
const colors = list(e.colors);
|
|
93
|
+
const g = (e.gravity ?? 0) * 0.65; // paper drifts down rather than dropping
|
|
94
|
+
const base = {
|
|
95
|
+
...aim(e),
|
|
96
|
+
mesh: 'cube',
|
|
97
|
+
gravity: g,
|
|
98
|
+
color: colors,
|
|
99
|
+
endSize: 0.3
|
|
100
|
+
};
|
|
101
|
+
world.burst({
|
|
102
|
+
...base,
|
|
103
|
+
count: part(e.count, 0.55),
|
|
104
|
+
size: e.size,
|
|
105
|
+
speed: e.speed,
|
|
106
|
+
lifetime: e.lifetime * 1.15
|
|
107
|
+
});
|
|
108
|
+
world.burst({
|
|
109
|
+
...base,
|
|
110
|
+
count: part(e.count, 0.3),
|
|
111
|
+
size: e.size * 0.7,
|
|
112
|
+
speed: e.speed * 0.5,
|
|
113
|
+
lifetime: e.lifetime
|
|
114
|
+
});
|
|
115
|
+
const glint = colors.slice(0, 4).map(c => hot(pale(c, 0.5, 1), 2.4));
|
|
116
|
+
world.burst({
|
|
117
|
+
...aim(e),
|
|
118
|
+
count: part(e.count, 0.15),
|
|
119
|
+
mesh: 'flame',
|
|
120
|
+
size: e.size * 1.1,
|
|
121
|
+
speed: e.speed * 0.8,
|
|
122
|
+
lifetime: e.lifetime * 0.8,
|
|
123
|
+
gravity: g * 0.5,
|
|
124
|
+
color: glint,
|
|
125
|
+
endSize: 0
|
|
126
|
+
});
|
|
127
|
+
if (e.flash !== false) glowFlash(world, e, [[1, 0.95, 0.8, 1]], e.size * 7);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Glitter for a reward: solid bright beads (seen on any background) with additive glints around them. */
|
|
131
|
+
function sparkle(world, e) {
|
|
132
|
+
const colors = list(e.colors);
|
|
133
|
+
const bright = colors.map(c => hot(c, 1.4));
|
|
134
|
+
const base = {
|
|
135
|
+
...aim(e),
|
|
136
|
+
gravity: e.gravity ?? 0,
|
|
137
|
+
endSize: 0
|
|
138
|
+
};
|
|
139
|
+
world.burst({
|
|
140
|
+
...base,
|
|
141
|
+
count: part(e.count, 0.35),
|
|
142
|
+
mesh: 'sphere',
|
|
143
|
+
size: e.size,
|
|
144
|
+
speed: e.speed,
|
|
145
|
+
lifetime: e.lifetime,
|
|
146
|
+
color: bright
|
|
147
|
+
});
|
|
148
|
+
world.burst({
|
|
149
|
+
...base,
|
|
150
|
+
count: part(e.count, 0.25),
|
|
151
|
+
mesh: 'sphere',
|
|
152
|
+
size: e.size * 1.3,
|
|
153
|
+
speed: e.speed * 0.45,
|
|
154
|
+
lifetime: e.lifetime * 0.85,
|
|
155
|
+
color: bright
|
|
156
|
+
});
|
|
157
|
+
world.burst({
|
|
158
|
+
...base,
|
|
159
|
+
count: part(e.count, 0.4),
|
|
160
|
+
mesh: 'flame',
|
|
161
|
+
size: e.size * 1.6,
|
|
162
|
+
speed: e.speed * 0.8,
|
|
163
|
+
lifetime: e.lifetime * 0.9,
|
|
164
|
+
color: colors.map(c => hot(pale(c, 0.35, 1), 2.4))
|
|
165
|
+
});
|
|
166
|
+
if (e.flash !== false) glowFlash(world, e, colors, e.size * 6);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Something breaking apart: big slow chunks, small fast crumbs, a glow at the break and a puff of
|
|
171
|
+
* its dust. `mesh` is the chunks' shape.
|
|
172
|
+
*/
|
|
173
|
+
function shatter(world, e) {
|
|
174
|
+
const colors = list(e.colors);
|
|
175
|
+
const mesh = e.mesh ?? 'cube';
|
|
176
|
+
const base = {
|
|
177
|
+
...aim(e),
|
|
178
|
+
gravity: e.gravity ?? 0,
|
|
179
|
+
color: colors
|
|
180
|
+
};
|
|
181
|
+
world.burst({
|
|
182
|
+
...base,
|
|
183
|
+
count: part(e.count, 0.55),
|
|
184
|
+
mesh,
|
|
185
|
+
size: e.size * 1.1,
|
|
186
|
+
speed: e.speed * 0.65,
|
|
187
|
+
lifetime: e.lifetime,
|
|
188
|
+
endSize: 0.2
|
|
189
|
+
});
|
|
190
|
+
world.burst({
|
|
191
|
+
...base,
|
|
192
|
+
count: part(e.count, 0.6),
|
|
193
|
+
mesh,
|
|
194
|
+
size: e.size * 0.5,
|
|
195
|
+
speed: e.speed * 1.1,
|
|
196
|
+
lifetime: e.lifetime * 0.85,
|
|
197
|
+
endSize: 0
|
|
198
|
+
});
|
|
199
|
+
if (e.flash !== false) glowFlash(world, e, colors, e.size * 3);
|
|
200
|
+
if (e.dust !== false) {
|
|
201
|
+
const c = pale(mean(colors), 0.45, 0.5);
|
|
202
|
+
world.burst({
|
|
203
|
+
...aim(e),
|
|
204
|
+
count: 2,
|
|
205
|
+
mesh: 'puff',
|
|
206
|
+
size: e.size * 2,
|
|
207
|
+
speed: e.speed * 0.2,
|
|
208
|
+
lifetime: e.lifetime * 0.9,
|
|
209
|
+
gravity: 0,
|
|
210
|
+
color: c,
|
|
211
|
+
endSize: 2.4,
|
|
212
|
+
endColor: fade(c)
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Liquid: droplets in two sizes thrown out of a fine mist of the same color. */
|
|
218
|
+
function splash(world, e) {
|
|
219
|
+
const colors = list(e.colors);
|
|
220
|
+
const base = {
|
|
221
|
+
...aim(e),
|
|
222
|
+
mesh: 'sphere',
|
|
223
|
+
gravity: e.gravity ?? 0,
|
|
224
|
+
color: colors
|
|
225
|
+
};
|
|
226
|
+
world.burst({
|
|
227
|
+
...base,
|
|
228
|
+
count: part(e.count, 0.6),
|
|
229
|
+
size: e.size,
|
|
230
|
+
speed: e.speed,
|
|
231
|
+
lifetime: e.lifetime,
|
|
232
|
+
endSize: 0.3
|
|
233
|
+
});
|
|
234
|
+
world.burst({
|
|
235
|
+
...base,
|
|
236
|
+
count: part(e.count, 0.4),
|
|
237
|
+
size: e.size * 1.5,
|
|
238
|
+
speed: e.speed * 0.45,
|
|
239
|
+
lifetime: e.lifetime * 0.8,
|
|
240
|
+
endSize: 0.2
|
|
241
|
+
});
|
|
242
|
+
const mist = pale(mean(colors), 0.2, 0.55);
|
|
243
|
+
world.burst({
|
|
244
|
+
...aim(e),
|
|
245
|
+
count: 3,
|
|
246
|
+
mesh: 'puff',
|
|
247
|
+
size: e.size * 3,
|
|
248
|
+
speed: e.speed * 0.25,
|
|
249
|
+
lifetime: e.lifetime * 0.7,
|
|
250
|
+
gravity: 0,
|
|
251
|
+
color: mist,
|
|
252
|
+
endSize: 2.2,
|
|
253
|
+
endColor: fade(mist)
|
|
254
|
+
});
|
|
255
|
+
if (e.flash !== false) glowFlash(world, e, colors, e.size * 4);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Hot metal sparks: additive streaks that burn out, with a few solid beads and a flash where they strike. */
|
|
259
|
+
function sparks(world, e) {
|
|
260
|
+
const colors = list(e.colors).map(c => hot(c, 2.6));
|
|
261
|
+
const base = {
|
|
262
|
+
...aim(e),
|
|
263
|
+
gravity: e.gravity ?? 0,
|
|
264
|
+
color: colors,
|
|
265
|
+
endSize: 0
|
|
266
|
+
};
|
|
267
|
+
world.burst({
|
|
268
|
+
...base,
|
|
269
|
+
count: part(e.count, 0.7),
|
|
270
|
+
mesh: 'flame',
|
|
271
|
+
size: e.size * 1.6,
|
|
272
|
+
speed: e.speed,
|
|
273
|
+
lifetime: e.lifetime
|
|
274
|
+
});
|
|
275
|
+
world.burst({
|
|
276
|
+
...base,
|
|
277
|
+
count: part(e.count, 0.3),
|
|
278
|
+
mesh: 'sphere',
|
|
279
|
+
size: e.size * 0.6,
|
|
280
|
+
speed: e.speed * 1.1,
|
|
281
|
+
lifetime: e.lifetime * 0.8
|
|
282
|
+
});
|
|
283
|
+
if (e.flash !== false) glowFlash(world, e, colors, e.size * 5);
|
|
284
|
+
}
|
|
285
|
+
const FIRE = [[2.6, 2.4, 1.8, 1], [2.4, 1.5, 0.45, 0.95], [2.2, 0.8, 0.2, 0.85]];
|
|
286
|
+
const EMBERS = [[2.8, 2.2, 1, 1], [2.6, 1.3, 0.3, 1]];
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* A fiery blast: a white-hot flash, a fireball that cools into smoke, embers thrown out and, given
|
|
290
|
+
* `debris` colors, chunks of whatever blew up. `size` is the fireball's puff size; `colors` its fire.
|
|
291
|
+
* The fire and smoke swell every way; `direction`, `spread` and `gravity` aim the embers and debris.
|
|
292
|
+
*/
|
|
293
|
+
function explosion(world, e) {
|
|
294
|
+
const fire = list(e.colors ?? FIRE);
|
|
295
|
+
const smoke = rgba(e.smoke ?? [0.28, 0.25, 0.24, 0.6]);
|
|
296
|
+
const g = e.gravity ?? 0;
|
|
297
|
+
const around = {
|
|
298
|
+
position: e.position,
|
|
299
|
+
spread: Math.PI,
|
|
300
|
+
gravity: 0
|
|
301
|
+
};
|
|
302
|
+
if (e.flash !== false) {
|
|
303
|
+
flash(world, {
|
|
304
|
+
position: e.position,
|
|
305
|
+
color: [3, 2.7, 2.1, 1],
|
|
306
|
+
size: e.size * 1.6,
|
|
307
|
+
lifetime: 0.2,
|
|
308
|
+
endSize: 2,
|
|
309
|
+
endColor: [2.4, 0.9, 0.2, 0]
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
world.burst({
|
|
313
|
+
...around,
|
|
314
|
+
count: part(e.count, 0.5),
|
|
315
|
+
mesh: 'puff',
|
|
316
|
+
size: e.size,
|
|
317
|
+
speed: e.speed,
|
|
318
|
+
lifetime: e.lifetime,
|
|
319
|
+
color: fire,
|
|
320
|
+
endSize: 2.2,
|
|
321
|
+
endColor: [smoke[0], smoke[1], smoke[2], 0]
|
|
322
|
+
});
|
|
323
|
+
world.burst({
|
|
324
|
+
...around,
|
|
325
|
+
count: part(e.count, 0.2),
|
|
326
|
+
mesh: 'flame',
|
|
327
|
+
size: e.size * 0.8,
|
|
328
|
+
speed: e.speed * 0.5,
|
|
329
|
+
lifetime: e.lifetime * 0.6,
|
|
330
|
+
color: fire,
|
|
331
|
+
endSize: 1.8,
|
|
332
|
+
endColor: [2, 0.5, 0.1, 0]
|
|
333
|
+
});
|
|
334
|
+
world.burst({
|
|
335
|
+
...around,
|
|
336
|
+
count: part(e.count, 0.25),
|
|
337
|
+
mesh: 'puff',
|
|
338
|
+
size: e.size * 0.9,
|
|
339
|
+
speed: e.speed * 0.6,
|
|
340
|
+
lifetime: e.lifetime * 1.7,
|
|
341
|
+
color: smoke,
|
|
342
|
+
endSize: 2.8,
|
|
343
|
+
endColor: fade(smoke)
|
|
344
|
+
});
|
|
345
|
+
world.burst({
|
|
346
|
+
...aim(e),
|
|
347
|
+
count: part(e.count, 0.4),
|
|
348
|
+
mesh: 'flame',
|
|
349
|
+
size: e.size * 0.12,
|
|
350
|
+
speed: e.speed * 2.4,
|
|
351
|
+
lifetime: e.lifetime * 0.9,
|
|
352
|
+
gravity: g,
|
|
353
|
+
color: EMBERS,
|
|
354
|
+
endSize: 0
|
|
355
|
+
});
|
|
356
|
+
if (e.debris) {
|
|
357
|
+
world.burst({
|
|
358
|
+
...aim(e),
|
|
359
|
+
count: part(e.count, 0.5),
|
|
360
|
+
mesh: 'cube',
|
|
361
|
+
size: e.size * 0.16,
|
|
362
|
+
speed: e.speed * 1.8,
|
|
363
|
+
lifetime: e.lifetime * 1.2,
|
|
364
|
+
gravity: g,
|
|
365
|
+
color: e.debris,
|
|
366
|
+
endSize: 0.3
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const WHITE_HOT = [2, 2, 2, 1]; // above 1 glows
|
|
371
|
+
const WHITE = [1, 1, 1, 1];
|
|
372
|
+
const POP_TIME = 0.5;
|
|
373
|
+
const times = (scale, k) => typeof scale === 'number' ? scale * k : [scale[0] * k, scale[1] * k, scale[2] * k];
|
|
374
|
+
|
|
375
|
+
/** The piece swells, flashes white, settles and shrinks away. Resolves when it's gone (despawning it is the caller's). */
|
|
376
|
+
function pop(world, entity, o) {
|
|
377
|
+
const delay = o.delay ?? 0;
|
|
378
|
+
const duration = o.duration ?? POP_TIME;
|
|
379
|
+
const flashFor = {
|
|
380
|
+
duration: duration * 0.3,
|
|
381
|
+
delay
|
|
382
|
+
};
|
|
383
|
+
world.stopAnimation(entity); // whatever it was doing (landing, falling, wobbling) gives way
|
|
384
|
+
if (o.color) world.animate(entity, [{
|
|
385
|
+
color: WHITE_HOT,
|
|
386
|
+
at: 0.33
|
|
387
|
+
}, {
|
|
388
|
+
color: o.color
|
|
389
|
+
}], flashFor);
|
|
390
|
+
if (o.flash?.length) world.animate(o.flash, [{
|
|
391
|
+
color: WHITE_HOT,
|
|
392
|
+
at: 0.33
|
|
393
|
+
}, {
|
|
394
|
+
color: WHITE
|
|
395
|
+
}], flashFor);
|
|
396
|
+
const s = o.scale;
|
|
397
|
+
return world.animate(entity, [{
|
|
398
|
+
scale: times(s, 1.35),
|
|
399
|
+
at: 0.15
|
|
400
|
+
}, {
|
|
401
|
+
scale: times(s, 0.9),
|
|
402
|
+
at: 0.25
|
|
403
|
+
}, {
|
|
404
|
+
scale: times(s, 1),
|
|
405
|
+
at: 0.35
|
|
406
|
+
}, {
|
|
407
|
+
scale: times(s, 0)
|
|
408
|
+
}], {
|
|
409
|
+
duration,
|
|
410
|
+
delay
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Ready-made effects. Each is one call; particles then live, fall, shrink and recolor in Rust.
|
|
416
|
+
*
|
|
417
|
+
* ```ts
|
|
418
|
+
* effects.confetti(world, { position, colors: [RED, GOLD], size: 0.12, speed: 6, count: 40, lifetime: 1.2, gravity: -6 });
|
|
419
|
+
* effects.dust(world, { position: feet, color: [0.8, 0.75, 0.65, 0.6], size: 0.3, speed: 1.5, count: 6, lifetime: 0.5 });
|
|
420
|
+
* effects.flash(world, { position: hit, color: effects.hot(ORANGE), size: 0.8 });
|
|
421
|
+
* await effects.pop(world, candy, { scale: 1, color: RED });
|
|
422
|
+
* world.despawn(candy);
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
export const effects = {
|
|
426
|
+
/** Celebration: tumbling paper, glints and a pop of light (wins, level-ups). */
|
|
427
|
+
confetti,
|
|
428
|
+
/** Reward glitter: bright beads and glints (pickups, perfect moves). */
|
|
429
|
+
sparkle,
|
|
430
|
+
/** Breaking apart: chunks, crumbs, a glow and dust (bricks, crates, glass). `mesh` shapes the chunks. */
|
|
431
|
+
shatter,
|
|
432
|
+
/** Liquid: droplets and mist (water, juice, paint). */
|
|
433
|
+
splash,
|
|
434
|
+
/** Hot metal: additive streaks and beads (scrapes, strikes, electricity). */
|
|
435
|
+
sparks,
|
|
436
|
+
/** Fiery blast: flash, fireball cooling to smoke, embers and optional debris. */
|
|
437
|
+
explosion,
|
|
438
|
+
/** Soft swelling puffs: dust, sand, sawdust, steam, smoke. */
|
|
439
|
+
dust,
|
|
440
|
+
/** One swelling glow: hits, strikes, muzzle flashes. */
|
|
441
|
+
flash,
|
|
442
|
+
/** The cleared-piece animation: swell, flash white, settle, shrink to nothing. */
|
|
443
|
+
pop,
|
|
444
|
+
/** Default `pop` duration in seconds. */
|
|
445
|
+
POP_TIME,
|
|
446
|
+
/** The same hue made self-lit (brightest channel lifted to `peak`), for glows. */
|
|
447
|
+
hot,
|
|
448
|
+
/** Part way (`t`, 0..1) to white. */
|
|
449
|
+
pale
|
|
450
|
+
};
|
|
451
|
+
//# sourceMappingURL=effects.js.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/** A writable quaternion; pass one as `out` to reuse it instead of allocating. */
|
|
4
|
+
|
|
5
|
+
const result = (out, x, y, z, w) => {
|
|
6
|
+
if (!out) return [x, y, z, w];
|
|
7
|
+
out[0] = x;
|
|
8
|
+
out[1] = y;
|
|
9
|
+
out[2] = z;
|
|
10
|
+
out[3] = w;
|
|
11
|
+
return out;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Quaternion helpers for `rotation` options, keyframes and geometry parts ([x, y, z, w]). Every
|
|
16
|
+
* function takes an optional `out` so per-frame code can reuse one array instead of allocating.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* world.set(car, { rotation: quat.y(heading) });
|
|
20
|
+
* const spin = quat.multiply(quat.y(yaw), quat.x(pitch), scratch); // pitch first, then yaw
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const quat = {
|
|
24
|
+
/** No rotation. */
|
|
25
|
+
identity: Object.freeze([0, 0, 0, 1]),
|
|
26
|
+
/** Rotation by `angle` radians around the unit axis (x, y, z). */
|
|
27
|
+
axisAngle(x, y, z, angle, out) {
|
|
28
|
+
const s = Math.sin(angle / 2);
|
|
29
|
+
return result(out, x * s, y * s, z * s, Math.cos(angle / 2));
|
|
30
|
+
},
|
|
31
|
+
/** Rotation around the x axis (pitch). */
|
|
32
|
+
x(angle, out) {
|
|
33
|
+
return result(out, Math.sin(angle / 2), 0, 0, Math.cos(angle / 2));
|
|
34
|
+
},
|
|
35
|
+
/** Rotation around the y axis (yaw, heading). */
|
|
36
|
+
y(angle, out) {
|
|
37
|
+
return result(out, 0, Math.sin(angle / 2), 0, Math.cos(angle / 2));
|
|
38
|
+
},
|
|
39
|
+
/** Rotation around the z axis (roll; spin for 2D games looking down -z). */
|
|
40
|
+
z(angle, out) {
|
|
41
|
+
return result(out, 0, 0, Math.sin(angle / 2), Math.cos(angle / 2));
|
|
42
|
+
},
|
|
43
|
+
/** `a` after `b`: `b` turns first. `out` may be `a` or `b`. */
|
|
44
|
+
multiply(a, b, out) {
|
|
45
|
+
const [ax, ay, az, aw] = a;
|
|
46
|
+
const [bx, by, bz, bw] = b;
|
|
47
|
+
return result(out, aw * bx + ax * bw + ay * bz - az * by, aw * by - ax * bz + ay * bw + az * bx, aw * bz + ax * by - ay * bx + az * bw, aw * bw - ax * bx - ay * by - az * bz);
|
|
48
|
+
},
|
|
49
|
+
/** Turns around x, then y, then z (radians): the same as `multiply(z(z), multiply(y(y), x(x)))`. */
|
|
50
|
+
fromEuler(x, y, z, out) {
|
|
51
|
+
const sx = Math.sin(x / 2);
|
|
52
|
+
const cx = Math.cos(x / 2);
|
|
53
|
+
const sy = Math.sin(y / 2);
|
|
54
|
+
const cy = Math.cos(y / 2);
|
|
55
|
+
const sz = Math.sin(z / 2);
|
|
56
|
+
const cz = Math.cos(z / 2);
|
|
57
|
+
return result(out, sx * cy * cz - cx * sy * sz, cx * sy * cz + sx * cy * sz, cx * cy * sz - sx * sy * cz, cx * cy * cz + sx * sy * sz);
|
|
58
|
+
},
|
|
59
|
+
/** Shortest-path interpolation from `a` (t = 0) to `b` (t = 1). */
|
|
60
|
+
slerp(a, b, t, out) {
|
|
61
|
+
let [bx, by, bz, bw] = b;
|
|
62
|
+
let cos = a[0] * bx + a[1] * by + a[2] * bz + a[3] * bw;
|
|
63
|
+
if (cos < 0) {
|
|
64
|
+
cos = -cos;
|
|
65
|
+
bx = -bx;
|
|
66
|
+
by = -by;
|
|
67
|
+
bz = -bz;
|
|
68
|
+
bw = -bw;
|
|
69
|
+
}
|
|
70
|
+
let k0 = 1 - t;
|
|
71
|
+
let k1 = t;
|
|
72
|
+
if (cos < 0.9995) {
|
|
73
|
+
const angle = Math.acos(cos);
|
|
74
|
+
const sin = Math.sin(angle);
|
|
75
|
+
k0 = Math.sin(k0 * angle) / sin;
|
|
76
|
+
k1 = Math.sin(k1 * angle) / sin;
|
|
77
|
+
}
|
|
78
|
+
const x = k0 * a[0] + k1 * bx;
|
|
79
|
+
const y = k0 * a[1] + k1 * by;
|
|
80
|
+
const z = k0 * a[2] + k1 * bz;
|
|
81
|
+
const w = k0 * a[3] + k1 * bw;
|
|
82
|
+
const length = Math.hypot(x, y, z, w) || 1;
|
|
83
|
+
return result(out, x / length, y / length, z / length, w / length);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=quat.js.map
|