reze-engine 0.9.4 → 0.10.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 +28 -5
- package/dist/animation.d.ts +26 -13
- package/dist/animation.d.ts.map +1 -1
- package/dist/animation.js +95 -35
- package/dist/engine.d.ts +5 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +78 -23
- package/dist/ik-solver.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +8 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +55 -38
- package/package.json +1 -1
- package/src/animation.ts +124 -40
- package/src/engine.ts +121 -69
- package/src/ik-solver.ts +7 -7
- package/src/index.ts +9 -5
- package/src/model.ts +64 -42
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npm install reze-engine
|
|
|
14
14
|
- VMD animation with IK solver and Bullet physics
|
|
15
15
|
- Orbit camera with bone-follow mode
|
|
16
16
|
- GPU picking (double-click/tap)
|
|
17
|
-
- Ground plane with PCF shadow mapping
|
|
17
|
+
- Ground plane with PCF shadow mapping, grid lines, and frosted texture
|
|
18
18
|
- Multi-model support
|
|
19
19
|
|
|
20
20
|
## Quick Start
|
|
@@ -71,13 +71,15 @@ engine.runRenderLoop()
|
|
|
71
71
|
| Method | Description |
|
|
72
72
|
|--------|-------------|
|
|
73
73
|
| `model.loadAnimation(name, url)` | Load VMD animation |
|
|
74
|
-
| `model.
|
|
75
|
-
| `model.
|
|
74
|
+
| `model.loadAnimation(name, clip)` | Load/replace animation clip directly |
|
|
75
|
+
| `model.show(name)` | Set pose at time 0 (resets bones and morphs first) |
|
|
76
|
+
| `model.play(name?)` | Play animation (queued if busy; named play resets bones/morphs first) |
|
|
77
|
+
| `model.play(name, { priority?, loop? })` | Priority-aware play; `loop` wraps at end (`0` default/lowest priority) |
|
|
76
78
|
| `model.pause()` | Pause playback |
|
|
77
79
|
| `model.stop()` | Stop playback |
|
|
78
80
|
| `model.seek(time)` | Seek to time |
|
|
79
|
-
| `model.getAnimationProgress()` | `{ current, duration, percentage, animationName }` |
|
|
80
|
-
| `model.
|
|
81
|
+
| `model.getAnimationProgress()` | `{ current, duration, percentage, animationName, looping, playing, paused }` — `current`/`duration` are seconds |
|
|
82
|
+
| `model.getAnimationClip(name)` | Get loaded clip by name |
|
|
81
83
|
| `model.rotateBones(rotations, ms?)` | Tween bone rotations |
|
|
82
84
|
| `model.moveBones(translations, ms?)` | Tween bone translations |
|
|
83
85
|
| `model.setMorphWeight(name, weight, ms?)` | Tween morph weight |
|
|
@@ -85,6 +87,8 @@ engine.runRenderLoop()
|
|
|
85
87
|
| `model.resetAllMorphs()` | Reset all morph weights |
|
|
86
88
|
| `model.getBoneWorldPosition(name)` | World position of bone |
|
|
87
89
|
|
|
90
|
+
`AnimationClip` is frame-based: `frameCount` is the last keyframe frame index, keyframes store `frame`. Engine playback uses fixed 30 FPS. Looping is controlled via `play(name, { loop: true })`, not on the clip.
|
|
91
|
+
|
|
88
92
|
### Engine Options
|
|
89
93
|
|
|
90
94
|
```javascript
|
|
@@ -108,6 +112,25 @@ engine.runRenderLoop()
|
|
|
108
112
|
|
|
109
113
|
`constraintSolverKeywords` — joints whose name contains any keyword use the Bullet 2.75 constraint solver; all others keep the stable Ammo 2.82+ default. See [babylon-mmd: Fix Constraint Behavior](https://noname0310.github.io/babylon-mmd/docs/reference/runtime/apply-physics-to-mmd-models/#fix-constraint-behavior) for details.
|
|
110
114
|
|
|
115
|
+
### Ground Options
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
engine.addGround({
|
|
119
|
+
width: 100, // ground plane width
|
|
120
|
+
height: 100, // ground plane depth
|
|
121
|
+
diffuseColor: Vec3, // base color (default: 0.8, 0.1, 1.0)
|
|
122
|
+
fadeStart: 5.0, // distance where edge fade begins
|
|
123
|
+
fadeEnd: 60.0, // distance where ground fully fades out
|
|
124
|
+
shadowMapSize: 4096, // shadow map resolution
|
|
125
|
+
shadowStrength: 1.0, // shadow darkness
|
|
126
|
+
gridSpacing: 5.0, // world-space distance between grid lines
|
|
127
|
+
gridLineWidth: 0.012, // thickness of grid lines
|
|
128
|
+
gridLineOpacity: 0.4, // grid line visibility (0–1)
|
|
129
|
+
gridLineColor: Vec3, // grid line color (default: 0.8, 0.8, 0.8)
|
|
130
|
+
noiseStrength: 0.08, // frosted/matte micro-texture intensity
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
111
134
|
## Projects Using This Engine
|
|
112
135
|
|
|
113
136
|
- **[MiKaPo](https://mikapo.vercel.app)** — Real-time motion capture for MMD
|
package/dist/animation.d.ts
CHANGED
|
@@ -15,31 +15,46 @@ export interface BoneKeyframe {
|
|
|
15
15
|
rotation: Quat;
|
|
16
16
|
translation: Vec3;
|
|
17
17
|
interpolation: BoneInterpolation;
|
|
18
|
-
time: number;
|
|
19
18
|
}
|
|
20
19
|
export interface MorphKeyframe {
|
|
21
20
|
morphName: string;
|
|
22
21
|
frame: number;
|
|
23
22
|
weight: number;
|
|
24
|
-
time: number;
|
|
25
23
|
}
|
|
26
24
|
export interface AnimationClip {
|
|
27
25
|
boneTracks: Map<string, BoneKeyframe[]>;
|
|
28
26
|
morphTracks: Map<string, MorphKeyframe[]>;
|
|
29
|
-
|
|
27
|
+
frameCount: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AnimationPlayOptions {
|
|
30
|
+
priority?: number;
|
|
30
31
|
loop?: boolean;
|
|
31
32
|
}
|
|
33
|
+
/** Wall-clock playback progress; `current`/`duration` are seconds (clip span uses `AnimationClip.frameCount`, not `duration`). */
|
|
34
|
+
export interface AnimationProgress {
|
|
35
|
+
animationName: string | null;
|
|
36
|
+
current: number;
|
|
37
|
+
duration: number;
|
|
38
|
+
percentage: number;
|
|
39
|
+
looping: boolean;
|
|
40
|
+
/** True while the timeline is advancing (not idle at end, not paused). */
|
|
41
|
+
playing: boolean;
|
|
42
|
+
paused: boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare const FPS = 30;
|
|
32
45
|
export declare class AnimationState {
|
|
33
46
|
private animations;
|
|
34
47
|
private currentAnimationName;
|
|
35
|
-
private
|
|
48
|
+
private currentFrame;
|
|
49
|
+
private currentPriority;
|
|
50
|
+
private currentLoop;
|
|
36
51
|
private isPlaying;
|
|
37
52
|
private isPaused;
|
|
38
|
-
private
|
|
53
|
+
private nextAnimation;
|
|
39
54
|
private onEnd;
|
|
40
55
|
loadAnimation(name: string, clip: AnimationClip): void;
|
|
41
56
|
removeAnimation(name: string): void;
|
|
42
|
-
play(name: string): boolean;
|
|
57
|
+
play(name: string, options?: AnimationPlayOptions): boolean;
|
|
43
58
|
play(): void;
|
|
44
59
|
update(deltaTime: number): {
|
|
45
60
|
ended: boolean;
|
|
@@ -47,17 +62,15 @@ export declare class AnimationState {
|
|
|
47
62
|
};
|
|
48
63
|
pause(): void;
|
|
49
64
|
stop(): void;
|
|
50
|
-
seek(
|
|
65
|
+
seek(seconds: number): void;
|
|
51
66
|
getCurrentClip(): AnimationClip | null;
|
|
67
|
+
getAnimationClip(name: string): AnimationClip | null;
|
|
52
68
|
getCurrentAnimation(): string | null;
|
|
53
69
|
getCurrentTime(): number;
|
|
70
|
+
getCurrentFrame(): number;
|
|
71
|
+
/** Clip length in seconds (`frameCount / FPS`). */
|
|
54
72
|
getDuration(): number;
|
|
55
|
-
getProgress():
|
|
56
|
-
animationName: string | null;
|
|
57
|
-
current: number;
|
|
58
|
-
duration: number;
|
|
59
|
-
percentage: number;
|
|
60
|
-
};
|
|
73
|
+
getProgress(): AnimationProgress;
|
|
61
74
|
getAnimationNames(): string[];
|
|
62
75
|
hasAnimation(name: string): boolean;
|
|
63
76
|
show(name: string): void;
|
package/dist/animation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAEnC,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,YAAY,EAAE,YAAY,EAAE,CAAA;IAC5B,YAAY,EAAE,YAAY,EAAE,CAAA;IAC5B,YAAY,EAAE,YAAY,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,IAAI,CAAA;IACd,WAAW,EAAE,IAAI,CAAA;IACjB,aAAa,EAAE,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
|
package/dist/animation.js
CHANGED
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
export const FPS = 30;
|
|
2
|
+
// Priority-aware playback: higher priority preempts, otherwise latest request is queued.
|
|
2
3
|
export class AnimationState {
|
|
3
4
|
constructor() {
|
|
4
5
|
this.animations = new Map();
|
|
5
6
|
this.currentAnimationName = null;
|
|
6
|
-
this.
|
|
7
|
+
this.currentFrame = 0;
|
|
8
|
+
this.currentPriority = 0;
|
|
9
|
+
this.currentLoop = false;
|
|
7
10
|
this.isPlaying = false;
|
|
8
11
|
this.isPaused = false;
|
|
9
|
-
this.
|
|
12
|
+
this.nextAnimation = null;
|
|
10
13
|
this.onEnd = null;
|
|
11
14
|
}
|
|
12
15
|
loadAnimation(name, clip) {
|
|
13
|
-
this.animations.set(name,
|
|
16
|
+
this.animations.set(name, {
|
|
17
|
+
boneTracks: clip.boneTracks,
|
|
18
|
+
morphTracks: clip.morphTracks,
|
|
19
|
+
frameCount: clip.frameCount,
|
|
20
|
+
});
|
|
14
21
|
}
|
|
15
22
|
removeAnimation(name) {
|
|
16
23
|
this.animations.delete(name);
|
|
17
24
|
if (this.currentAnimationName === name) {
|
|
18
25
|
this.currentAnimationName = null;
|
|
19
|
-
this.
|
|
26
|
+
this.currentFrame = 0;
|
|
27
|
+
this.currentPriority = 0;
|
|
28
|
+
this.currentLoop = false;
|
|
20
29
|
this.isPlaying = false;
|
|
21
|
-
this.
|
|
30
|
+
this.nextAnimation = this.nextAnimation?.name === name ? null : this.nextAnimation;
|
|
22
31
|
}
|
|
23
|
-
else if (this.
|
|
24
|
-
this.
|
|
32
|
+
else if (this.nextAnimation?.name === name) {
|
|
33
|
+
this.nextAnimation = null;
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
|
-
play(name) {
|
|
36
|
+
play(name, options) {
|
|
28
37
|
if (name === undefined) {
|
|
29
38
|
if (this.currentAnimationName && this.animations.has(this.currentAnimationName)) {
|
|
30
39
|
this.isPaused = false;
|
|
@@ -34,15 +43,37 @@ export class AnimationState {
|
|
|
34
43
|
}
|
|
35
44
|
if (!this.animations.has(name))
|
|
36
45
|
return false;
|
|
46
|
+
const priority = options?.priority ?? 0;
|
|
47
|
+
const loop = options?.loop ?? false;
|
|
48
|
+
if (this.currentAnimationName === name) {
|
|
49
|
+
this.currentFrame = 0;
|
|
50
|
+
this.currentPriority = priority;
|
|
51
|
+
this.currentLoop = loop;
|
|
52
|
+
this.isPlaying = true;
|
|
53
|
+
this.isPaused = false;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
37
56
|
if (this.isPlaying && !this.isPaused) {
|
|
38
|
-
this.
|
|
57
|
+
if (priority > this.currentPriority) {
|
|
58
|
+
this.currentAnimationName = name;
|
|
59
|
+
this.currentFrame = 0;
|
|
60
|
+
this.currentPriority = priority;
|
|
61
|
+
this.currentLoop = loop;
|
|
62
|
+
this.isPlaying = true;
|
|
63
|
+
this.isPaused = false;
|
|
64
|
+
this.nextAnimation = null;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
this.nextAnimation = { name, priority, loop };
|
|
39
68
|
return true;
|
|
40
69
|
}
|
|
41
70
|
this.currentAnimationName = name;
|
|
42
|
-
this.
|
|
71
|
+
this.currentFrame = 0;
|
|
72
|
+
this.currentPriority = priority;
|
|
73
|
+
this.currentLoop = loop;
|
|
43
74
|
this.isPlaying = true;
|
|
44
75
|
this.isPaused = false;
|
|
45
|
-
this.
|
|
76
|
+
this.nextAnimation = null;
|
|
46
77
|
return true;
|
|
47
78
|
}
|
|
48
79
|
update(deltaTime) {
|
|
@@ -52,21 +83,28 @@ export class AnimationState {
|
|
|
52
83
|
const clip = this.animations.get(this.currentAnimationName);
|
|
53
84
|
if (!clip)
|
|
54
85
|
return { ended: false, animationName: this.currentAnimationName };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
const frameCount = clip.frameCount;
|
|
87
|
+
if (frameCount <= 0 || !Number.isFinite(frameCount)) {
|
|
88
|
+
return { ended: false, animationName: this.currentAnimationName };
|
|
89
|
+
}
|
|
90
|
+
this.currentFrame += deltaTime * FPS;
|
|
91
|
+
if (this.currentFrame >= frameCount) {
|
|
92
|
+
if (this.currentLoop) {
|
|
93
|
+
while (this.currentFrame >= frameCount) {
|
|
94
|
+
this.currentFrame -= frameCount;
|
|
95
|
+
}
|
|
61
96
|
return { ended: false, animationName: this.currentAnimationName };
|
|
62
97
|
}
|
|
98
|
+
this.currentFrame = frameCount;
|
|
63
99
|
const finishedName = this.currentAnimationName;
|
|
64
100
|
this.onEnd?.(finishedName);
|
|
65
|
-
if (this.
|
|
66
|
-
const next = this.
|
|
67
|
-
this.
|
|
68
|
-
this.currentAnimationName = next;
|
|
69
|
-
this.
|
|
101
|
+
if (this.nextAnimation !== null) {
|
|
102
|
+
const next = this.nextAnimation;
|
|
103
|
+
this.nextAnimation = null;
|
|
104
|
+
this.currentAnimationName = next.name;
|
|
105
|
+
this.currentFrame = 0;
|
|
106
|
+
this.currentPriority = next.priority;
|
|
107
|
+
this.currentLoop = next.loop;
|
|
70
108
|
this.isPlaying = true;
|
|
71
109
|
this.isPaused = false;
|
|
72
110
|
return { ended: true, animationName: finishedName };
|
|
@@ -82,37 +120,57 @@ export class AnimationState {
|
|
|
82
120
|
stop() {
|
|
83
121
|
this.isPlaying = false;
|
|
84
122
|
this.isPaused = false;
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
123
|
+
this.currentFrame = 0;
|
|
124
|
+
this.currentPriority = 0;
|
|
125
|
+
this.currentLoop = false;
|
|
126
|
+
this.nextAnimation = null;
|
|
87
127
|
}
|
|
88
|
-
|
|
128
|
+
// Seek by absolute timeline seconds, not frame index.
|
|
129
|
+
seek(seconds) {
|
|
89
130
|
const clip = this.getCurrentClip();
|
|
90
|
-
if (!clip)
|
|
131
|
+
if (!clip || clip.frameCount <= 0 || !Number.isFinite(clip.frameCount))
|
|
91
132
|
return;
|
|
92
|
-
|
|
133
|
+
const targetFrame = seconds * FPS;
|
|
134
|
+
this.currentFrame = Math.max(0, Math.min(targetFrame, clip.frameCount));
|
|
93
135
|
}
|
|
94
136
|
getCurrentClip() {
|
|
95
137
|
return this.currentAnimationName !== null ? this.animations.get(this.currentAnimationName) ?? null : null;
|
|
96
138
|
}
|
|
139
|
+
getAnimationClip(name) {
|
|
140
|
+
return this.animations.get(name) ?? null;
|
|
141
|
+
}
|
|
97
142
|
getCurrentAnimation() {
|
|
98
143
|
return this.currentAnimationName;
|
|
99
144
|
}
|
|
100
145
|
getCurrentTime() {
|
|
101
|
-
|
|
146
|
+
const clip = this.getCurrentClip();
|
|
147
|
+
if (!clip)
|
|
148
|
+
return 0;
|
|
149
|
+
return this.currentFrame / FPS;
|
|
150
|
+
}
|
|
151
|
+
getCurrentFrame() {
|
|
152
|
+
return this.currentFrame;
|
|
102
153
|
}
|
|
154
|
+
/** Clip length in seconds (`frameCount / FPS`). */
|
|
103
155
|
getDuration() {
|
|
104
156
|
const clip = this.getCurrentClip();
|
|
105
|
-
|
|
157
|
+
if (!clip || clip.frameCount <= 0 || !Number.isFinite(clip.frameCount))
|
|
158
|
+
return 0;
|
|
159
|
+
return clip.frameCount / FPS;
|
|
106
160
|
}
|
|
107
161
|
getProgress() {
|
|
108
162
|
const clip = this.getCurrentClip();
|
|
109
|
-
const duration = clip ? clip.
|
|
110
|
-
const
|
|
163
|
+
const duration = clip && clip.frameCount > 0 ? clip.frameCount / FPS : 0;
|
|
164
|
+
const current = clip ? this.currentFrame / FPS : 0;
|
|
165
|
+
const percentage = duration > 0 ? (current / duration) * 100 : 0;
|
|
111
166
|
return {
|
|
112
167
|
animationName: this.currentAnimationName,
|
|
113
|
-
current
|
|
168
|
+
current,
|
|
114
169
|
duration,
|
|
115
170
|
percentage,
|
|
171
|
+
looping: this.currentLoop,
|
|
172
|
+
playing: this.isPlaying && !this.isPaused,
|
|
173
|
+
paused: this.isPaused,
|
|
116
174
|
};
|
|
117
175
|
}
|
|
118
176
|
getAnimationNames() {
|
|
@@ -125,10 +183,12 @@ export class AnimationState {
|
|
|
125
183
|
if (!this.animations.has(name))
|
|
126
184
|
return;
|
|
127
185
|
this.currentAnimationName = name;
|
|
128
|
-
this.
|
|
186
|
+
this.currentFrame = 0;
|
|
187
|
+
this.currentPriority = 0;
|
|
188
|
+
this.currentLoop = false;
|
|
129
189
|
this.isPlaying = false;
|
|
130
190
|
this.isPaused = false;
|
|
131
|
-
this.
|
|
191
|
+
this.nextAnimation = null;
|
|
132
192
|
}
|
|
133
193
|
setOnEnd(callback) {
|
|
134
194
|
this.onEnd = callback;
|
package/dist/engine.d.ts
CHANGED
|
@@ -132,7 +132,6 @@ export declare class Engine {
|
|
|
132
132
|
setCameraBeta(b: number): void;
|
|
133
133
|
private setupLighting;
|
|
134
134
|
private setAmbientColor;
|
|
135
|
-
clearLights(): void;
|
|
136
135
|
private addLight;
|
|
137
136
|
addGround(options?: {
|
|
138
137
|
width?: number;
|
|
@@ -142,6 +141,11 @@ export declare class Engine {
|
|
|
142
141
|
fadeEnd?: number;
|
|
143
142
|
shadowMapSize?: number;
|
|
144
143
|
shadowStrength?: number;
|
|
144
|
+
gridSpacing?: number;
|
|
145
|
+
gridLineWidth?: number;
|
|
146
|
+
gridLineOpacity?: number;
|
|
147
|
+
gridLineColor?: Vec3;
|
|
148
|
+
noiseStrength?: number;
|
|
145
149
|
}): void;
|
|
146
150
|
private updateLightBuffer;
|
|
147
151
|
getStats(): EngineStats;
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,IAAI,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;AAEpH,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,CAAC,EAAE,IAAI,CAAA;IACnB,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,oBAAoB,CAAC,EAAE,IAAI,CAAA;CAC5B,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;CAWlC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;CAClB;AA2CD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAsB;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,IAAI,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA;AAExD,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;AAEpH,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY,CAAC,EAAE,IAAI,CAAA;IACnB,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,IAAI,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,oBAAoB,CAAC,EAAE,IAAI,CAAA;CAC5B,CAAA;AAED,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;CAWlC,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;CAClB;AA2CD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAsB;IAE7C,MAAM,CAAC,WAAW,IAAI,MAAM;IAO5B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,kBAAkB,CAAmB;IAC7C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,oBAAoB,CAAoB;IAChD,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,eAAe,CAAoB;IAC3C,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,iCAAiC,CAAqB;IAC9D,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,wBAAwB,CAAe;IAC/C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAI;IAC7C,OAAO,CAAC,oBAAoB,CAA0B;IAGtD,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,iBAAiB,CAAS;IAGlC,OAAO,CAAC,kBAAkB,CAAC,CAAW;IACtC,OAAO,CAAC,iBAAiB,CAAC,CAAW;IACrC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,gBAAgB,CAAC,CAAY;IACrC,OAAO,CAAC,kBAAkB,CAAC,CAAgB;IAC3C,OAAO,CAAC,mBAAmB,CAAoB;IAC/C,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,qBAAqB,CAAC,CAAc;IAC5C,OAAO,CAAC,uBAAuB,CAAa;IAC5C,OAAO,CAAC,0BAA0B,CAAC,CAAW;IAC9C,OAAO,CAAC,cAAc,CAAwB;IAE9C,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,cAAc,CAAwD;IAC9E,OAAO,CAAC,oBAAoB,CAAoD;IAChF,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAM;IAEvC,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,2BAA2B,CAAqB;IACxD,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,qBAAqB,CAAe;IAC5C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,kBAAkB,CAAY;IACtC,OAAO,CAAC,WAAW,CAAwC;IAE3D,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,mBAAmB,CAAI;IAG/B,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,cAAc,CAAO;IAG7B,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,kBAAkB,CAA0B;IAEpD,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,qBAAqB,CAAI;IACjC,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,KAAK,CAGZ;IACD,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,kBAAkB,CAA4B;gBAE1C,MAAM,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,aAAa;IAkBxD,IAAI;IA6BV,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,eAAe;IAumBvB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,YAAY;IAwEpB,OAAO,CAAC,WAAW;IAanB,iFAAiF;IACjF,eAAe,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI;IAC9B,gGAAgG;IAChG,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI;IAoB3E,mIAAmI;IACnI,eAAe,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI;IAY5E,iBAAiB,IAAI,MAAM;IAC3B,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAClC,cAAc,IAAI,MAAM;IACxB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/B,aAAa,IAAI,MAAM;IACvB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAG9B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,QAAQ;IAmBhB,SAAS,CAAC,OAAO,CAAC,EAAE;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,YAAY,CAAC,EAAE,IAAI,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,aAAa,CAAC,EAAE,IAAI,CAAA;QACpB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,GAAG,IAAI;IA4BR,OAAO,CAAC,iBAAiB;IAIzB,QAAQ,IAAI,WAAW;IAIvB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI;IAgBnC,cAAc;IAQd,OAAO;IAkBD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IACvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAUrD,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAc7E,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI/B,aAAa,IAAI,MAAM,EAAE;IAIzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAIpC,qBAAqB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAe9D,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAOnF,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAOpE,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO;IAKnE,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIpC,YAAY,IAAI,OAAO;IAIvB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIzC,iBAAiB,IAAI,OAAO;IAI5B,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,kBAAkB;YAOZ,kBAAkB;IA0GhC,OAAO,CAAC,oBAAoB;IAwE5B,OAAO,CAAC,2BAA2B;IA2CnC,OAAO,CAAC,kBAAkB,CAAO;IACjC,OAAO,CAAC,mBAAmB;YAeb,yBAAyB;IAsFvC,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,oBAAoB;YAId,qBAAqB;IAmCnC,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,uBAAuB,CAI9B;IAED,OAAO,CAAC,iBAAiB,CA0BxB;IAED,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,cAAc;YA6CR,iBAAiB;IAuC/B,MAAM;IA+DN,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,kBAAkB;IAW1B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,WAAW;CAyBpB"}
|
package/dist/engine.js
CHANGED
|
@@ -446,7 +446,12 @@ export class Engine {
|
|
|
446
446
|
struct CameraUniforms { view: mat4x4f, projection: mat4x4f, viewPos: vec3f, _p: f32, };
|
|
447
447
|
struct Light { direction: vec4f, color: vec4f, };
|
|
448
448
|
struct LightUniforms { ambientColor: vec4f, lights: array<Light, 4>, };
|
|
449
|
-
struct GroundShadowMat {
|
|
449
|
+
struct GroundShadowMat {
|
|
450
|
+
diffuseColor: vec3f, fadeStart: f32,
|
|
451
|
+
fadeEnd: f32, shadowStrength: f32, pcfTexel: f32, gridSpacing: f32,
|
|
452
|
+
gridLineWidth: f32, gridLineOpacity: f32, noiseStrength: f32, _pad: f32,
|
|
453
|
+
gridLineColor: vec3f, _pad2: f32,
|
|
454
|
+
};
|
|
450
455
|
struct LightVP { viewProj: mat4x4f, };
|
|
451
456
|
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
452
457
|
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
@@ -454,6 +459,32 @@ export class Engine {
|
|
|
454
459
|
@group(0) @binding(3) var shadowSampler: sampler_comparison;
|
|
455
460
|
@group(0) @binding(4) var<uniform> material: GroundShadowMat;
|
|
456
461
|
@group(0) @binding(5) var<uniform> lightVP: LightVP;
|
|
462
|
+
|
|
463
|
+
// Hash-based noise for frosted/matte surface
|
|
464
|
+
fn hash2(p: vec2f) -> f32 {
|
|
465
|
+
var p3 = fract(vec3f(p.x, p.y, p.x) * 0.1031);
|
|
466
|
+
p3 += dot(p3, vec3f(p3.y + 33.33, p3.z + 33.33, p3.x + 33.33));
|
|
467
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
468
|
+
}
|
|
469
|
+
fn valueNoise(p: vec2f) -> f32 {
|
|
470
|
+
let i = floor(p);
|
|
471
|
+
let f = fract(p);
|
|
472
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
473
|
+
return mix(mix(hash2(i), hash2(i + vec2f(1.0, 0.0)), u.x),
|
|
474
|
+
mix(hash2(i + vec2f(0.0, 1.0)), hash2(i + vec2f(1.0, 1.0)), u.x), u.y);
|
|
475
|
+
}
|
|
476
|
+
fn fbmNoise(p: vec2f) -> f32 {
|
|
477
|
+
var v = 0.0;
|
|
478
|
+
var a = 0.5;
|
|
479
|
+
var pp = p;
|
|
480
|
+
for (var i = 0; i < 4; i++) {
|
|
481
|
+
v += a * valueNoise(pp);
|
|
482
|
+
pp *= 2.0;
|
|
483
|
+
a *= 0.5;
|
|
484
|
+
}
|
|
485
|
+
return v;
|
|
486
|
+
}
|
|
487
|
+
|
|
457
488
|
struct VO { @builtin(position) position: vec4f, @location(0) worldPos: vec3f, @location(1) normal: vec3f, };
|
|
458
489
|
@vertex fn vs(@location(0) position: vec3f, @location(1) normal: vec3f, @location(2) uv: vec2f) -> VO {
|
|
459
490
|
var o: VO; o.worldPos = position; o.normal = normal;
|
|
@@ -463,6 +494,8 @@ export class Engine {
|
|
|
463
494
|
let n = normalize(i.normal);
|
|
464
495
|
let centerDist = length(i.worldPos.xz);
|
|
465
496
|
let edgeFade = 1.0 - smoothstep(0.0, 1.0, clamp((centerDist - material.fadeStart) / max(material.fadeEnd - material.fadeStart, 0.001), 0.0, 1.0));
|
|
497
|
+
|
|
498
|
+
// Shadow sampling
|
|
466
499
|
let lclip = lightVP.viewProj * vec4f(i.worldPos, 1.0);
|
|
467
500
|
let ndc = lclip.xyz / max(lclip.w, 1e-6);
|
|
468
501
|
let suv = vec2f(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
@@ -476,10 +509,26 @@ export class Engine {
|
|
|
476
509
|
}
|
|
477
510
|
}
|
|
478
511
|
vis *= 0.04;
|
|
512
|
+
|
|
513
|
+
// Frosted/matte micro-texture (磨砂)
|
|
514
|
+
let noiseVal = fbmNoise(i.worldPos.xz * 3.0);
|
|
515
|
+
let noiseTint = 1.0 + (noiseVal - 0.5) * material.noiseStrength;
|
|
516
|
+
|
|
517
|
+
// Grid lines — anti-aliased via screen-space derivatives
|
|
518
|
+
let gp = i.worldPos.xz / material.gridSpacing;
|
|
519
|
+
let gridFrac = abs(fract(gp - 0.5) - 0.5);
|
|
520
|
+
let gridDeriv = fwidth(gp);
|
|
521
|
+
let halfLine = material.gridLineWidth * 0.5;
|
|
522
|
+
let gridLine = 1.0 - min(
|
|
523
|
+
smoothstep(halfLine - gridDeriv.x, halfLine + gridDeriv.x, gridFrac.x),
|
|
524
|
+
smoothstep(halfLine - gridDeriv.y, halfLine + gridDeriv.y, gridFrac.y)
|
|
525
|
+
);
|
|
479
526
|
let sun = light.ambientColor.xyz + light.lights[0].color.xyz * light.lights[0].color.w * max(dot(n, -light.lights[0].direction.xyz), 0.0);
|
|
480
527
|
let dark = (1.0 - vis) * material.shadowStrength;
|
|
481
|
-
|
|
482
|
-
|
|
528
|
+
var baseColor = material.diffuseColor * sun * (1.0 - dark * 0.65);
|
|
529
|
+
baseColor *= noiseTint;
|
|
530
|
+
let finalColor = mix(baseColor, material.gridLineColor, gridLine * material.gridLineOpacity * edgeFade);
|
|
531
|
+
return vec4f(finalColor * edgeFade, edgeFade);
|
|
483
532
|
}
|
|
484
533
|
`,
|
|
485
534
|
});
|
|
@@ -574,7 +623,7 @@ export class Engine {
|
|
|
574
623
|
// Screen-stable edgeline: extrusion ∝ camera distance (same idea as MMD viewers / babylon-mmd-style scaling)
|
|
575
624
|
let camDist = max(length(camera.viewPos - worldPos), 0.25);
|
|
576
625
|
let refDist = 30.0;
|
|
577
|
-
let edgeScale = 0.
|
|
626
|
+
let edgeScale = 0.025;
|
|
578
627
|
let expandedPos = worldPos + worldNormal * material.edgeSize * edgeScale * (camDist / refDist);
|
|
579
628
|
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
580
629
|
return output;
|
|
@@ -831,15 +880,6 @@ export class Engine {
|
|
|
831
880
|
this.lightData[3] = this.minSpecularIntensity; // ambientColor.w = minSpecularIntensity
|
|
832
881
|
this.updateLightBuffer();
|
|
833
882
|
}
|
|
834
|
-
clearLights() {
|
|
835
|
-
this.lightCount = 0;
|
|
836
|
-
// Clear all light data by setting intensity to 0
|
|
837
|
-
for (let i = 0; i < 4; i++) {
|
|
838
|
-
const baseIndex = 4 + i * 8;
|
|
839
|
-
this.lightData[baseIndex + 7] = 0; // color.w / intensity
|
|
840
|
-
}
|
|
841
|
-
this.updateLightBuffer();
|
|
842
|
-
}
|
|
843
883
|
addLight(direction, color, intensity = 1.0) {
|
|
844
884
|
if (this.lightCount >= 4)
|
|
845
885
|
return false;
|
|
@@ -859,17 +899,22 @@ export class Engine {
|
|
|
859
899
|
}
|
|
860
900
|
addGround(options) {
|
|
861
901
|
const opts = {
|
|
862
|
-
width:
|
|
863
|
-
height:
|
|
864
|
-
diffuseColor: new Vec3(
|
|
865
|
-
fadeStart:
|
|
866
|
-
fadeEnd:
|
|
902
|
+
width: 160,
|
|
903
|
+
height: 160,
|
|
904
|
+
diffuseColor: new Vec3(0.8, 0.1, 1.0),
|
|
905
|
+
fadeStart: 10.0,
|
|
906
|
+
fadeEnd: 80.0,
|
|
867
907
|
shadowMapSize: 4096,
|
|
868
908
|
shadowStrength: 1.0,
|
|
909
|
+
gridSpacing: 4.2,
|
|
910
|
+
gridLineWidth: 0.012,
|
|
911
|
+
gridLineOpacity: 0.4,
|
|
912
|
+
gridLineColor: new Vec3(0.85, 0.85, 0.85),
|
|
913
|
+
noiseStrength: 0.05,
|
|
869
914
|
...options,
|
|
870
915
|
};
|
|
871
916
|
this.createGroundGeometry(opts.width, opts.height);
|
|
872
|
-
this.createShadowGroundResources(opts
|
|
917
|
+
this.createShadowGroundResources(opts);
|
|
873
918
|
this.hasGround = true;
|
|
874
919
|
this.groundDrawCall = {
|
|
875
920
|
type: "ground",
|
|
@@ -1171,7 +1216,8 @@ export class Engine {
|
|
|
1171
1216
|
});
|
|
1172
1217
|
this.device.queue.writeBuffer(this.groundIndexBuffer, 0, indices);
|
|
1173
1218
|
}
|
|
1174
|
-
createShadowGroundResources(
|
|
1219
|
+
createShadowGroundResources(opts) {
|
|
1220
|
+
const { shadowMapSize, diffuseColor, fadeStart, fadeEnd, shadowStrength, gridSpacing, gridLineWidth, gridLineOpacity, gridLineColor, noiseStrength } = opts;
|
|
1175
1221
|
this.shadowMapTexture = this.device.createTexture({
|
|
1176
1222
|
label: "shadow map",
|
|
1177
1223
|
size: [shadowMapSize, shadowMapSize],
|
|
@@ -1179,7 +1225,8 @@ export class Engine {
|
|
|
1179
1225
|
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
1180
1226
|
});
|
|
1181
1227
|
this.shadowMapDepthView = this.shadowMapTexture.createView();
|
|
1182
|
-
|
|
1228
|
+
// Layout: diffuseColor(3f) fadeStart(1f) | fadeEnd(1f) shadowStrength(1f) pcfTexel(1f) gridSpacing(1f) | gridLineWidth(1f) gridOpacity(1f) noiseStrength(1f) _pad(1f) | gridColor(3f) _pad2(1f)
|
|
1229
|
+
const gb = new Float32Array(16);
|
|
1183
1230
|
gb[0] = diffuseColor.x;
|
|
1184
1231
|
gb[1] = diffuseColor.y;
|
|
1185
1232
|
gb[2] = diffuseColor.z;
|
|
@@ -1187,8 +1234,16 @@ export class Engine {
|
|
|
1187
1234
|
gb[4] = fadeEnd;
|
|
1188
1235
|
gb[5] = shadowStrength;
|
|
1189
1236
|
gb[6] = 1 / shadowMapSize;
|
|
1190
|
-
gb[7] =
|
|
1191
|
-
|
|
1237
|
+
gb[7] = gridSpacing;
|
|
1238
|
+
gb[8] = gridLineWidth;
|
|
1239
|
+
gb[9] = gridLineOpacity;
|
|
1240
|
+
gb[10] = noiseStrength;
|
|
1241
|
+
gb[11] = 0;
|
|
1242
|
+
gb[12] = gridLineColor.x;
|
|
1243
|
+
gb[13] = gridLineColor.y;
|
|
1244
|
+
gb[14] = gridLineColor.z;
|
|
1245
|
+
gb[15] = 0;
|
|
1246
|
+
this.groundShadowMaterialBuffer = this.device.createBuffer({ size: gb.byteLength, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
|
|
1192
1247
|
this.device.queue.writeBuffer(this.groundShadowMaterialBuffer, 0, gb);
|
|
1193
1248
|
this.groundShadowBindGroup = this.device.createBindGroup({
|
|
1194
1249
|
label: "ground shadow bind",
|
package/dist/ik-solver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAoE/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;
|
|
1
|
+
{"version":3,"file":"ik-solver.d.ts","sourceRoot":"","sources":["../src/ik-solver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,IAAI,EAAU,QAAQ,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG7D,MAAM,MAAM,mBAAmB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;AAoE/E,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAuB;IAExD,MAAM,CAAC,KAAK,CACV,SAAS,EAAE,QAAQ,EAAE,EACrB,KAAK,EAAE,IAAI,EAAE,EACb,cAAc,EAAE,IAAI,EAAE,EACtB,iBAAiB,EAAE,IAAI,EAAE,EACzB,aAAa,EAAE,IAAI,EAAE,EACrB,WAAW,EAAE,WAAW,EAAE,EAC1B,iBAAiB,CAAC,EAAE,mBAAmB,GACtC,IAAI;IAMP,OAAO,CAAC,MAAM,CAAC,OAAO;IAoFtB,OAAO,CAAC,MAAM,CAAC,UAAU;IAqGzB,OAAO,CAAC,MAAM,CAAC,UAAU;IAYzB,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAKlC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAmCjC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAQ/B,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAqBvC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAc3C,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAoCjC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Engine, type EngineStats } from "./engine";
|
|
2
2
|
export { Model } from "./model";
|
|
3
3
|
export { Vec3, Quat, Mat4 } from "./math";
|
|
4
|
-
export {
|
|
4
|
+
export type { AnimationClip, AnimationPlayOptions, AnimationProgress, BoneKeyframe, MorphKeyframe, BoneInterpolation, ControlPoint, } from "./animation";
|
|
5
|
+
export { FPS } from "./animation";
|
|
5
6
|
export { Physics, type PhysicsOptions } from "./physics";
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAC/B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
CHANGED