reze-engine 0.9.0 → 0.9.1
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 +91 -21
- package/dist/camera.d.ts +2 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +14 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +7 -4
- package/dist/pmx-loader.js +2 -2
- package/package.json +2 -2
- package/src/camera.ts +15 -7
- package/src/engine.ts +7 -4
- package/src/pmx-loader.ts +2 -2
package/README.md
CHANGED
|
@@ -1,44 +1,114 @@
|
|
|
1
1
|
# Reze Engine
|
|
2
2
|
|
|
3
|
-
A lightweight
|
|
3
|
+
A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering, built with TypeScript.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install reze-engine
|
|
9
|
+
```
|
|
4
10
|
|
|
5
11
|
## Features
|
|
6
12
|
|
|
7
13
|
- Blinn-Phong lighting, alpha blending, rim lighting, outlines, MSAA 4x
|
|
8
|
-
-
|
|
9
|
-
-
|
|
14
|
+
- VMD animation (multiple named, non-interruptible), IK solver, Ammo/Bullet physics
|
|
15
|
+
- GPU picking (double-click/tap returns model + material name)
|
|
16
|
+
- Souls-style follow cam (orbit center bound to model bone)
|
|
17
|
+
- Optimized bind groups (per-frame / per-instance / per-material)
|
|
18
|
+
- Ground shadow mapping with PCF
|
|
10
19
|
- Multi-model (per-model materials, IK, physics)
|
|
11
20
|
|
|
12
|
-
##
|
|
21
|
+
## Quick Start
|
|
13
22
|
|
|
14
23
|
```javascript
|
|
15
|
-
import { Engine,
|
|
24
|
+
import { Engine, Vec3 } from "reze-engine"
|
|
16
25
|
|
|
17
|
-
const engine = new Engine(canvas, {
|
|
26
|
+
const engine = new Engine(canvas, {
|
|
27
|
+
ambientColor: new Vec3(0.88, 0.92, 0.99),
|
|
28
|
+
cameraDistance: 31.5,
|
|
29
|
+
})
|
|
18
30
|
await engine.init()
|
|
19
|
-
|
|
20
|
-
await
|
|
21
|
-
model.
|
|
31
|
+
|
|
32
|
+
const model = await engine.loadModel("hero", "/models/hero/hero.pmx")
|
|
33
|
+
await model.loadAnimation("idle", "/animations/idle.vmd")
|
|
34
|
+
model.show("idle")
|
|
22
35
|
model.play()
|
|
23
|
-
|
|
36
|
+
|
|
37
|
+
engine.setCameraFollow(model, "センター", new Vec3(0, 3.5, 0))
|
|
38
|
+
engine.addGround({ width: 160, height: 160 })
|
|
39
|
+
engine.runRenderLoop()
|
|
24
40
|
```
|
|
25
41
|
|
|
26
|
-
## API
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
### Engine
|
|
45
|
+
|
|
46
|
+
| Method | Description |
|
|
47
|
+
|--------|-------------|
|
|
48
|
+
| `new Engine(canvas, options?)` | Create engine with optional config |
|
|
49
|
+
| `engine.init()` | Initialize WebGPU device and context |
|
|
50
|
+
| `engine.loadModel(path)` | Load PMX model (auto-named) |
|
|
51
|
+
| `engine.loadModel(name, path)` | Load PMX model with name |
|
|
52
|
+
| `engine.getModel(name)` | Get model by name |
|
|
53
|
+
| `engine.getModelNames()` | List all model names |
|
|
54
|
+
| `engine.removeModel(name)` | Remove model |
|
|
55
|
+
| `engine.setMaterialVisible(model, mat, visible)` | Show/hide material |
|
|
56
|
+
| `engine.toggleMaterialVisible(model, mat)` | Toggle material visibility |
|
|
57
|
+
| `engine.setIKEnabled(enabled)` | Enable/disable IK globally |
|
|
58
|
+
| `engine.setPhysicsEnabled(enabled)` | Enable/disable physics globally |
|
|
59
|
+
| `engine.resetPhysics()` | Reset physics to current pose |
|
|
60
|
+
| `engine.setCameraFollow(model, bone?, offset?)` | Follow cam bound to bone |
|
|
61
|
+
| `engine.setCameraTarget(vec3)` | Static camera target |
|
|
62
|
+
| `engine.setCameraDistance(d)` | Set orbit radius |
|
|
63
|
+
| `engine.setCameraAlpha(a)` | Set horizontal orbit angle |
|
|
64
|
+
| `engine.setCameraBeta(b)` | Set vertical orbit angle |
|
|
65
|
+
| `engine.addGround(options?)` | Add ground plane with shadows |
|
|
66
|
+
| `engine.runRenderLoop(callback?)` | Start render loop |
|
|
67
|
+
| `engine.stopRenderLoop()` | Stop render loop |
|
|
68
|
+
| `engine.getStats()` | Returns `{ fps, frameTime }` |
|
|
69
|
+
| `engine.dispose()` | Clean up all resources |
|
|
27
70
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
71
|
+
### Model
|
|
72
|
+
|
|
73
|
+
| Method | Description |
|
|
74
|
+
|--------|-------------|
|
|
75
|
+
| `model.loadAnimation(name, url)` | Load VMD animation |
|
|
76
|
+
| `model.show(name)` | Set pose at time 0 |
|
|
77
|
+
| `model.play(name?)` | Play animation (queued if busy) |
|
|
78
|
+
| `model.pause()` | Pause playback |
|
|
79
|
+
| `model.stop()` | Stop playback |
|
|
80
|
+
| `model.seek(time)` | Seek to time |
|
|
81
|
+
| `model.getAnimationProgress()` | `{ current, duration, percentage, animationName }` |
|
|
82
|
+
| `model.getAnimationState()` | Access animation controller |
|
|
83
|
+
| `model.rotateBones(rotations, ms?)` | Tween bone rotations |
|
|
84
|
+
| `model.moveBones(translations, ms?)` | Tween bone translations |
|
|
85
|
+
| `model.setMorphWeight(name, weight, ms?)` | Tween morph weight |
|
|
86
|
+
| `model.resetAllBones()` | Reset to bind pose |
|
|
87
|
+
| `model.resetAllMorphs()` | Reset all morph weights |
|
|
88
|
+
| `model.getBoneWorldPosition(name)` | World position of bone |
|
|
89
|
+
|
|
90
|
+
### Engine Options
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
{
|
|
94
|
+
ambientColor: Vec3,
|
|
95
|
+
directionalLightIntensity: number,
|
|
96
|
+
minSpecularIntensity: number,
|
|
97
|
+
rimLightIntensity: number,
|
|
98
|
+
cameraDistance: number,
|
|
99
|
+
cameraTarget: Vec3,
|
|
100
|
+
cameraFov: number,
|
|
101
|
+
onRaycast: (modelName, material, screenX, screenY) => void,
|
|
102
|
+
}
|
|
103
|
+
```
|
|
32
104
|
|
|
33
105
|
## Projects Using This Engine
|
|
34
106
|
|
|
35
|
-
- **[MiKaPo](https://mikapo.vercel.app)** -
|
|
36
|
-
- **[Popo](https://popo.love)** -
|
|
37
|
-
- **[MPL](https://mmd-mpl.vercel.app)** -
|
|
38
|
-
- **[Mixamo-MMD](https://mixamo-mmd.vercel.app)** - Retarget Mixamo FBX
|
|
107
|
+
- **[MiKaPo](https://mikapo.vercel.app)** - Real-time motion capture for MMD
|
|
108
|
+
- **[Popo](https://popo.love)** - LLM-generated MMD poses
|
|
109
|
+
- **[MPL](https://mmd-mpl.vercel.app)** - Motion programming language for MMD
|
|
110
|
+
- **[Mixamo-MMD](https://mixamo-mmd.vercel.app)** - Retarget Mixamo FBX to VMD
|
|
39
111
|
|
|
40
112
|
## Tutorial
|
|
41
113
|
|
|
42
|
-
Learn WebGPU from scratch by building an anime character renderer in incremental steps. The tutorial covers the complete rendering pipeline from a simple triangle to fully textured, skeletal-animated characters.
|
|
43
|
-
|
|
44
114
|
[How to Render an Anime Character with WebGPU](https://reze.one/tutorial)
|
package/dist/camera.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export declare class Camera {
|
|
|
31
31
|
getViewMatrix(): Mat4;
|
|
32
32
|
private getCameraVectors;
|
|
33
33
|
private panCamera;
|
|
34
|
+
/** Far plane grows with zoom-out so big floors / distant geometry stay visible */
|
|
35
|
+
private updateFarFromRadius;
|
|
34
36
|
getProjectionMatrix(): Mat4;
|
|
35
37
|
attachControl(canvas: HTMLCanvasElement): void;
|
|
36
38
|
detachControl(): void;
|
package/dist/camera.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAMnC,qBAAa,MAAM;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,IAAI,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAI;IAClB,IAAI,EAAE,MAAM,CAAO;IACnB,GAAG,EAAE,MAAM,CAAU;IAGrB,OAAO,CAAC,MAAM,CAAiC;IAC/C,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,oBAAoB,CAAY;IAGxC,kBAAkB,EAAE,MAAM,CAAQ;IAClC,cAAc,EAAE,MAAM,CAAS;IAC/B,cAAc,EAAE,MAAM,CAAO;IAC7B,cAAc,EAAE,MAAM,CAAO;IAC7B,IAAI,EAAE,MAAM,CAAO;IACnB,IAAI,EAAE,MAAM,CAAU;IACtB,cAAc,EAAE,MAAM,CAAQ;IAC9B,cAAc,EAAE,MAAM,CAAkB;gBAE5B,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAE,MAAoB;IAmBhG,WAAW,IAAI,IAAI;IAQnB,aAAa,IAAI,IAAI;IAQrB,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,SAAS;IAiBjB,kFAAkF;IAClF,OAAO,CAAC,mBAAmB;IAK3B,mBAAmB,IAAI,IAAI;IAK3B,aAAa,CAAC,MAAM,EAAE,iBAAiB;IAiBvC,aAAa;IAkBb,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,WAAW;IAgFnB,OAAO,CAAC,UAAU;CA+BnB"}
|
package/dist/camera.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Mat4, Vec3 } from "./math";
|
|
2
|
-
|
|
2
|
+
/** Far cap / zoom limit; large enough for wide shots without clipping distant ground */
|
|
3
|
+
const FAR_CAP = 8000;
|
|
4
|
+
const FAR_MIN = 200;
|
|
3
5
|
export class Camera {
|
|
4
6
|
constructor(alpha, beta, radius, target, fov = Math.PI / 4) {
|
|
5
7
|
this.aspect = 1;
|
|
6
8
|
this.near = 0.05;
|
|
7
|
-
this.far =
|
|
9
|
+
this.far = FAR_CAP;
|
|
8
10
|
// Input state
|
|
9
11
|
this.canvas = null;
|
|
10
12
|
this.isDragging = false;
|
|
@@ -22,7 +24,7 @@ export class Camera {
|
|
|
22
24
|
this.wheelPrecision = 0.01;
|
|
23
25
|
this.pinchPrecision = 0.05;
|
|
24
26
|
this.minZ = 0.05;
|
|
25
|
-
this.maxZ =
|
|
27
|
+
this.maxZ = FAR_CAP;
|
|
26
28
|
this.lowerBetaLimit = 0.001;
|
|
27
29
|
this.upperBetaLimit = Math.PI - 0.001;
|
|
28
30
|
this.alpha = alpha;
|
|
@@ -30,6 +32,7 @@ export class Camera {
|
|
|
30
32
|
this.radius = radius;
|
|
31
33
|
this.target = target;
|
|
32
34
|
this.fov = fov;
|
|
35
|
+
this.updateFarFromRadius();
|
|
33
36
|
// Bind event handlers
|
|
34
37
|
this.onMouseDown = this.onMouseDown.bind(this);
|
|
35
38
|
this.onMouseMove = this.onMouseMove.bind(this);
|
|
@@ -103,7 +106,13 @@ export class Camera {
|
|
|
103
106
|
// Update target position smoothly
|
|
104
107
|
this.target = this.target.add(panRight).add(panUp);
|
|
105
108
|
}
|
|
109
|
+
/** Far plane grows with zoom-out so big floors / distant geometry stay visible */
|
|
110
|
+
updateFarFromRadius() {
|
|
111
|
+
const margin = 600;
|
|
112
|
+
this.far = Math.min(FAR_CAP, Math.max(FAR_MIN, this.radius * 12 + margin));
|
|
113
|
+
}
|
|
106
114
|
getProjectionMatrix() {
|
|
115
|
+
this.updateFarFromRadius();
|
|
107
116
|
return Mat4.perspective(this.fov, this.aspect, this.near, this.far);
|
|
108
117
|
}
|
|
109
118
|
attachControl(canvas) {
|
|
@@ -168,8 +177,7 @@ export class Camera {
|
|
|
168
177
|
this.radius += e.deltaY * this.wheelPrecision;
|
|
169
178
|
// Clamp radius to reasonable bounds
|
|
170
179
|
this.radius = Math.max(this.minZ, Math.min(this.maxZ, this.radius));
|
|
171
|
-
|
|
172
|
-
this.far = Math.max(FAR, this.radius * 4);
|
|
180
|
+
this.updateFarFromRadius();
|
|
173
181
|
}
|
|
174
182
|
onContextMenu(e) {
|
|
175
183
|
e.preventDefault();
|
|
@@ -235,8 +243,7 @@ export class Camera {
|
|
|
235
243
|
this.radius += delta * this.pinchPrecision;
|
|
236
244
|
// Clamp radius to reasonable bounds
|
|
237
245
|
this.radius = Math.max(this.minZ, Math.min(this.maxZ, this.radius));
|
|
238
|
-
|
|
239
|
-
this.far = Math.max(FAR, this.radius * 4);
|
|
246
|
+
this.updateFarFromRadius();
|
|
240
247
|
}
|
|
241
248
|
if (isPanGesture) {
|
|
242
249
|
// Primary gesture is pan (two-finger drag)
|
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;AAI/B,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;CAC5B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;AAEjH,eAAO,MAAM,sBAAsB,EAAE,qBASpC,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;WAE/B,WAAW,IAAI,MAAM;IAOnC,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;IAC9C,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IAEnC,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,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;IAgBjD,IAAI;IA6BjB,OAAO,CAAC,oBAAoB;IA+B5B,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -570,9 +570,11 @@ export class Engine {
|
|
|
570
570
|
}
|
|
571
571
|
let worldPos = skinnedPos.xyz;
|
|
572
572
|
let worldNormal = normalize(skinnedNrm);
|
|
573
|
-
|
|
574
|
-
let
|
|
575
|
-
let
|
|
573
|
+
// Screen-stable edgeline: extrusion ∝ camera distance (same idea as MMD viewers / babylon-mmd-style scaling)
|
|
574
|
+
let camDist = max(length(camera.viewPos - worldPos), 0.25);
|
|
575
|
+
let refDist = 30.0;
|
|
576
|
+
let edgeScale = 0.03;
|
|
577
|
+
let expandedPos = worldPos + worldNormal * material.edgeSize * edgeScale * (camDist / refDist);
|
|
576
578
|
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
577
579
|
return output;
|
|
578
580
|
}
|
|
@@ -591,7 +593,8 @@ export class Engine {
|
|
|
591
593
|
cullMode: "back",
|
|
592
594
|
depthStencil: {
|
|
593
595
|
format: "depth24plus-stencil8",
|
|
594
|
-
|
|
596
|
+
// Don’t write outline into depth buffer — stops z-fighting / black cracks vs body (MMD-style; body depth stays authoritative)
|
|
597
|
+
depthWriteEnabled: false,
|
|
595
598
|
depthCompare: "less-equal",
|
|
596
599
|
},
|
|
597
600
|
});
|
package/dist/pmx-loader.js
CHANGED
|
@@ -217,8 +217,8 @@ export class PmxLoader {
|
|
|
217
217
|
this.getFloat32(),
|
|
218
218
|
this.getFloat32(),
|
|
219
219
|
];
|
|
220
|
-
// edgeSize float
|
|
221
|
-
const edgeSize = this.getFloat32()
|
|
220
|
+
// edgeSize float (outline width in PMX units; engine scales by camera distance for stable screen thickness)
|
|
221
|
+
const edgeSize = this.getFloat32();
|
|
222
222
|
const textureIndex = this.getNonVertexIndex(this.textureIndexSize);
|
|
223
223
|
const sphereTextureIndex = this.getNonVertexIndex(this.textureIndexSize);
|
|
224
224
|
const sphereTextureMode = this.getUint8();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reze-engine",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "A WebGPU
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "A lightweight WebGPU engine for real-time 3D MMD/PMX model rendering",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
package/src/camera.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Mat4, Vec3 } from "./math"
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** Far cap / zoom limit; large enough for wide shots without clipping distant ground */
|
|
4
|
+
const FAR_CAP = 8000
|
|
5
|
+
const FAR_MIN = 200
|
|
4
6
|
|
|
5
7
|
export class Camera {
|
|
6
8
|
alpha: number
|
|
@@ -10,7 +12,7 @@ export class Camera {
|
|
|
10
12
|
fov: number
|
|
11
13
|
aspect: number = 1
|
|
12
14
|
near: number = 0.05
|
|
13
|
-
far: number =
|
|
15
|
+
far: number = FAR_CAP
|
|
14
16
|
|
|
15
17
|
// Input state
|
|
16
18
|
private canvas: HTMLCanvasElement | null = null
|
|
@@ -30,7 +32,7 @@ export class Camera {
|
|
|
30
32
|
wheelPrecision: number = 0.01
|
|
31
33
|
pinchPrecision: number = 0.05
|
|
32
34
|
minZ: number = 0.05
|
|
33
|
-
maxZ: number =
|
|
35
|
+
maxZ: number = FAR_CAP
|
|
34
36
|
lowerBetaLimit: number = 0.001
|
|
35
37
|
upperBetaLimit: number = Math.PI - 0.001
|
|
36
38
|
|
|
@@ -40,6 +42,7 @@ export class Camera {
|
|
|
40
42
|
this.radius = radius
|
|
41
43
|
this.target = target
|
|
42
44
|
this.fov = fov
|
|
45
|
+
this.updateFarFromRadius()
|
|
43
46
|
|
|
44
47
|
// Bind event handlers
|
|
45
48
|
this.onMouseDown = this.onMouseDown.bind(this)
|
|
@@ -127,7 +130,14 @@ export class Camera {
|
|
|
127
130
|
this.target = this.target.add(panRight).add(panUp)
|
|
128
131
|
}
|
|
129
132
|
|
|
133
|
+
/** Far plane grows with zoom-out so big floors / distant geometry stay visible */
|
|
134
|
+
private updateFarFromRadius(): void {
|
|
135
|
+
const margin = 600
|
|
136
|
+
this.far = Math.min(FAR_CAP, Math.max(FAR_MIN, this.radius * 12 + margin))
|
|
137
|
+
}
|
|
138
|
+
|
|
130
139
|
getProjectionMatrix(): Mat4 {
|
|
140
|
+
this.updateFarFromRadius()
|
|
131
141
|
return Mat4.perspective(this.fov, this.aspect, this.near, this.far)
|
|
132
142
|
}
|
|
133
143
|
|
|
@@ -206,8 +216,7 @@ export class Camera {
|
|
|
206
216
|
|
|
207
217
|
// Clamp radius to reasonable bounds
|
|
208
218
|
this.radius = Math.max(this.minZ, Math.min(this.maxZ, this.radius))
|
|
209
|
-
|
|
210
|
-
this.far = Math.max(FAR, this.radius * 4)
|
|
219
|
+
this.updateFarFromRadius()
|
|
211
220
|
}
|
|
212
221
|
|
|
213
222
|
private onContextMenu(e: Event) {
|
|
@@ -285,8 +294,7 @@ export class Camera {
|
|
|
285
294
|
|
|
286
295
|
// Clamp radius to reasonable bounds
|
|
287
296
|
this.radius = Math.max(this.minZ, Math.min(this.maxZ, this.radius))
|
|
288
|
-
|
|
289
|
-
this.far = Math.max(FAR, this.radius * 4)
|
|
297
|
+
this.updateFarFromRadius()
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
if (isPanGesture) {
|
package/src/engine.ts
CHANGED
|
@@ -696,9 +696,11 @@ export class Engine {
|
|
|
696
696
|
}
|
|
697
697
|
let worldPos = skinnedPos.xyz;
|
|
698
698
|
let worldNormal = normalize(skinnedNrm);
|
|
699
|
-
|
|
700
|
-
let
|
|
701
|
-
let
|
|
699
|
+
// Screen-stable edgeline: extrusion ∝ camera distance (same idea as MMD viewers / babylon-mmd-style scaling)
|
|
700
|
+
let camDist = max(length(camera.viewPos - worldPos), 0.25);
|
|
701
|
+
let refDist = 30.0;
|
|
702
|
+
let edgeScale = 0.03;
|
|
703
|
+
let expandedPos = worldPos + worldNormal * material.edgeSize * edgeScale * (camDist / refDist);
|
|
702
704
|
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
703
705
|
return output;
|
|
704
706
|
}
|
|
@@ -718,7 +720,8 @@ export class Engine {
|
|
|
718
720
|
cullMode: "back",
|
|
719
721
|
depthStencil: {
|
|
720
722
|
format: "depth24plus-stencil8",
|
|
721
|
-
|
|
723
|
+
// Don’t write outline into depth buffer — stops z-fighting / black cracks vs body (MMD-style; body depth stays authoritative)
|
|
724
|
+
depthWriteEnabled: false,
|
|
722
725
|
depthCompare: "less-equal",
|
|
723
726
|
},
|
|
724
727
|
})
|
package/src/pmx-loader.ts
CHANGED
|
@@ -261,8 +261,8 @@ export class PmxLoader {
|
|
|
261
261
|
this.getFloat32(),
|
|
262
262
|
this.getFloat32(),
|
|
263
263
|
]
|
|
264
|
-
// edgeSize float
|
|
265
|
-
const edgeSize = this.getFloat32()
|
|
264
|
+
// edgeSize float (outline width in PMX units; engine scales by camera distance for stable screen thickness)
|
|
265
|
+
const edgeSize = this.getFloat32()
|
|
266
266
|
|
|
267
267
|
const textureIndex = this.getNonVertexIndex(this.textureIndexSize)
|
|
268
268
|
const sphereTextureIndex = this.getNonVertexIndex(this.textureIndexSize)
|