@gwenjs/camera-core 0.1.0
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/README.md +188 -0
- package/dist/camera-core-plugin.d.ts +18 -0
- package/dist/camera-core-plugin.d.ts.map +1 -0
- package/dist/camera-path-store.d.ts +4 -0
- package/dist/camera-path-store.d.ts.map +1 -0
- package/dist/camera-system.d.ts +15 -0
- package/dist/camera-system.d.ts.map +1 -0
- package/dist/camera-viewport-map.d.ts +3 -0
- package/dist/camera-viewport-map.d.ts.map +1 -0
- package/dist/components.d.ts +232 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +353 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# `@gwenjs/camera-core`
|
|
2
|
+
|
|
3
|
+
ECS camera system for Gwen. Provides the components, `CameraSystem` orchestrator, and `CameraCorePlugin` shared by `@gwenjs/camera2d` and `@gwenjs/camera3d`.
|
|
4
|
+
|
|
5
|
+
> **Note** — you normally do not install this package directly. Use `@gwenjs/camera2d` or `@gwenjs/camera3d` instead. They install `CameraCorePlugin` automatically.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @gwenjs/camera-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
Since `camera-core` is a low-level primitive, the canonical way to use it is via a
|
|
16
|
+
`gwen.config.ts` with `CameraCorePlugin` and a setup system:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// gwen.config.ts
|
|
20
|
+
import { defineConfig } from "@gwenjs/app";
|
|
21
|
+
import { defineSystem, onUpdate } from "@gwenjs/core/system";
|
|
22
|
+
import { useEngine } from "@gwenjs/core";
|
|
23
|
+
import { useViewportManager } from "@gwenjs/renderer-core";
|
|
24
|
+
import { CameraCorePlugin, Camera, cameraViewportMap } from "@gwenjs/camera-core";
|
|
25
|
+
|
|
26
|
+
const CameraSetupSystem = defineSystem("CameraSetupSystem", () => {
|
|
27
|
+
const engine = useEngine();
|
|
28
|
+
const viewports = useViewportManager();
|
|
29
|
+
|
|
30
|
+
// Register a full-screen viewport (normalized [0–1])
|
|
31
|
+
viewports.set("main", { x: 0, y: 0, width: 1, height: 1 });
|
|
32
|
+
|
|
33
|
+
// Create the camera entity
|
|
34
|
+
const camId = engine.createEntity();
|
|
35
|
+
engine.addComponent(camId, Camera, {
|
|
36
|
+
active: 1,
|
|
37
|
+
priority: 0,
|
|
38
|
+
projectionType: 0, // 0 = orthographic, 1 = perspective
|
|
39
|
+
x: 0,
|
|
40
|
+
y: 0,
|
|
41
|
+
z: 0,
|
|
42
|
+
rotX: 0,
|
|
43
|
+
rotY: 0,
|
|
44
|
+
rotZ: 0,
|
|
45
|
+
zoom: 1,
|
|
46
|
+
fov: Math.PI / 3,
|
|
47
|
+
near: -1000,
|
|
48
|
+
far: 1000,
|
|
49
|
+
});
|
|
50
|
+
cameraViewportMap.set(camId, "main");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export default defineConfig({
|
|
54
|
+
plugins: [CameraCorePlugin(), CameraSetupSystem],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## ECS components
|
|
59
|
+
|
|
60
|
+
| Component | Purpose |
|
|
61
|
+
| -------------- | --------------------------------------------------------------------------------------- |
|
|
62
|
+
| `Camera` | Core camera state — position, rotation, projection, active flag, priority |
|
|
63
|
+
| `FollowTarget` | Lerps the camera toward another entity's position each frame |
|
|
64
|
+
| `CameraBounds` | Clamps the camera position to a bounding box after movement |
|
|
65
|
+
| `CameraShake` | Trauma-based screen shake — offsets the rendered position without moving `Camera.x/y/z` |
|
|
66
|
+
| `CameraPath` | ECS bookmark for path-following state (index + progress in current segment) |
|
|
67
|
+
|
|
68
|
+
### `Camera` fields
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
{
|
|
72
|
+
active: 0 | 1, // 0 = inactive, 1 = active
|
|
73
|
+
priority: number, // higher priority wins the viewport slot
|
|
74
|
+
projectionType: 0 | 1, // 0 = orthographic, 1 = perspective
|
|
75
|
+
x, y, z: number, // world position
|
|
76
|
+
rotX, rotY, rotZ: number, // euler rotation (radians)
|
|
77
|
+
zoom: number, // orthographic zoom
|
|
78
|
+
fov: number, // perspective field-of-view (radians)
|
|
79
|
+
near, far: number, // clipping planes
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `FollowTarget` fields
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
{
|
|
87
|
+
entityId: number, // target entity (u32 cast of EntityId)
|
|
88
|
+
lerp: number, // interpolation factor per frame [0–1]
|
|
89
|
+
offsetX, offsetY, offsetZ: number,
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### `CameraBounds` fields
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
{ minX, minY, minZ, maxX, maxY, maxZ: number }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### `CameraShake` fields
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
{
|
|
103
|
+
trauma: number, // current trauma [0–1], add to it to trigger shake
|
|
104
|
+
decay: number, // trauma lost per second
|
|
105
|
+
maxX: number, // max horizontal offset in world units
|
|
106
|
+
maxY: number, // max vertical offset in world units
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Side-car stores
|
|
111
|
+
|
|
112
|
+
`cameraViewportMap` and `cameraPathStore` are module-level `Map`s that live alongside the ECS components because strings and complex objects cannot be stored in SoA buffers.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { cameraViewportMap, cameraPathStore } from "@gwenjs/camera-core";
|
|
116
|
+
import type { CameraPathData } from "@gwenjs/camera-core";
|
|
117
|
+
|
|
118
|
+
// Assign a camera to a viewport
|
|
119
|
+
cameraViewportMap.set(camId, "main");
|
|
120
|
+
|
|
121
|
+
// Start a path
|
|
122
|
+
const pathData: CameraPathData = {
|
|
123
|
+
waypoints: [
|
|
124
|
+
{ position: { x: 200, y: 0, z: 0 }, duration: 1.5, easing: "easeInOut" },
|
|
125
|
+
{ position: { x: 200, y: 300, z: 0 }, duration: 1.0 },
|
|
126
|
+
],
|
|
127
|
+
opts: { loop: false, onComplete: () => console.log("done") },
|
|
128
|
+
elapsed: 0,
|
|
129
|
+
};
|
|
130
|
+
engine.addComponent(camId, CameraPath, { index: 0, progress: 0 });
|
|
131
|
+
cameraPathStore.set(camId, pathData);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Engine hooks
|
|
135
|
+
|
|
136
|
+
`CameraSystem` emits these hooks each frame via `engine.hooks`:
|
|
137
|
+
|
|
138
|
+
| Hook | Payload | When |
|
|
139
|
+
| ------------------- | ------------------------------------------------------ | ---------------------------------------------------- |
|
|
140
|
+
| `camera:activate` | `{ viewportId: string, entityId: EntityId }` | First time a camera becomes active on a viewport |
|
|
141
|
+
| `camera:deactivate` | `{ viewportId: string }` | The active camera is deactivated with no replacement |
|
|
142
|
+
| `camera:switch` | `{ viewportId: string, from: EntityId, to: EntityId }` | Active camera changes from one entity to another |
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
engine.hooks.hook("camera:activate", ({ viewportId, entityId }) => {
|
|
146
|
+
console.log(`camera ${entityId} is now active on ${viewportId}`);
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`viewport:*` hooks (`viewport:add`, `viewport:resize`, `viewport:remove`) are declared in `@gwenjs/renderer-core`.
|
|
151
|
+
|
|
152
|
+
## `CameraSystem` pipeline (per frame)
|
|
153
|
+
|
|
154
|
+
1. `CameraManager.clearFrame()` — stale states are discarded
|
|
155
|
+
2. For each entity in `useQuery([Camera])` with `active = 1`:
|
|
156
|
+
- Apply `FollowTarget` lerp toward the target entity — **or** advance `CameraPath` waypoints
|
|
157
|
+
- Clamp to `CameraBounds`
|
|
158
|
+
- Compute `CameraShake` offset (does **not** modify `Camera.x/y/z`)
|
|
159
|
+
- Push `CameraState` to `CameraManager`
|
|
160
|
+
3. Detect semantic changes per viewport and emit `camera:activate / deactivate / switch`
|
|
161
|
+
|
|
162
|
+
## Multi-camera / priority
|
|
163
|
+
|
|
164
|
+
Multiple cameras can target the same viewport. The one with the highest `Camera.priority` wins. On equal priority, the last entity to push its state wins.
|
|
165
|
+
|
|
166
|
+
## Building a custom camera handle
|
|
167
|
+
|
|
168
|
+
If `camera2d`/`camera3d` don't fit your needs, you can build your own on top of `camera-core`:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { CameraCorePlugin, Camera, cameraViewportMap } from "@gwenjs/camera-core";
|
|
172
|
+
import { useCameraManager } from "@gwenjs/renderer-core";
|
|
173
|
+
import { defineSystem, onUpdate } from "@gwenjs/core/system";
|
|
174
|
+
|
|
175
|
+
await engine.use(CameraCorePlugin());
|
|
176
|
+
|
|
177
|
+
// Your system reads CameraManager after CameraSystem runs
|
|
178
|
+
const MyRenderSystem = defineSystem("MyRenderSystem", () => {
|
|
179
|
+
const cameras = useCameraManager();
|
|
180
|
+
onUpdate(() => {
|
|
181
|
+
const state = cameras.get("main");
|
|
182
|
+
if (state) {
|
|
183
|
+
const { x, y, z } = state.worldTransform.position;
|
|
184
|
+
// apply to your renderer
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file CameraCorePlugin — installs CameraManager, ViewportManager, and CameraSystem.
|
|
3
|
+
*
|
|
4
|
+
* Called automatically by Camera2DPlugin and Camera3DPlugin.
|
|
5
|
+
* Users do not install this plugin manually unless they are building a custom
|
|
6
|
+
* camera composable from scratch.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* // Only if building a custom camera — normally not needed
|
|
11
|
+
* await engine.use(CameraCorePlugin())
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare const CameraCorePlugin: (options?: unknown) => {
|
|
15
|
+
name: string;
|
|
16
|
+
setup(engine: import('@gwenjs/core').GwenEngine): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=camera-core-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-core-plugin.d.ts","sourceRoot":"","sources":["../src/camera-core-plugin.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAMH,eAAO,MAAM,gBAAgB;;;CAO1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-path-store.d.ts","sourceRoot":"","sources":["../src/camera-path-store.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,eAAO,MAAM,eAAe,+BAAsC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file CameraSystem — single orchestrator for all camera entities.
|
|
3
|
+
*
|
|
4
|
+
* Pipeline per frame (onAfterUpdate):
|
|
5
|
+
* 1. clearFrame() on CameraManager so stale states don't persist
|
|
6
|
+
* 2. For each active camera entity:
|
|
7
|
+
* a. Apply FollowTarget (lerp toward target) — or —
|
|
8
|
+
* Advance CameraPath (interpolate waypoints)
|
|
9
|
+
* b. Clamp to CameraBounds
|
|
10
|
+
* c. Apply CameraShake offset (does NOT write Camera.x/y/z)
|
|
11
|
+
* d. Push CameraState to CameraManager
|
|
12
|
+
* 3. Detect semantic changes per viewport and emit camera:* hooks
|
|
13
|
+
*/
|
|
14
|
+
export declare const CameraSystem: import('@gwenjs/core').GwenPlugin;
|
|
15
|
+
//# sourceMappingURL=camera-system.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-system.d.ts","sourceRoot":"","sources":["../src/camera-system.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAgBH,eAAO,MAAM,YAAY,mCA+LvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera-viewport-map.d.ts","sourceRoot":"","sources":["../src/camera-viewport-map.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,eAAO,MAAM,iBAAiB,uBAA8B,CAAC"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ECS component definitions for the camera system.
|
|
3
|
+
*
|
|
4
|
+
* All SoA arrays are indexed by EntityId.
|
|
5
|
+
* String data (viewportId) and object data (CameraPathData) live in separate
|
|
6
|
+
* Maps — see camera-viewport-map.ts and camera-path-store.ts.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Core camera component. One per camera entity.
|
|
10
|
+
*
|
|
11
|
+
* projectionType: 0 = orthographic, 1 = perspective
|
|
12
|
+
* active: 0 = inactive, 1 = active
|
|
13
|
+
*
|
|
14
|
+
* The `viewportId` string is stored in `cameraViewportMap` (camera-viewport-map.ts),
|
|
15
|
+
* not here — strings cannot live in SoA buffers.
|
|
16
|
+
*/
|
|
17
|
+
export declare const Camera: import('@gwenjs/core').ComponentDefinition<{
|
|
18
|
+
active: {
|
|
19
|
+
type: "u32";
|
|
20
|
+
byteLength: number;
|
|
21
|
+
read: "getUint32";
|
|
22
|
+
write: "setUint32";
|
|
23
|
+
};
|
|
24
|
+
priority: {
|
|
25
|
+
type: "f32";
|
|
26
|
+
byteLength: number;
|
|
27
|
+
read: "getFloat32";
|
|
28
|
+
write: "setFloat32";
|
|
29
|
+
};
|
|
30
|
+
x: {
|
|
31
|
+
type: "f32";
|
|
32
|
+
byteLength: number;
|
|
33
|
+
read: "getFloat32";
|
|
34
|
+
write: "setFloat32";
|
|
35
|
+
};
|
|
36
|
+
y: {
|
|
37
|
+
type: "f32";
|
|
38
|
+
byteLength: number;
|
|
39
|
+
read: "getFloat32";
|
|
40
|
+
write: "setFloat32";
|
|
41
|
+
};
|
|
42
|
+
z: {
|
|
43
|
+
type: "f32";
|
|
44
|
+
byteLength: number;
|
|
45
|
+
read: "getFloat32";
|
|
46
|
+
write: "setFloat32";
|
|
47
|
+
};
|
|
48
|
+
rotX: {
|
|
49
|
+
type: "f32";
|
|
50
|
+
byteLength: number;
|
|
51
|
+
read: "getFloat32";
|
|
52
|
+
write: "setFloat32";
|
|
53
|
+
};
|
|
54
|
+
rotY: {
|
|
55
|
+
type: "f32";
|
|
56
|
+
byteLength: number;
|
|
57
|
+
read: "getFloat32";
|
|
58
|
+
write: "setFloat32";
|
|
59
|
+
};
|
|
60
|
+
rotZ: {
|
|
61
|
+
type: "f32";
|
|
62
|
+
byteLength: number;
|
|
63
|
+
read: "getFloat32";
|
|
64
|
+
write: "setFloat32";
|
|
65
|
+
};
|
|
66
|
+
projectionType: {
|
|
67
|
+
type: "u32";
|
|
68
|
+
byteLength: number;
|
|
69
|
+
read: "getUint32";
|
|
70
|
+
write: "setUint32";
|
|
71
|
+
};
|
|
72
|
+
zoom: {
|
|
73
|
+
type: "f32";
|
|
74
|
+
byteLength: number;
|
|
75
|
+
read: "getFloat32";
|
|
76
|
+
write: "setFloat32";
|
|
77
|
+
};
|
|
78
|
+
fov: {
|
|
79
|
+
type: "f32";
|
|
80
|
+
byteLength: number;
|
|
81
|
+
read: "getFloat32";
|
|
82
|
+
write: "setFloat32";
|
|
83
|
+
};
|
|
84
|
+
near: {
|
|
85
|
+
type: "f32";
|
|
86
|
+
byteLength: number;
|
|
87
|
+
read: "getFloat32";
|
|
88
|
+
write: "setFloat32";
|
|
89
|
+
};
|
|
90
|
+
far: {
|
|
91
|
+
type: "f32";
|
|
92
|
+
byteLength: number;
|
|
93
|
+
read: "getFloat32";
|
|
94
|
+
write: "setFloat32";
|
|
95
|
+
};
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Follow-target behaviour. When present on a camera entity, `CameraSystem`
|
|
99
|
+
* lerps the camera position toward the target entity's Camera x/y/z each frame.
|
|
100
|
+
*
|
|
101
|
+
* Mutually exclusive with `CameraPath` — adding FollowTarget removes CameraPath and vice versa.
|
|
102
|
+
*/
|
|
103
|
+
export declare const FollowTarget: import('@gwenjs/core').ComponentDefinition<{
|
|
104
|
+
entityId: {
|
|
105
|
+
type: "u64";
|
|
106
|
+
byteLength: number;
|
|
107
|
+
read: "getBigUint64";
|
|
108
|
+
write: "setBigUint64";
|
|
109
|
+
};
|
|
110
|
+
lerp: {
|
|
111
|
+
type: "f32";
|
|
112
|
+
byteLength: number;
|
|
113
|
+
read: "getFloat32";
|
|
114
|
+
write: "setFloat32";
|
|
115
|
+
};
|
|
116
|
+
offsetX: {
|
|
117
|
+
type: "f32";
|
|
118
|
+
byteLength: number;
|
|
119
|
+
read: "getFloat32";
|
|
120
|
+
write: "setFloat32";
|
|
121
|
+
};
|
|
122
|
+
offsetY: {
|
|
123
|
+
type: "f32";
|
|
124
|
+
byteLength: number;
|
|
125
|
+
read: "getFloat32";
|
|
126
|
+
write: "setFloat32";
|
|
127
|
+
};
|
|
128
|
+
offsetZ: {
|
|
129
|
+
type: "f32";
|
|
130
|
+
byteLength: number;
|
|
131
|
+
read: "getFloat32";
|
|
132
|
+
write: "setFloat32";
|
|
133
|
+
};
|
|
134
|
+
}>;
|
|
135
|
+
/**
|
|
136
|
+
* Spatial clamp. `CameraSystem` clamps `Camera.x/y/z` to [min, max] after
|
|
137
|
+
* applying follow/path each frame.
|
|
138
|
+
*/
|
|
139
|
+
export declare const CameraBounds: import('@gwenjs/core').ComponentDefinition<{
|
|
140
|
+
minX: {
|
|
141
|
+
type: "f32";
|
|
142
|
+
byteLength: number;
|
|
143
|
+
read: "getFloat32";
|
|
144
|
+
write: "setFloat32";
|
|
145
|
+
};
|
|
146
|
+
minY: {
|
|
147
|
+
type: "f32";
|
|
148
|
+
byteLength: number;
|
|
149
|
+
read: "getFloat32";
|
|
150
|
+
write: "setFloat32";
|
|
151
|
+
};
|
|
152
|
+
minZ: {
|
|
153
|
+
type: "f32";
|
|
154
|
+
byteLength: number;
|
|
155
|
+
read: "getFloat32";
|
|
156
|
+
write: "setFloat32";
|
|
157
|
+
};
|
|
158
|
+
maxX: {
|
|
159
|
+
type: "f32";
|
|
160
|
+
byteLength: number;
|
|
161
|
+
read: "getFloat32";
|
|
162
|
+
write: "setFloat32";
|
|
163
|
+
};
|
|
164
|
+
maxY: {
|
|
165
|
+
type: "f32";
|
|
166
|
+
byteLength: number;
|
|
167
|
+
read: "getFloat32";
|
|
168
|
+
write: "setFloat32";
|
|
169
|
+
};
|
|
170
|
+
maxZ: {
|
|
171
|
+
type: "f32";
|
|
172
|
+
byteLength: number;
|
|
173
|
+
read: "getFloat32";
|
|
174
|
+
write: "setFloat32";
|
|
175
|
+
};
|
|
176
|
+
}>;
|
|
177
|
+
/**
|
|
178
|
+
* Trauma-based screen shake.
|
|
179
|
+
*
|
|
180
|
+
* - `trauma` accumulates (clamped to [0,1]) and decays by `decay` per second.
|
|
181
|
+
* - `CameraSystem` computes a shake offset each frame proportional to trauma²
|
|
182
|
+
* and adds it to the final `CameraState.worldTransform` **without** modifying
|
|
183
|
+
* `Camera.x/y/z` — so the underlying position remains stable.
|
|
184
|
+
*/
|
|
185
|
+
export declare const CameraShake: import('@gwenjs/core').ComponentDefinition<{
|
|
186
|
+
trauma: {
|
|
187
|
+
type: "f32";
|
|
188
|
+
byteLength: number;
|
|
189
|
+
read: "getFloat32";
|
|
190
|
+
write: "setFloat32";
|
|
191
|
+
};
|
|
192
|
+
decay: {
|
|
193
|
+
type: "f32";
|
|
194
|
+
byteLength: number;
|
|
195
|
+
read: "getFloat32";
|
|
196
|
+
write: "setFloat32";
|
|
197
|
+
};
|
|
198
|
+
maxX: {
|
|
199
|
+
type: "f32";
|
|
200
|
+
byteLength: number;
|
|
201
|
+
read: "getFloat32";
|
|
202
|
+
write: "setFloat32";
|
|
203
|
+
};
|
|
204
|
+
maxY: {
|
|
205
|
+
type: "f32";
|
|
206
|
+
byteLength: number;
|
|
207
|
+
read: "getFloat32";
|
|
208
|
+
write: "setFloat32";
|
|
209
|
+
};
|
|
210
|
+
}>;
|
|
211
|
+
/**
|
|
212
|
+
* Path-following behaviour. The actual waypoints and options are stored in
|
|
213
|
+
* `cameraPathStore` (camera-path-store.ts). This component tracks position
|
|
214
|
+
* in the path.
|
|
215
|
+
*
|
|
216
|
+
* Mutually exclusive with `FollowTarget`.
|
|
217
|
+
*/
|
|
218
|
+
export declare const CameraPath: import('@gwenjs/core').ComponentDefinition<{
|
|
219
|
+
index: {
|
|
220
|
+
type: "u32";
|
|
221
|
+
byteLength: number;
|
|
222
|
+
read: "getUint32";
|
|
223
|
+
write: "setUint32";
|
|
224
|
+
};
|
|
225
|
+
progress: {
|
|
226
|
+
type: "f32";
|
|
227
|
+
byteLength: number;
|
|
228
|
+
read: "getFloat32";
|
|
229
|
+
write: "setFloat32";
|
|
230
|
+
};
|
|
231
|
+
}>;
|
|
232
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAIH;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBjB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EASvB,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUvB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;EAQtB,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;EAMrB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Camera error codes and error classes.
|
|
3
|
+
*
|
|
4
|
+
* All errors carry a `code`, a human-readable `hint`, and a `docsUrl`.
|
|
5
|
+
* Callers may log these errors before throwing or handling them.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CameraErrorCodes: {
|
|
8
|
+
readonly VIEWPORT_NOT_FOUND: "CAMERA:VIEWPORT_NOT_FOUND";
|
|
9
|
+
readonly EMPTY_PATH: "CAMERA:EMPTY_PATH";
|
|
10
|
+
readonly PERSPECTIVE_FALLBACK: "CAMERA:PERSPECTIVE_FALLBACK";
|
|
11
|
+
readonly PRIORITY_CONFLICT: "CAMERA:PRIORITY_CONFLICT";
|
|
12
|
+
};
|
|
13
|
+
export type CameraErrorCode = (typeof CameraErrorCodes)[keyof typeof CameraErrorCodes];
|
|
14
|
+
/**
|
|
15
|
+
* Thrown by `use2DCamera()`, `use3DCamera()`, and `Camera*Handle.setViewport()`
|
|
16
|
+
* when the requested viewport id is not registered in `ViewportManager`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const err = new CameraViewportNotFoundError('minimap')
|
|
21
|
+
* log.error(`[${err.code}] ${err.message} — ${err.hint}`)
|
|
22
|
+
* throw err
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class CameraViewportNotFoundError extends Error {
|
|
26
|
+
readonly code: "CAMERA:VIEWPORT_NOT_FOUND";
|
|
27
|
+
readonly hint: string;
|
|
28
|
+
readonly docsUrl: string;
|
|
29
|
+
constructor(viewportId: string);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Thrown by `Camera*Handle.playPath()` when the waypoints array is empty.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const err = new CameraEmptyPathError()
|
|
37
|
+
* log.error(`[${err.code}] ${err.message} — ${err.hint}`)
|
|
38
|
+
* throw err
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare class CameraEmptyPathError extends Error {
|
|
42
|
+
readonly code: "CAMERA:EMPTY_PATH";
|
|
43
|
+
readonly hint: string;
|
|
44
|
+
readonly docsUrl: string;
|
|
45
|
+
constructor();
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB;;;;;CAKnB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,OAAO,gBAAgB,CAAC,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,IAAI,8BAAuC;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,UAAU,EAAE,MAAM;CAQ/B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,sBAA+B;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;;CAQ1B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { EntityId } from '@gwenjs/core';
|
|
2
|
+
/**
|
|
3
|
+
* @file Public API for `@gwenjs/camera-core`.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import {
|
|
8
|
+
* Camera,
|
|
9
|
+
* FollowTarget,
|
|
10
|
+
* CameraShake,
|
|
11
|
+
* CameraCorePlugin,
|
|
12
|
+
* CameraErrorCodes,
|
|
13
|
+
* cameraViewportMap,
|
|
14
|
+
* cameraPathStore,
|
|
15
|
+
* } from '@gwenjs/camera-core'
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export { CameraErrorCodes, type CameraErrorCode, CameraViewportNotFoundError, CameraEmptyPathError, } from './errors.js';
|
|
19
|
+
export type { EasingName, CameraWaypoint, PathOpts, CameraPathData, BlendOpts, FollowOpts, ShakeOpts, ShakeHandle, } from './types.js';
|
|
20
|
+
export { Camera, FollowTarget, CameraBounds, CameraShake, CameraPath } from './components.js';
|
|
21
|
+
export { cameraViewportMap } from './camera-viewport-map.js';
|
|
22
|
+
export { cameraPathStore } from './camera-path-store.js';
|
|
23
|
+
export { CameraSystem } from './camera-system.js';
|
|
24
|
+
export { CameraCorePlugin } from './camera-core-plugin.js';
|
|
25
|
+
declare module "@gwenjs/core" {
|
|
26
|
+
interface GwenRuntimeHooks {
|
|
27
|
+
/**
|
|
28
|
+
* Fired the first time a camera becomes active on a viewport.
|
|
29
|
+
* Not fired again until the camera is deactivated and a new one activates.
|
|
30
|
+
*/
|
|
31
|
+
"camera:activate": (payload: {
|
|
32
|
+
viewportId: string;
|
|
33
|
+
entityId: EntityId;
|
|
34
|
+
}) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Fired when the previously active camera on a viewport loses its active state
|
|
37
|
+
* and no replacement camera is found for that frame.
|
|
38
|
+
*/
|
|
39
|
+
"camera:deactivate": (payload: {
|
|
40
|
+
viewportId: string;
|
|
41
|
+
}) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Fired when the active camera on a viewport changes from one entity to another.
|
|
44
|
+
* Not fired on initial activation — use `camera:activate` for that.
|
|
45
|
+
*/
|
|
46
|
+
"camera:switch": (payload: {
|
|
47
|
+
viewportId: string;
|
|
48
|
+
from: EntityId;
|
|
49
|
+
to: EntityId;
|
|
50
|
+
}) => void;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,2BAA2B,EAC3B,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,UAAU,EACV,cAAc,EACd,QAAQ,EACR,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAI3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,QAAQ,cAAc,CAAC;IAC5B,UAAU,gBAAgB;QACxB;;;WAGG;QACH,iBAAiB,EAAE,CAAC,OAAO,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,CAAC;QACjF;;;WAGG;QACH,mBAAmB,EAAE,CAAC,OAAO,EAAE;YAAE,UAAU,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;QAC/D;;;WAGG;QACH,eAAe,EAAE,CAAC,OAAO,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,QAAQ,CAAC;YAAC,EAAE,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,CAAC;KAC1F;CACF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { Types as e, defineComponent as t, useEngine as n } from "@gwenjs/core";
|
|
2
|
+
import { getOrCreateCameraManager as r, getOrCreateViewportManager as i, useCameraManager as a, useViewportManager as o } from "@gwenjs/renderer-core";
|
|
3
|
+
//#region src/errors.ts
|
|
4
|
+
var s = {
|
|
5
|
+
VIEWPORT_NOT_FOUND: "CAMERA:VIEWPORT_NOT_FOUND",
|
|
6
|
+
EMPTY_PATH: "CAMERA:EMPTY_PATH",
|
|
7
|
+
PERSPECTIVE_FALLBACK: "CAMERA:PERSPECTIVE_FALLBACK",
|
|
8
|
+
PRIORITY_CONFLICT: "CAMERA:PRIORITY_CONFLICT"
|
|
9
|
+
}, c = class extends Error {
|
|
10
|
+
code = s.VIEWPORT_NOT_FOUND;
|
|
11
|
+
hint;
|
|
12
|
+
docsUrl;
|
|
13
|
+
constructor(e) {
|
|
14
|
+
super(`[GwenCamera] Viewport "${e}" not found.`), this.name = "CameraViewportNotFoundError", this.hint = `Declare it in defineConfig({ viewports: { "${e}": { x, y, width, height } } }) or call useViewportManager().set("${e}", region) at runtime.`, this.docsUrl = "https://gwenengine.dev/docs/camera#viewports";
|
|
15
|
+
}
|
|
16
|
+
}, l = class extends Error {
|
|
17
|
+
code = s.EMPTY_PATH;
|
|
18
|
+
hint;
|
|
19
|
+
docsUrl;
|
|
20
|
+
constructor() {
|
|
21
|
+
super("[GwenCamera] playPath() requires at least one waypoint."), this.name = "CameraEmptyPathError", this.hint = "Provide a non-empty waypoints array to playPath().", this.docsUrl = "https://gwenengine.dev/docs/camera#playPath";
|
|
22
|
+
}
|
|
23
|
+
}, u = t({
|
|
24
|
+
name: "Camera",
|
|
25
|
+
schema: {
|
|
26
|
+
active: e.u32,
|
|
27
|
+
priority: e.f32,
|
|
28
|
+
x: e.f32,
|
|
29
|
+
y: e.f32,
|
|
30
|
+
z: e.f32,
|
|
31
|
+
rotX: e.f32,
|
|
32
|
+
rotY: e.f32,
|
|
33
|
+
rotZ: e.f32,
|
|
34
|
+
projectionType: e.u32,
|
|
35
|
+
zoom: e.f32,
|
|
36
|
+
fov: e.f32,
|
|
37
|
+
near: e.f32,
|
|
38
|
+
far: e.f32
|
|
39
|
+
}
|
|
40
|
+
}), d = t({
|
|
41
|
+
name: "FollowTarget",
|
|
42
|
+
schema: {
|
|
43
|
+
entityId: e.u64,
|
|
44
|
+
lerp: e.f32,
|
|
45
|
+
offsetX: e.f32,
|
|
46
|
+
offsetY: e.f32,
|
|
47
|
+
offsetZ: e.f32
|
|
48
|
+
}
|
|
49
|
+
}), f = t({
|
|
50
|
+
name: "CameraBounds",
|
|
51
|
+
schema: {
|
|
52
|
+
minX: e.f32,
|
|
53
|
+
minY: e.f32,
|
|
54
|
+
minZ: e.f32,
|
|
55
|
+
maxX: e.f32,
|
|
56
|
+
maxY: e.f32,
|
|
57
|
+
maxZ: e.f32
|
|
58
|
+
}
|
|
59
|
+
}), p = t({
|
|
60
|
+
name: "CameraShake",
|
|
61
|
+
schema: {
|
|
62
|
+
trauma: e.f32,
|
|
63
|
+
decay: e.f32,
|
|
64
|
+
maxX: e.f32,
|
|
65
|
+
maxY: e.f32
|
|
66
|
+
}
|
|
67
|
+
}), m = t({
|
|
68
|
+
name: "CameraPath",
|
|
69
|
+
schema: {
|
|
70
|
+
index: e.u32,
|
|
71
|
+
progress: e.f32
|
|
72
|
+
}
|
|
73
|
+
}), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map();
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region ../../node_modules/.pnpm/unctx@2.5.0/node_modules/unctx/dist/index.mjs
|
|
76
|
+
function _(e = {}) {
|
|
77
|
+
let t, n = !1, r = (e) => {
|
|
78
|
+
if (t && t !== e) throw Error("Context conflict");
|
|
79
|
+
}, i;
|
|
80
|
+
if (e.asyncContext) {
|
|
81
|
+
let t = e.AsyncLocalStorage || globalThis.AsyncLocalStorage;
|
|
82
|
+
t ? i = new t() : console.warn("[unctx] `AsyncLocalStorage` is not provided.");
|
|
83
|
+
}
|
|
84
|
+
let a = () => {
|
|
85
|
+
if (i) {
|
|
86
|
+
let e = i.getStore();
|
|
87
|
+
if (e !== void 0) return e;
|
|
88
|
+
}
|
|
89
|
+
return t;
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
use: () => {
|
|
93
|
+
let e = a();
|
|
94
|
+
if (e === void 0) throw Error("Context is not available");
|
|
95
|
+
return e;
|
|
96
|
+
},
|
|
97
|
+
tryUse: () => a(),
|
|
98
|
+
set: (e, i) => {
|
|
99
|
+
i || r(e), t = e, n = !0;
|
|
100
|
+
},
|
|
101
|
+
unset: () => {
|
|
102
|
+
t = void 0, n = !1;
|
|
103
|
+
},
|
|
104
|
+
call: (e, a) => {
|
|
105
|
+
r(e), t = e;
|
|
106
|
+
try {
|
|
107
|
+
return i ? i.run(e, a) : a();
|
|
108
|
+
} finally {
|
|
109
|
+
n || (t = void 0);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
async callAsync(e, r) {
|
|
113
|
+
t = e;
|
|
114
|
+
let a = () => {
|
|
115
|
+
t = e;
|
|
116
|
+
}, o = () => t === e ? a : void 0;
|
|
117
|
+
S.add(o);
|
|
118
|
+
try {
|
|
119
|
+
let a = i ? i.run(e, r) : r();
|
|
120
|
+
return n || (t = void 0), await a;
|
|
121
|
+
} finally {
|
|
122
|
+
S.delete(o);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function v(e = {}) {
|
|
128
|
+
let t = {};
|
|
129
|
+
return { get(n, r = {}) {
|
|
130
|
+
return t[n] || (t[n] = _({
|
|
131
|
+
...e,
|
|
132
|
+
...r
|
|
133
|
+
})), t[n];
|
|
134
|
+
} };
|
|
135
|
+
}
|
|
136
|
+
var y = typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : typeof global < "u" ? global : typeof window < "u" ? window : {}, b = "__unctx__";
|
|
137
|
+
y[b] || (y[b] = v());
|
|
138
|
+
var x = "__unctx_async_handlers__", S = y[x] || (y[x] = /* @__PURE__ */ new Set()), C = _({ asyncContext: !1 }), w = class extends Error {
|
|
139
|
+
constructor(e) {
|
|
140
|
+
super(e), this.name = "GwenContextError";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
function T() {
|
|
144
|
+
let e = C.tryUse();
|
|
145
|
+
if (!e) throw new w("[GWEN] useEngine() was called outside of an engine context.\nMake sure you are calling it inside defineSystem(), engine.run(), or a plugin lifecycle hook (setup, onUpdate, onRender, etc.).");
|
|
146
|
+
return e;
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region ../core/src/system.ts
|
|
150
|
+
var E = null;
|
|
151
|
+
function D() {
|
|
152
|
+
if (!E) throw Error("[GWEN] onUpdate/onRender/onBeforeUpdate/onAfterUpdate must be called inside a defineSystem() setup callback, not inside the lifecycle function itself.");
|
|
153
|
+
return E;
|
|
154
|
+
}
|
|
155
|
+
function O(e, t) {
|
|
156
|
+
let n = E;
|
|
157
|
+
E = e;
|
|
158
|
+
try {
|
|
159
|
+
t();
|
|
160
|
+
} finally {
|
|
161
|
+
E = n;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function k(e) {
|
|
165
|
+
D().onAfterUpdate(e);
|
|
166
|
+
}
|
|
167
|
+
function A(e, t) {
|
|
168
|
+
let n = typeof e == "string" ? e : e.name || "", r = typeof e == "function" ? e : t, i = [], a = [], o = [], s = [];
|
|
169
|
+
return {
|
|
170
|
+
name: n || (console.warn("[GWEN] defineSystem() called without a name. Pass a name as first argument: defineSystem('mySystem', () => { ... })"), "anonymous-system"),
|
|
171
|
+
setup(e) {
|
|
172
|
+
O({
|
|
173
|
+
onBeforeUpdate: (e) => i.push(e),
|
|
174
|
+
onUpdate: (e) => a.push(e),
|
|
175
|
+
onAfterUpdate: (e) => o.push(e),
|
|
176
|
+
onRender: (e) => s.push(e)
|
|
177
|
+
}, r);
|
|
178
|
+
},
|
|
179
|
+
onBeforeUpdate(e) {
|
|
180
|
+
for (let t = 0; t < i.length; t++) i[t](e);
|
|
181
|
+
},
|
|
182
|
+
onUpdate(e) {
|
|
183
|
+
for (let t = 0; t < a.length; t++) a[t](e);
|
|
184
|
+
},
|
|
185
|
+
onAfterUpdate(e) {
|
|
186
|
+
for (let t = 0; t < o.length; t++) o[t](e);
|
|
187
|
+
},
|
|
188
|
+
onRender() {
|
|
189
|
+
for (let e = 0; e < s.length; e++) s[e]();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function j(e) {
|
|
194
|
+
return T().createLiveQuery(e);
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/camera-system.ts
|
|
198
|
+
function M(e, t) {
|
|
199
|
+
return Math.sin(t * 127.1 + e * 311.7) * e * e;
|
|
200
|
+
}
|
|
201
|
+
var N = A("CameraSystem", () => {
|
|
202
|
+
let e = n(), t = a();
|
|
203
|
+
o();
|
|
204
|
+
let r = j([u]), i = /* @__PURE__ */ new Map(), s = 0;
|
|
205
|
+
k((n) => {
|
|
206
|
+
s++;
|
|
207
|
+
let a = n / 1e3;
|
|
208
|
+
t.clearFrame();
|
|
209
|
+
for (let n of r) {
|
|
210
|
+
let r = n.id, i = e.getComponent(r, u);
|
|
211
|
+
if (!i || i.active !== 1) continue;
|
|
212
|
+
let o = h.get(r);
|
|
213
|
+
if (!o) continue;
|
|
214
|
+
let c = i.x, l = i.y, _ = i.z, v = e.hasComponent(r, d), y = g.has(r) && e.hasComponent(r, m);
|
|
215
|
+
if (v) {
|
|
216
|
+
let t = e.getComponent(r, d), n = t.entityId, a = e.getComponent(n, u);
|
|
217
|
+
if (a) {
|
|
218
|
+
let n = a.x + t.offsetX, o = a.y + t.offsetY, s = a.z + t.offsetZ;
|
|
219
|
+
c += (n - c) * t.lerp, l += (o - l) * t.lerp, _ += (s - _) * t.lerp, e.addComponent(r, u, {
|
|
220
|
+
...i,
|
|
221
|
+
x: c,
|
|
222
|
+
y: l,
|
|
223
|
+
z: _
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} else if (y) {
|
|
227
|
+
let t = g.get(r), n = e.getComponent(r, m);
|
|
228
|
+
if (!n) continue;
|
|
229
|
+
let o = t.waypoints[n.index];
|
|
230
|
+
if (o) {
|
|
231
|
+
t.elapsed += a;
|
|
232
|
+
let s = Math.min(t.elapsed / o.duration, 1);
|
|
233
|
+
if (e.addComponent(r, m, {
|
|
234
|
+
...n,
|
|
235
|
+
progress: s
|
|
236
|
+
}), s >= 1) {
|
|
237
|
+
c = o.position.x, l = o.position.y, _ = o.position.z, e.addComponent(r, u, {
|
|
238
|
+
...i,
|
|
239
|
+
x: c,
|
|
240
|
+
y: l,
|
|
241
|
+
z: _
|
|
242
|
+
});
|
|
243
|
+
let a = n.index + 1;
|
|
244
|
+
a < t.waypoints.length ? (e.addComponent(r, m, {
|
|
245
|
+
...n,
|
|
246
|
+
index: a,
|
|
247
|
+
progress: 0
|
|
248
|
+
}), t.elapsed = 0) : t.opts.loop ? (e.addComponent(r, m, {
|
|
249
|
+
...n,
|
|
250
|
+
index: 0,
|
|
251
|
+
progress: 0
|
|
252
|
+
}), t.elapsed = 0, t.opts.onWaypoint?.(a - 1)) : (t.opts.onComplete?.(), g.delete(r), e.removeComponent(r, m));
|
|
253
|
+
} else {
|
|
254
|
+
let a = t.waypoints[n.index - 1], d = a ? a.position.x : i.x, f = a ? a.position.y : i.y, p = a ? a.position.z : i.z;
|
|
255
|
+
c = d + (o.position.x - d) * s, l = f + (o.position.y - f) * s, _ = p + (o.position.z - p) * s, e.addComponent(r, u, {
|
|
256
|
+
...i,
|
|
257
|
+
x: c,
|
|
258
|
+
y: l,
|
|
259
|
+
z: _
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (e.hasComponent(r, f)) {
|
|
265
|
+
let t = e.getComponent(r, f);
|
|
266
|
+
c = Math.max(t.minX, Math.min(t.maxX, c)), l = Math.max(t.minY, Math.min(t.maxY, l)), _ = Math.max(t.minZ, Math.min(t.maxZ, _)), e.addComponent(r, u, {
|
|
267
|
+
...i,
|
|
268
|
+
x: c,
|
|
269
|
+
y: l,
|
|
270
|
+
z: _
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
let b = 0, x = 0, S = e.getComponent(r, p);
|
|
274
|
+
if (S && S.trauma > 0) {
|
|
275
|
+
b = M(S.trauma, s) * S.maxX, x = M(S.trauma, s + 100) * S.maxY;
|
|
276
|
+
let t = Math.max(0, S.trauma - S.decay * a);
|
|
277
|
+
e.addComponent(r, p, {
|
|
278
|
+
...S,
|
|
279
|
+
trauma: t
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
let C = e.getComponent(r, u) ?? i, w = C.projectionType === 0 ? {
|
|
283
|
+
type: "orthographic",
|
|
284
|
+
zoom: C.zoom,
|
|
285
|
+
near: C.near,
|
|
286
|
+
far: C.far
|
|
287
|
+
} : {
|
|
288
|
+
type: "perspective",
|
|
289
|
+
fov: C.fov,
|
|
290
|
+
near: C.near,
|
|
291
|
+
far: C.far
|
|
292
|
+
};
|
|
293
|
+
t.set(o, {
|
|
294
|
+
worldTransform: {
|
|
295
|
+
position: {
|
|
296
|
+
x: c + b,
|
|
297
|
+
y: l + x,
|
|
298
|
+
z: _
|
|
299
|
+
},
|
|
300
|
+
rotation: {
|
|
301
|
+
x: C.rotX,
|
|
302
|
+
y: C.rotY,
|
|
303
|
+
z: C.rotZ
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
projection: w,
|
|
307
|
+
viewportId: o,
|
|
308
|
+
active: !0,
|
|
309
|
+
priority: C.priority
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
let o = /* @__PURE__ */ new Map();
|
|
313
|
+
for (let n of r) {
|
|
314
|
+
let r = n.id, i = e.getComponent(r, u);
|
|
315
|
+
if (!i || i.active !== 1) continue;
|
|
316
|
+
let a = h.get(r);
|
|
317
|
+
if (!a || !t.get(a)) continue;
|
|
318
|
+
let s = o.get(a), c = s === void 0 ? -Infinity : e.getComponent(s, u)?.priority ?? -Infinity;
|
|
319
|
+
i.priority >= c && o.set(a, r);
|
|
320
|
+
}
|
|
321
|
+
let c = e.hooks;
|
|
322
|
+
for (let [e, t] of o) {
|
|
323
|
+
let n = i.get(e);
|
|
324
|
+
n === void 0 ? c.callHook("camera:activate", {
|
|
325
|
+
viewportId: e,
|
|
326
|
+
entityId: t
|
|
327
|
+
}) : n !== t && c.callHook("camera:switch", {
|
|
328
|
+
viewportId: e,
|
|
329
|
+
from: n,
|
|
330
|
+
to: t
|
|
331
|
+
}), i.set(e, t);
|
|
332
|
+
}
|
|
333
|
+
for (let [e] of i) o.has(e) || (i.delete(e), c.callHook("camera:deactivate", { viewportId: e }));
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region ../kit/dist/plugin/index.js
|
|
338
|
+
function P(e) {
|
|
339
|
+
function t(t) {
|
|
340
|
+
return e(t);
|
|
341
|
+
}
|
|
342
|
+
return t;
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/camera-core-plugin.ts
|
|
346
|
+
var F = P(() => ({
|
|
347
|
+
name: "camera-core",
|
|
348
|
+
async setup(e) {
|
|
349
|
+
r(e), i(e), await e.use(N);
|
|
350
|
+
}
|
|
351
|
+
}));
|
|
352
|
+
//#endregion
|
|
353
|
+
export { u as Camera, f as CameraBounds, F as CameraCorePlugin, l as CameraEmptyPathError, s as CameraErrorCodes, m as CameraPath, p as CameraShake, N as CameraSystem, c as CameraViewportNotFoundError, d as FollowTarget, g as cameraPathStore, h as cameraViewportMap };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EntityId } from '@gwenjs/core';
|
|
2
|
+
import { Vec3 } from '@gwenjs/math';
|
|
3
|
+
/** Easing function names accepted by CameraWaypoint and BlendOpts. */
|
|
4
|
+
export type EasingName = "linear" | "easeIn" | "easeOut" | "easeInOut" | "easeInQuad" | "easeOutQuad" | "easeInOutQuad" | "easeInCubic" | "easeOutCubic" | "easeInOutCubic";
|
|
5
|
+
/** A single waypoint on a camera travelling path. Shared by camera2d and camera3d. */
|
|
6
|
+
export interface CameraWaypoint {
|
|
7
|
+
/** Target position. 2D cameras use z = 0. */
|
|
8
|
+
position: Vec3;
|
|
9
|
+
/** Target rotation in euler radians. 2D cameras use only z. */
|
|
10
|
+
rotation?: Vec3;
|
|
11
|
+
/**
|
|
12
|
+
* If provided, overrides `rotation` — the camera orients toward this point/entity.
|
|
13
|
+
* Only meaningful for 3D cameras.
|
|
14
|
+
*/
|
|
15
|
+
lookAt?: Vec3 | EntityId;
|
|
16
|
+
/** Target zoom (orthographic cameras). */
|
|
17
|
+
zoom?: number;
|
|
18
|
+
/** Target fov in radians (perspective cameras). */
|
|
19
|
+
fov?: number;
|
|
20
|
+
/** Transition duration in seconds. */
|
|
21
|
+
duration: number;
|
|
22
|
+
easing?: EasingName;
|
|
23
|
+
}
|
|
24
|
+
/** Options passed to `playPath()`. */
|
|
25
|
+
export interface PathOpts {
|
|
26
|
+
loop?: boolean;
|
|
27
|
+
onComplete?: () => void;
|
|
28
|
+
onWaypoint?: (index: number) => void;
|
|
29
|
+
}
|
|
30
|
+
/** Runtime state for a camera path in progress — stored alongside the CameraPath component. */
|
|
31
|
+
export interface CameraPathData {
|
|
32
|
+
waypoints: CameraWaypoint[];
|
|
33
|
+
opts: PathOpts;
|
|
34
|
+
/** Elapsed time in the current segment (seconds). */
|
|
35
|
+
elapsed: number;
|
|
36
|
+
}
|
|
37
|
+
/** Options for blend transitions between cameras. */
|
|
38
|
+
export interface BlendOpts {
|
|
39
|
+
duration: number;
|
|
40
|
+
easing?: EasingName;
|
|
41
|
+
onComplete?: () => void;
|
|
42
|
+
}
|
|
43
|
+
/** Options accepted by `useFollowTarget`. */
|
|
44
|
+
export interface FollowOpts {
|
|
45
|
+
lerp?: number;
|
|
46
|
+
offset?: Vec3;
|
|
47
|
+
/** 3D only: auto-orient toward the target. */
|
|
48
|
+
lookAt?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** Options accepted by `useShake`. */
|
|
51
|
+
export interface ShakeOpts {
|
|
52
|
+
decay?: number;
|
|
53
|
+
maxOffset?: Vec3;
|
|
54
|
+
}
|
|
55
|
+
/** Handle returned by `useShake`. */
|
|
56
|
+
export interface ShakeHandle {
|
|
57
|
+
/** Add trauma [0–1]. Values accumulate up to 1, then decay each frame. */
|
|
58
|
+
trauma(amount: number): void;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,YAAY,GACZ,aAAa,GACb,eAAe,GACf,aAAa,GACb,cAAc,GACd,gBAAgB,CAAC;AAErB,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,QAAQ,EAAE,IAAI,CAAC;IACf,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACzB,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,sCAAsC;AACtC,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED,+FAA+F;AAC/F,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,sCAAsC;AACtC,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gwenjs/camera-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@gwenjs/core": "0.2.0",
|
|
19
|
+
"@gwenjs/math": "0.1.1",
|
|
20
|
+
"@gwenjs/kit": "0.2.0",
|
|
21
|
+
"@gwenjs/renderer-core": "0.2.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^6.0.2",
|
|
25
|
+
"vite": "^8.0.3",
|
|
26
|
+
"vite-plugin-dts": "^4.5.4",
|
|
27
|
+
"vitest": "^4.1.2"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vite build",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "vitest run"
|
|
33
|
+
}
|
|
34
|
+
}
|