reze-engine 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -91
- package/dist/animation.d.ts +74 -0
- package/dist/animation.d.ts.map +1 -1
- package/dist/animation.js +151 -0
- package/dist/engine.d.ts +24 -32
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +374 -486
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/model.d.ts +35 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +97 -93
- package/package.json +1 -1
- package/src/animation.ts +200 -0
- package/src/engine.ts +393 -592
- package/src/index.ts +6 -0
- package/src/model.ts +124 -106
package/README.md
CHANGED
|
@@ -2,110 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight engine built with WebGPU and TypeScript for real-time 3D anime character MMD model rendering.
|
|
4
4
|
|
|
5
|
-

|
|
6
|
-
|
|
7
5
|
## Features
|
|
8
6
|
|
|
9
|
-
- Blinn-Phong lighting
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- Outlines
|
|
14
|
-
- MSAA 4x anti-aliasing
|
|
15
|
-
- Bone and morph API
|
|
16
|
-
- VMD animation
|
|
17
|
-
- IK solver
|
|
18
|
-
- Ammo/Bullet physics
|
|
7
|
+
- Blinn-Phong lighting, alpha blending, rim lighting, outlines, MSAA 4x
|
|
8
|
+
- Post alpha eye rendering (see-through eyes)
|
|
9
|
+
- Bone and morph API, VMD animation (multiple named, non-interruptible), IK solver, Ammo/Bullet physics
|
|
10
|
+
- Multi-model (per-model materials, IK, physics)
|
|
19
11
|
|
|
20
12
|
## Usage
|
|
21
13
|
|
|
22
14
|
```javascript
|
|
23
15
|
import { Engine, Model } from "reze-engine"
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const initEngine = useCallback(async () => {
|
|
30
|
-
if (canvasRef.current) {
|
|
31
|
-
try {
|
|
32
|
-
const engine = new Engine(canvasRef.current, {})
|
|
33
|
-
engineRef.current = engine
|
|
34
|
-
await engine.init()
|
|
35
|
-
await Model.loadPmx("/models/reze/reze.pmx")
|
|
36
|
-
|
|
37
|
-
engine.runRenderLoop(() => {})
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(error)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}, [])
|
|
43
|
-
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
void (async () => {
|
|
46
|
-
initEngine()
|
|
47
|
-
})()
|
|
48
|
-
|
|
49
|
-
return () => {
|
|
50
|
-
if (engineRef.current) {
|
|
51
|
-
engineRef.current.dispose()
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}, [initEngine])
|
|
55
|
-
|
|
56
|
-
return <canvas ref={canvasRef} className="w-full h-full" />
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Engine options
|
|
61
|
-
|
|
62
|
-
```javascript
|
|
63
|
-
const DEFAULT_ENGINE_OPTIONS: RequiredEngineOptions = {
|
|
64
|
-
ambientColor: new Vec3(0.82, 0.82, 0.82),
|
|
65
|
-
directionalLightIntensity: 0.2,
|
|
66
|
-
minSpecularIntensity: 0.3,
|
|
67
|
-
rimLightIntensity: 0.4,
|
|
68
|
-
cameraDistance: 26.6,
|
|
69
|
-
cameraTarget: new Vec3(0, 12.5, 0),
|
|
70
|
-
cameraFov: Math.PI / 4,
|
|
71
|
-
onRaycast: undefined,
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## API
|
|
76
|
-
|
|
77
|
-
Use **`Model.loadPmx(path)`** after **`await engine.init()`**—the model is registered on the engine automatically.
|
|
78
|
-
|
|
79
|
-
### Animation (model instance)
|
|
80
|
-
|
|
81
|
-
```javascript
|
|
82
|
-
const model = await Model.loadPmx("/models/char.pmx")
|
|
17
|
+
const engine = new Engine(canvas, {})
|
|
18
|
+
await engine.init()
|
|
19
|
+
const model = await Model.loadPmx("/models/reze/reze.pmx")
|
|
83
20
|
await model.loadVmd("/animations/dance.vmd")
|
|
84
21
|
model.playAnimation()
|
|
85
|
-
|
|
86
|
-
model.stopAnimation()
|
|
87
|
-
model.seekAnimation(2.5)
|
|
88
|
-
|
|
89
|
-
const { current, duration, percentage } = model.getAnimationProgress()
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Bones / morphs (model)
|
|
93
|
-
|
|
94
|
-
```javascript
|
|
95
|
-
model.rotateBones({ 首: neckQuat, 頭: headQuat }, 300)
|
|
96
|
-
model.moveBones({ センター: centerVec }, 300)
|
|
97
|
-
model.setMorphWeight("まばたき", 1.0, 300)
|
|
98
|
-
model.resetAllBones()
|
|
99
|
-
model.resetAllMorphs()
|
|
22
|
+
engine.runRenderLoop(() => {})
|
|
100
23
|
```
|
|
101
24
|
|
|
102
|
-
|
|
25
|
+
## API (summary)
|
|
103
26
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
27
|
+
- **Multi-model:** `engine.addModel(model, pmxPath, name?)`, `getModel(name)`, `getModelNames()`, `removeModel(name)`, `setMaterialVisible(modelName, materialName, visible)`, `setModelIKEnabled(modelName, enabled)`, `setModelPhysicsEnabled(modelName, enabled)`, `resetPhysics()`, `markVertexBufferDirty(modelName?)`
|
|
28
|
+
- **Animation:** `model.loadVmd(url)` (loads "default", no auto-play); `model.loadAnimation(name, vmdUrl)`; `model.show(name)`; `model.play()` / `model.play(name)`; `model.pause()`; `model.stop()`; `model.seek(t)`; `model.getAnimationProgress()`. Animations are non-interruptible (next is queued).
|
|
29
|
+
- **Bones / morphs:** `model.rotateBones()`, `model.moveBones()`, `model.setMorphWeight()`, `model.resetAllBones()`, `model.resetAllMorphs()`
|
|
30
|
+
- **Engine:** `runRenderLoop()`, `addGround({ mode: "reflection" | "shadow", ... })`, `onRaycast: (modelName, material, screenX, screenY) => {}`
|
|
109
31
|
|
|
110
32
|
## Projects Using This Engine
|
|
111
33
|
|
package/dist/animation.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Quat, Vec3 } from "./math";
|
|
1
2
|
export interface ControlPoint {
|
|
2
3
|
x: number;
|
|
3
4
|
y: number;
|
|
@@ -8,6 +9,79 @@ export interface BoneInterpolation {
|
|
|
8
9
|
translationY: ControlPoint[];
|
|
9
10
|
translationZ: ControlPoint[];
|
|
10
11
|
}
|
|
12
|
+
export interface BoneKeyframe {
|
|
13
|
+
boneName: string;
|
|
14
|
+
frame: number;
|
|
15
|
+
rotation: Quat;
|
|
16
|
+
translation: Vec3;
|
|
17
|
+
interpolation: BoneInterpolation;
|
|
18
|
+
time: number;
|
|
19
|
+
}
|
|
20
|
+
export interface MorphKeyframe {
|
|
21
|
+
morphName: string;
|
|
22
|
+
frame: number;
|
|
23
|
+
weight: number;
|
|
24
|
+
time: number;
|
|
25
|
+
}
|
|
26
|
+
/** Immutable clip data for one animation (e.g. one VMD). */
|
|
27
|
+
export interface AnimationClip {
|
|
28
|
+
boneTracks: Map<string, BoneKeyframe[]>;
|
|
29
|
+
morphTracks: Map<string, MorphKeyframe[]>;
|
|
30
|
+
duration: number;
|
|
31
|
+
/** When true, clip loops at end. When false, playback stops and onEnd fires. Default false. */
|
|
32
|
+
loop?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Per-model animation state: multiple animations, non-interruptible playback.
|
|
36
|
+
* While one is playing, play(name) queues it to start when the current one finishes.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AnimationState {
|
|
39
|
+
private animations;
|
|
40
|
+
private currentAnimationName;
|
|
41
|
+
private currentTime;
|
|
42
|
+
private isPlaying;
|
|
43
|
+
private isPaused;
|
|
44
|
+
/** When current (non-loop) ends, play this next. Cleared when started. */
|
|
45
|
+
private nextAnimationName;
|
|
46
|
+
private onEnd;
|
|
47
|
+
/** Add or replace an animation by name. Does not start playback. */
|
|
48
|
+
loadAnimation(name: string, clip: AnimationClip): void;
|
|
49
|
+
/** Remove an animation. If it was current, state is cleared. */
|
|
50
|
+
removeAnimation(name: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Start playing an animation by name. Non-interruptible: if one is already playing,
|
|
53
|
+
* this animation is queued to start when the current one finishes.
|
|
54
|
+
*/
|
|
55
|
+
play(name: string): boolean;
|
|
56
|
+
/** Resume current animation (no-op if none). */
|
|
57
|
+
play(): void;
|
|
58
|
+
/** Advance time. When a non-loop clip ends, starts nextAnimationName if set. */
|
|
59
|
+
update(deltaTime: number): {
|
|
60
|
+
ended: boolean;
|
|
61
|
+
animationName: string | null;
|
|
62
|
+
};
|
|
63
|
+
pause(): void;
|
|
64
|
+
stop(): void;
|
|
65
|
+
seek(time: number): void;
|
|
66
|
+
getCurrentClip(): AnimationClip | null;
|
|
67
|
+
getCurrentAnimation(): string | null;
|
|
68
|
+
getCurrentTime(): number;
|
|
69
|
+
getDuration(): number;
|
|
70
|
+
/** Progress of the current animation (time, duration, percentage). */
|
|
71
|
+
getProgress(): {
|
|
72
|
+
animationName: string | null;
|
|
73
|
+
current: number;
|
|
74
|
+
duration: number;
|
|
75
|
+
percentage: number;
|
|
76
|
+
};
|
|
77
|
+
getAnimationNames(): string[];
|
|
78
|
+
hasAnimation(name: string): boolean;
|
|
79
|
+
/** Show animation at time 0 without playing. Use after load when you want to play later (e.g. dance visualization). */
|
|
80
|
+
show(name: string): void;
|
|
81
|
+
setOnEnd(callback: ((animationName: string) => void) | null): void;
|
|
82
|
+
getPlaying(): boolean;
|
|
83
|
+
getPaused(): boolean;
|
|
84
|
+
}
|
|
11
85
|
export declare function bezierInterpolate(x1: number, x2: number, y1: number, y2: number, t: number): number;
|
|
12
86
|
export declare function rawInterpolationToBoneInterpolation(raw: Uint8Array): BoneInterpolation;
|
|
13
87
|
export declare function interpolateControlPoints(cp: ControlPoint[], t: number): number;
|
package/dist/animation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"AAAA,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;AAGD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA0BnG;AAKD,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,UAAU,GAAG,iBAAiB,CAmBtF;AAGD,wBAAgB,wBAAwB,CAAC,EAAE,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ9E"}
|
|
1
|
+
{"version":3,"file":"animation.d.ts","sourceRoot":"","sources":["../src/animation.ts"],"names":[],"mappings":"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"}
|
package/dist/animation.js
CHANGED
|
@@ -1,3 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-model animation state: multiple animations, non-interruptible playback.
|
|
3
|
+
* While one is playing, play(name) queues it to start when the current one finishes.
|
|
4
|
+
*/
|
|
5
|
+
export class AnimationState {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.animations = new Map();
|
|
8
|
+
this.currentAnimationName = null;
|
|
9
|
+
this.currentTime = 0;
|
|
10
|
+
this.isPlaying = false;
|
|
11
|
+
this.isPaused = false;
|
|
12
|
+
/** When current (non-loop) ends, play this next. Cleared when started. */
|
|
13
|
+
this.nextAnimationName = null;
|
|
14
|
+
this.onEnd = null;
|
|
15
|
+
}
|
|
16
|
+
/** Add or replace an animation by name. Does not start playback. */
|
|
17
|
+
loadAnimation(name, clip) {
|
|
18
|
+
this.animations.set(name, clip);
|
|
19
|
+
}
|
|
20
|
+
/** Remove an animation. If it was current, state is cleared. */
|
|
21
|
+
removeAnimation(name) {
|
|
22
|
+
this.animations.delete(name);
|
|
23
|
+
if (this.currentAnimationName === name) {
|
|
24
|
+
this.currentAnimationName = null;
|
|
25
|
+
this.currentTime = 0;
|
|
26
|
+
this.isPlaying = false;
|
|
27
|
+
this.nextAnimationName = this.nextAnimationName === name ? null : this.nextAnimationName;
|
|
28
|
+
}
|
|
29
|
+
else if (this.nextAnimationName === name) {
|
|
30
|
+
this.nextAnimationName = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
play(name) {
|
|
34
|
+
if (name === undefined) {
|
|
35
|
+
if (this.currentAnimationName && this.animations.has(this.currentAnimationName)) {
|
|
36
|
+
this.isPaused = false;
|
|
37
|
+
this.isPlaying = true;
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!this.animations.has(name))
|
|
42
|
+
return false;
|
|
43
|
+
if (this.isPlaying && !this.isPaused) {
|
|
44
|
+
this.nextAnimationName = name;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
this.currentAnimationName = name;
|
|
48
|
+
this.currentTime = 0;
|
|
49
|
+
this.isPlaying = true;
|
|
50
|
+
this.isPaused = false;
|
|
51
|
+
this.nextAnimationName = null;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/** Advance time. When a non-loop clip ends, starts nextAnimationName if set. */
|
|
55
|
+
update(deltaTime) {
|
|
56
|
+
if (!this.isPlaying || this.isPaused || this.currentAnimationName === null) {
|
|
57
|
+
return { ended: false, animationName: this.currentAnimationName };
|
|
58
|
+
}
|
|
59
|
+
const clip = this.animations.get(this.currentAnimationName);
|
|
60
|
+
if (!clip)
|
|
61
|
+
return { ended: false, animationName: this.currentAnimationName };
|
|
62
|
+
this.currentTime += deltaTime;
|
|
63
|
+
const duration = clip.duration;
|
|
64
|
+
if (this.currentTime >= duration) {
|
|
65
|
+
this.currentTime = duration;
|
|
66
|
+
if (clip.loop) {
|
|
67
|
+
this.currentTime = 0;
|
|
68
|
+
return { ended: false, animationName: this.currentAnimationName };
|
|
69
|
+
}
|
|
70
|
+
const finishedName = this.currentAnimationName;
|
|
71
|
+
this.onEnd?.(finishedName);
|
|
72
|
+
if (this.nextAnimationName !== null) {
|
|
73
|
+
const next = this.nextAnimationName;
|
|
74
|
+
this.nextAnimationName = null;
|
|
75
|
+
this.currentAnimationName = next;
|
|
76
|
+
this.currentTime = 0;
|
|
77
|
+
this.isPlaying = true;
|
|
78
|
+
this.isPaused = false;
|
|
79
|
+
return { ended: true, animationName: finishedName };
|
|
80
|
+
}
|
|
81
|
+
this.isPlaying = false;
|
|
82
|
+
return { ended: true, animationName: finishedName };
|
|
83
|
+
}
|
|
84
|
+
return { ended: false, animationName: this.currentAnimationName };
|
|
85
|
+
}
|
|
86
|
+
pause() {
|
|
87
|
+
this.isPaused = true;
|
|
88
|
+
}
|
|
89
|
+
stop() {
|
|
90
|
+
this.isPlaying = false;
|
|
91
|
+
this.isPaused = false;
|
|
92
|
+
this.currentTime = 0;
|
|
93
|
+
this.nextAnimationName = null;
|
|
94
|
+
}
|
|
95
|
+
seek(time) {
|
|
96
|
+
const clip = this.getCurrentClip();
|
|
97
|
+
if (!clip)
|
|
98
|
+
return;
|
|
99
|
+
this.currentTime = Math.max(0, Math.min(time, clip.duration));
|
|
100
|
+
}
|
|
101
|
+
getCurrentClip() {
|
|
102
|
+
return this.currentAnimationName !== null ? this.animations.get(this.currentAnimationName) ?? null : null;
|
|
103
|
+
}
|
|
104
|
+
getCurrentAnimation() {
|
|
105
|
+
return this.currentAnimationName;
|
|
106
|
+
}
|
|
107
|
+
getCurrentTime() {
|
|
108
|
+
return this.currentTime;
|
|
109
|
+
}
|
|
110
|
+
getDuration() {
|
|
111
|
+
const clip = this.getCurrentClip();
|
|
112
|
+
return clip ? clip.duration : 0;
|
|
113
|
+
}
|
|
114
|
+
/** Progress of the current animation (time, duration, percentage). */
|
|
115
|
+
getProgress() {
|
|
116
|
+
const clip = this.getCurrentClip();
|
|
117
|
+
const duration = clip ? clip.duration : 0;
|
|
118
|
+
const percentage = duration > 0 ? (this.currentTime / duration) * 100 : 0;
|
|
119
|
+
return {
|
|
120
|
+
animationName: this.currentAnimationName,
|
|
121
|
+
current: this.currentTime,
|
|
122
|
+
duration,
|
|
123
|
+
percentage,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
getAnimationNames() {
|
|
127
|
+
return Array.from(this.animations.keys());
|
|
128
|
+
}
|
|
129
|
+
hasAnimation(name) {
|
|
130
|
+
return this.animations.has(name);
|
|
131
|
+
}
|
|
132
|
+
/** Show animation at time 0 without playing. Use after load when you want to play later (e.g. dance visualization). */
|
|
133
|
+
show(name) {
|
|
134
|
+
if (!this.animations.has(name))
|
|
135
|
+
return;
|
|
136
|
+
this.currentAnimationName = name;
|
|
137
|
+
this.currentTime = 0;
|
|
138
|
+
this.isPlaying = false;
|
|
139
|
+
this.isPaused = false;
|
|
140
|
+
this.nextAnimationName = null;
|
|
141
|
+
}
|
|
142
|
+
setOnEnd(callback) {
|
|
143
|
+
this.onEnd = callback;
|
|
144
|
+
}
|
|
145
|
+
getPlaying() {
|
|
146
|
+
return this.isPlaying;
|
|
147
|
+
}
|
|
148
|
+
getPaused() {
|
|
149
|
+
return this.isPaused;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
1
152
|
// Cubic bezier in normalized 0–1 space (binary search on x)
|
|
2
153
|
export function bezierInterpolate(x1, x2, y1, y2, t) {
|
|
3
154
|
t = Math.max(0, Math.min(1, t));
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Vec3 } from "./math";
|
|
2
2
|
import { Model } from "./model";
|
|
3
|
-
export type RaycastCallback = (material: string | null, screenX: number, screenY: number) => void;
|
|
3
|
+
export type RaycastCallback = (modelName: string, material: string | null, screenX: number, screenY: number) => void;
|
|
4
4
|
export type EngineOptions = {
|
|
5
5
|
ambientColor?: Vec3;
|
|
6
6
|
directionalLightIntensity?: number;
|
|
@@ -10,8 +10,6 @@ export type EngineOptions = {
|
|
|
10
10
|
cameraTarget?: Vec3;
|
|
11
11
|
cameraFov?: number;
|
|
12
12
|
onRaycast?: RaycastCallback;
|
|
13
|
-
disableIK?: boolean;
|
|
14
|
-
disablePhysics?: boolean;
|
|
15
13
|
multisampleCount?: 1 | 4;
|
|
16
14
|
};
|
|
17
15
|
export type RequiredEngineOptions = Required<Omit<EngineOptions, "onRaycast">> & Pick<EngineOptions, "onRaycast">;
|
|
@@ -36,8 +34,6 @@ export declare class Engine {
|
|
|
36
34
|
private lightUniformBuffer;
|
|
37
35
|
private lightData;
|
|
38
36
|
private lightCount;
|
|
39
|
-
private vertexBuffer;
|
|
40
|
-
private indexBuffer?;
|
|
41
37
|
private resizeObserver;
|
|
42
38
|
private depthTexture;
|
|
43
39
|
private modelPipeline;
|
|
@@ -52,10 +48,6 @@ export declare class Engine {
|
|
|
52
48
|
private hairOutlinePipeline;
|
|
53
49
|
private mainBindGroupLayout;
|
|
54
50
|
private outlineBindGroupLayout;
|
|
55
|
-
private jointsBuffer;
|
|
56
|
-
private weightsBuffer;
|
|
57
|
-
private skinMatrixBuffer?;
|
|
58
|
-
private inverseBindMatrixBuffer?;
|
|
59
51
|
private multisampleTexture;
|
|
60
52
|
private sampleCount;
|
|
61
53
|
private renderPassDescriptor;
|
|
@@ -76,7 +68,6 @@ export declare class Engine {
|
|
|
76
68
|
private shadowMapTexture?;
|
|
77
69
|
private shadowMapDepthView?;
|
|
78
70
|
private shadowDepthPipeline;
|
|
79
|
-
private shadowBindGroup?;
|
|
80
71
|
private shadowLightVPBuffer;
|
|
81
72
|
private shadowLightVPMatrix;
|
|
82
73
|
private groundShadowPipeline;
|
|
@@ -84,25 +75,18 @@ export declare class Engine {
|
|
|
84
75
|
private groundShadowBindGroup?;
|
|
85
76
|
private shadowComparisonSampler;
|
|
86
77
|
private groundShadowMaterialBuffer?;
|
|
87
|
-
private
|
|
78
|
+
private groundDrawCall;
|
|
88
79
|
private shadowVPLightX;
|
|
89
80
|
private shadowVPLightY;
|
|
90
81
|
private shadowVPLightZ;
|
|
91
82
|
private onRaycast?;
|
|
92
|
-
private cachedSkinnedVertices?;
|
|
93
|
-
private cachedSkinMatricesVersion;
|
|
94
|
-
private skinMatricesVersion;
|
|
95
83
|
private lastTouchTime;
|
|
96
84
|
private readonly DOUBLE_TAP_DELAY;
|
|
97
|
-
private
|
|
98
|
-
private _disablePhysics;
|
|
99
|
-
private currentModel;
|
|
100
|
-
private modelDir;
|
|
85
|
+
private modelInstances;
|
|
101
86
|
private materialSampler;
|
|
102
87
|
private textureCache;
|
|
103
|
-
|
|
104
|
-
private
|
|
105
|
-
private hiddenMaterials;
|
|
88
|
+
/** Reusable buffer for raycast skinning to avoid per-instance allocations (Three.js/Babylon.js style). */
|
|
89
|
+
private raycastVertexBuffer;
|
|
106
90
|
private lastFpsUpdate;
|
|
107
91
|
private framesSinceLastUpdate;
|
|
108
92
|
private lastFrameTime;
|
|
@@ -139,23 +123,29 @@ export declare class Engine {
|
|
|
139
123
|
runRenderLoop(callback?: () => void): void;
|
|
140
124
|
stopRenderLoop(): void;
|
|
141
125
|
dispose(): void;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
126
|
+
addModel(model: Model, pmxPath: string, name?: string): Promise<string>;
|
|
127
|
+
registerModel(model: Model, pmxPath: string): Promise<string>;
|
|
128
|
+
removeModel(name: string): void;
|
|
129
|
+
getModelNames(): string[];
|
|
130
|
+
getModel(name: string): Model | null;
|
|
131
|
+
markVertexBufferDirty(modelNameOrModel?: string | Model): void;
|
|
132
|
+
setMaterialVisible(modelName: string, materialName: string, visible: boolean): void;
|
|
133
|
+
toggleMaterialVisible(modelName: string, materialName: string): void;
|
|
134
|
+
isMaterialVisible(modelName: string, materialName: string): boolean;
|
|
135
|
+
setModelIKEnabled(modelName: string, enabled: boolean): void;
|
|
136
|
+
setModelPhysicsEnabled(modelName: string, enabled: boolean): void;
|
|
137
|
+
resetPhysics(): void;
|
|
138
|
+
private instances;
|
|
139
|
+
private forEachInstance;
|
|
140
|
+
private updateInstances;
|
|
151
141
|
private updateVertexBuffer;
|
|
152
|
-
private
|
|
142
|
+
private setupModelInstance;
|
|
153
143
|
private createGroundGeometry;
|
|
154
144
|
private createGroundMaterialBuffer;
|
|
155
145
|
private createReflectionTexture;
|
|
156
146
|
private createShadowGroundResources;
|
|
157
147
|
private updateShadowLightVP;
|
|
158
|
-
private
|
|
148
|
+
private setupMaterialsForInstance;
|
|
159
149
|
private createMaterialUniformBuffer;
|
|
160
150
|
private createUniformBuffer;
|
|
161
151
|
private shouldRenderDrawCall;
|
|
@@ -168,6 +158,8 @@ export declare class Engine {
|
|
|
168
158
|
private handleCanvasTouch;
|
|
169
159
|
private performRaycast;
|
|
170
160
|
render(): void;
|
|
161
|
+
private drawInstanceShadow;
|
|
162
|
+
private renderOneModel;
|
|
171
163
|
private updateCameraUniforms;
|
|
172
164
|
private updateRenderTarget;
|
|
173
165
|
private updateSkinMatrices;
|
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,EAAc,IAAI,EAAE,MAAM,QAAQ,CAAA;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|