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 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.show(name)` | Set pose at time 0 |
75
- | `model.play(name?)` | Play animation (queued if busy) |
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.getAnimationState()` | Access animation controller |
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
@@ -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
- duration: number;
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 currentTime;
48
+ private currentFrame;
49
+ private currentPriority;
50
+ private currentLoop;
36
51
  private isPlaying;
37
52
  private isPaused;
38
- private nextAnimationName;
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(time: number): void;
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;
@@ -1 +1 @@
1
- {"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
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
- // Non-interruptible playback; play(name) while playing queues one next.
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.currentTime = 0;
7
+ this.currentFrame = 0;
8
+ this.currentPriority = 0;
9
+ this.currentLoop = false;
7
10
  this.isPlaying = false;
8
11
  this.isPaused = false;
9
- this.nextAnimationName = null;
12
+ this.nextAnimation = null;
10
13
  this.onEnd = null;
11
14
  }
12
15
  loadAnimation(name, clip) {
13
- this.animations.set(name, clip);
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.currentTime = 0;
26
+ this.currentFrame = 0;
27
+ this.currentPriority = 0;
28
+ this.currentLoop = false;
20
29
  this.isPlaying = false;
21
- this.nextAnimationName = this.nextAnimationName === name ? null : this.nextAnimationName;
30
+ this.nextAnimation = this.nextAnimation?.name === name ? null : this.nextAnimation;
22
31
  }
23
- else if (this.nextAnimationName === name) {
24
- this.nextAnimationName = null;
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.nextAnimationName = name;
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.currentTime = 0;
71
+ this.currentFrame = 0;
72
+ this.currentPriority = priority;
73
+ this.currentLoop = loop;
43
74
  this.isPlaying = true;
44
75
  this.isPaused = false;
45
- this.nextAnimationName = null;
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
- this.currentTime += deltaTime;
56
- const duration = clip.duration;
57
- if (this.currentTime >= duration) {
58
- this.currentTime = duration;
59
- if (clip.loop) {
60
- this.currentTime = 0;
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.nextAnimationName !== null) {
66
- const next = this.nextAnimationName;
67
- this.nextAnimationName = null;
68
- this.currentAnimationName = next;
69
- this.currentTime = 0;
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.currentTime = 0;
86
- this.nextAnimationName = null;
123
+ this.currentFrame = 0;
124
+ this.currentPriority = 0;
125
+ this.currentLoop = false;
126
+ this.nextAnimation = null;
87
127
  }
88
- seek(time) {
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
- this.currentTime = Math.max(0, Math.min(time, clip.duration));
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
- return this.currentTime;
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
- return clip ? clip.duration : 0;
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.duration : 0;
110
- const percentage = duration > 0 ? (this.currentTime / duration) * 100 : 0;
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: this.currentTime,
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.currentTime = 0;
186
+ this.currentFrame = 0;
187
+ this.currentPriority = 0;
188
+ this.currentLoop = false;
129
189
  this.isPlaying = false;
130
190
  this.isPaused = false;
131
- this.nextAnimationName = null;
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;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
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 { diffuseColor: vec3f, fadeStart: f32, fadeEnd: f32, shadowStrength: f32, pcfTexel: f32, _y: f32, };
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
- let lit = material.diffuseColor * sun * (1.0 - dark * 0.65);
482
- return vec4f(lit * edgeFade, edgeFade);
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.032;
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: 100,
863
- height: 100,
864
- diffuseColor: new Vec3(1, 1, 1),
865
- fadeStart: 5.0,
866
- fadeEnd: 60.0,
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.shadowMapSize, opts.diffuseColor, opts.fadeStart, opts.fadeEnd, opts.shadowStrength);
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(shadowMapSize, diffuseColor, fadeStart, fadeEnd, shadowStrength) {
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
- const gb = new Float32Array(8);
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] = 0;
1191
- this.groundShadowMaterialBuffer = this.device.createBuffer({ size: 64, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
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",
@@ -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;WAE1C,KAAK,CACjB,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"}
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 { AnimationState, type AnimationClip, type BoneKeyframe, type MorphKeyframe, } from "./animation";
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
@@ -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,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAA"}
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
@@ -1,5 +1,5 @@
1
1
  export { Engine } from "./engine";
2
2
  export { Model } from "./model";
3
3
  export { Vec3, Quat, Mat4 } from "./math";
4
- export { AnimationState, } from "./animation";
4
+ export { FPS } from "./animation";
5
5
  export { Physics } from "./physics";