reze-engine 0.1.6 → 0.1.7
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 +99 -99
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +308 -307
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +6 -12
- package/package.json +1 -1
- package/src/camera.ts +358 -358
- package/src/engine.ts +1829 -1826
- package/src/math.ts +546 -546
- package/src/model.ts +421 -421
- package/src/physics.ts +680 -680
- package/src/pmx-loader.ts +1054 -1060
package/dist/engine.js
CHANGED
|
@@ -72,226 +72,226 @@ export class Engine {
|
|
|
72
72
|
});
|
|
73
73
|
const shaderModule = this.device.createShaderModule({
|
|
74
74
|
label: "model shaders",
|
|
75
|
-
code: /* wgsl */ `
|
|
76
|
-
struct CameraUniforms {
|
|
77
|
-
view: mat4x4f,
|
|
78
|
-
projection: mat4x4f,
|
|
79
|
-
viewPos: vec3f,
|
|
80
|
-
_padding: f32,
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
struct Light {
|
|
84
|
-
direction: vec3f,
|
|
85
|
-
_padding1: f32,
|
|
86
|
-
color: vec3f,
|
|
87
|
-
intensity: f32,
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
struct LightUniforms {
|
|
91
|
-
ambient: f32,
|
|
92
|
-
lightCount: f32,
|
|
93
|
-
_padding1: f32,
|
|
94
|
-
_padding2: f32,
|
|
95
|
-
lights: array<Light, 4>,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
struct MaterialUniforms {
|
|
99
|
-
alpha: f32,
|
|
100
|
-
_padding1: f32,
|
|
101
|
-
_padding2: f32,
|
|
102
|
-
_padding3: f32,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
struct VertexOutput {
|
|
106
|
-
@builtin(position) position: vec4f,
|
|
107
|
-
@location(0) normal: vec3f,
|
|
108
|
-
@location(1) uv: vec2f,
|
|
109
|
-
@location(2) worldPos: vec3f,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
113
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
114
|
-
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
115
|
-
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
116
|
-
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
117
|
-
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
118
|
-
@group(0) @binding(6) var toonSampler: sampler;
|
|
119
|
-
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
120
|
-
|
|
121
|
-
@vertex fn vs(
|
|
122
|
-
@location(0) position: vec3f,
|
|
123
|
-
@location(1) normal: vec3f,
|
|
124
|
-
@location(2) uv: vec2f,
|
|
125
|
-
@location(3) joints0: vec4<u32>,
|
|
126
|
-
@location(4) weights0: vec4<f32>
|
|
127
|
-
) -> VertexOutput {
|
|
128
|
-
var output: VertexOutput;
|
|
129
|
-
let pos4 = vec4f(position, 1.0);
|
|
130
|
-
|
|
131
|
-
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
132
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
133
|
-
var normalizedWeights: vec4f;
|
|
134
|
-
if (weightSum > 0.0001) {
|
|
135
|
-
normalizedWeights = weights0 / weightSum;
|
|
136
|
-
} else {
|
|
137
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
141
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
142
|
-
for (var i = 0u; i < 4u; i++) {
|
|
143
|
-
let j = joints0[i];
|
|
144
|
-
let w = normalizedWeights[i];
|
|
145
|
-
let m = skinMats[j];
|
|
146
|
-
skinnedPos += (m * pos4) * w;
|
|
147
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
148
|
-
skinnedNrm += (r3 * normal) * w;
|
|
149
|
-
}
|
|
150
|
-
let worldPos = skinnedPos.xyz;
|
|
151
|
-
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
152
|
-
output.normal = normalize(skinnedNrm);
|
|
153
|
-
output.uv = uv;
|
|
154
|
-
output.worldPos = worldPos;
|
|
155
|
-
return output;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
159
|
-
let n = normalize(input.normal);
|
|
160
|
-
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
161
|
-
|
|
162
|
-
var lightAccum = vec3f(light.ambient);
|
|
163
|
-
let numLights = u32(light.lightCount);
|
|
164
|
-
for (var i = 0u; i < numLights; i++) {
|
|
165
|
-
let l = -light.lights[i].direction;
|
|
166
|
-
let nDotL = max(dot(n, l), 0.0);
|
|
167
|
-
let toonUV = vec2f(nDotL, 0.5);
|
|
168
|
-
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
169
|
-
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
170
|
-
lightAccum += toonFactor * radiance * nDotL;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
let color = albedo * lightAccum;
|
|
174
|
-
let finalAlpha = material.alpha;
|
|
175
|
-
if (finalAlpha < 0.001) {
|
|
176
|
-
discard;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), finalAlpha);
|
|
180
|
-
}
|
|
75
|
+
code: /* wgsl */ `
|
|
76
|
+
struct CameraUniforms {
|
|
77
|
+
view: mat4x4f,
|
|
78
|
+
projection: mat4x4f,
|
|
79
|
+
viewPos: vec3f,
|
|
80
|
+
_padding: f32,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
struct Light {
|
|
84
|
+
direction: vec3f,
|
|
85
|
+
_padding1: f32,
|
|
86
|
+
color: vec3f,
|
|
87
|
+
intensity: f32,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
struct LightUniforms {
|
|
91
|
+
ambient: f32,
|
|
92
|
+
lightCount: f32,
|
|
93
|
+
_padding1: f32,
|
|
94
|
+
_padding2: f32,
|
|
95
|
+
lights: array<Light, 4>,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
struct MaterialUniforms {
|
|
99
|
+
alpha: f32,
|
|
100
|
+
_padding1: f32,
|
|
101
|
+
_padding2: f32,
|
|
102
|
+
_padding3: f32,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
struct VertexOutput {
|
|
106
|
+
@builtin(position) position: vec4f,
|
|
107
|
+
@location(0) normal: vec3f,
|
|
108
|
+
@location(1) uv: vec2f,
|
|
109
|
+
@location(2) worldPos: vec3f,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
113
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
114
|
+
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
115
|
+
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
116
|
+
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
117
|
+
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
118
|
+
@group(0) @binding(6) var toonSampler: sampler;
|
|
119
|
+
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
120
|
+
|
|
121
|
+
@vertex fn vs(
|
|
122
|
+
@location(0) position: vec3f,
|
|
123
|
+
@location(1) normal: vec3f,
|
|
124
|
+
@location(2) uv: vec2f,
|
|
125
|
+
@location(3) joints0: vec4<u32>,
|
|
126
|
+
@location(4) weights0: vec4<f32>
|
|
127
|
+
) -> VertexOutput {
|
|
128
|
+
var output: VertexOutput;
|
|
129
|
+
let pos4 = vec4f(position, 1.0);
|
|
130
|
+
|
|
131
|
+
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
132
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
133
|
+
var normalizedWeights: vec4f;
|
|
134
|
+
if (weightSum > 0.0001) {
|
|
135
|
+
normalizedWeights = weights0 / weightSum;
|
|
136
|
+
} else {
|
|
137
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
141
|
+
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
142
|
+
for (var i = 0u; i < 4u; i++) {
|
|
143
|
+
let j = joints0[i];
|
|
144
|
+
let w = normalizedWeights[i];
|
|
145
|
+
let m = skinMats[j];
|
|
146
|
+
skinnedPos += (m * pos4) * w;
|
|
147
|
+
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
148
|
+
skinnedNrm += (r3 * normal) * w;
|
|
149
|
+
}
|
|
150
|
+
let worldPos = skinnedPos.xyz;
|
|
151
|
+
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
152
|
+
output.normal = normalize(skinnedNrm);
|
|
153
|
+
output.uv = uv;
|
|
154
|
+
output.worldPos = worldPos;
|
|
155
|
+
return output;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
159
|
+
let n = normalize(input.normal);
|
|
160
|
+
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
161
|
+
|
|
162
|
+
var lightAccum = vec3f(light.ambient);
|
|
163
|
+
let numLights = u32(light.lightCount);
|
|
164
|
+
for (var i = 0u; i < numLights; i++) {
|
|
165
|
+
let l = -light.lights[i].direction;
|
|
166
|
+
let nDotL = max(dot(n, l), 0.0);
|
|
167
|
+
let toonUV = vec2f(nDotL, 0.5);
|
|
168
|
+
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
169
|
+
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
170
|
+
lightAccum += toonFactor * radiance * nDotL;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
let color = albedo * lightAccum;
|
|
174
|
+
let finalAlpha = material.alpha;
|
|
175
|
+
if (finalAlpha < 0.001) {
|
|
176
|
+
discard;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), finalAlpha);
|
|
180
|
+
}
|
|
181
181
|
`,
|
|
182
182
|
});
|
|
183
183
|
// Create a separate shader for hair-over-eyes that outputs pre-multiplied color for darkening effect
|
|
184
184
|
const hairMultiplyShaderModule = this.device.createShaderModule({
|
|
185
185
|
label: "hair multiply shaders",
|
|
186
|
-
code: /* wgsl */ `
|
|
187
|
-
struct CameraUniforms {
|
|
188
|
-
view: mat4x4f,
|
|
189
|
-
projection: mat4x4f,
|
|
190
|
-
viewPos: vec3f,
|
|
191
|
-
_padding: f32,
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
struct Light {
|
|
195
|
-
direction: vec3f,
|
|
196
|
-
_padding1: f32,
|
|
197
|
-
color: vec3f,
|
|
198
|
-
intensity: f32,
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
struct LightUniforms {
|
|
202
|
-
ambient: f32,
|
|
203
|
-
lightCount: f32,
|
|
204
|
-
_padding1: f32,
|
|
205
|
-
_padding2: f32,
|
|
206
|
-
lights: array<Light, 4>,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
struct MaterialUniforms {
|
|
210
|
-
alpha: f32,
|
|
211
|
-
_padding1: f32,
|
|
212
|
-
_padding2: f32,
|
|
213
|
-
_padding3: f32,
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
struct VertexOutput {
|
|
217
|
-
@builtin(position) position: vec4f,
|
|
218
|
-
@location(0) normal: vec3f,
|
|
219
|
-
@location(1) uv: vec2f,
|
|
220
|
-
@location(2) worldPos: vec3f,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
224
|
-
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
225
|
-
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
226
|
-
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
227
|
-
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
228
|
-
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
229
|
-
@group(0) @binding(6) var toonSampler: sampler;
|
|
230
|
-
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
231
|
-
|
|
232
|
-
@vertex fn vs(
|
|
233
|
-
@location(0) position: vec3f,
|
|
234
|
-
@location(1) normal: vec3f,
|
|
235
|
-
@location(2) uv: vec2f,
|
|
236
|
-
@location(3) joints0: vec4<u32>,
|
|
237
|
-
@location(4) weights0: vec4<f32>
|
|
238
|
-
) -> VertexOutput {
|
|
239
|
-
var output: VertexOutput;
|
|
240
|
-
let pos4 = vec4f(position, 1.0);
|
|
241
|
-
|
|
242
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
243
|
-
var normalizedWeights: vec4f;
|
|
244
|
-
if (weightSum > 0.0001) {
|
|
245
|
-
normalizedWeights = weights0 / weightSum;
|
|
246
|
-
} else {
|
|
247
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
251
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
252
|
-
for (var i = 0u; i < 4u; i++) {
|
|
253
|
-
let j = joints0[i];
|
|
254
|
-
let w = normalizedWeights[i];
|
|
255
|
-
let m = skinMats[j];
|
|
256
|
-
skinnedPos += (m * pos4) * w;
|
|
257
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
258
|
-
skinnedNrm += (r3 * normal) * w;
|
|
259
|
-
}
|
|
260
|
-
let worldPos = skinnedPos.xyz;
|
|
261
|
-
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
262
|
-
output.normal = normalize(skinnedNrm);
|
|
263
|
-
output.uv = uv;
|
|
264
|
-
output.worldPos = worldPos;
|
|
265
|
-
return output;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
269
|
-
let n = normalize(input.normal);
|
|
270
|
-
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
271
|
-
|
|
272
|
-
var lightAccum = vec3f(light.ambient);
|
|
273
|
-
let numLights = u32(light.lightCount);
|
|
274
|
-
for (var i = 0u; i < numLights; i++) {
|
|
275
|
-
let l = -light.lights[i].direction;
|
|
276
|
-
let nDotL = max(dot(n, l), 0.0);
|
|
277
|
-
let toonUV = vec2f(nDotL, 0.5);
|
|
278
|
-
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
279
|
-
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
280
|
-
lightAccum += toonFactor * radiance * nDotL;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
let color = albedo * lightAccum;
|
|
284
|
-
let finalAlpha = material.alpha;
|
|
285
|
-
if (finalAlpha < 0.001) {
|
|
286
|
-
discard;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// For hair-over-eyes effect: simple half-transparent overlay
|
|
290
|
-
// Use
|
|
291
|
-
let overlayAlpha = finalAlpha * 0.
|
|
292
|
-
|
|
293
|
-
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), overlayAlpha);
|
|
294
|
-
}
|
|
186
|
+
code: /* wgsl */ `
|
|
187
|
+
struct CameraUniforms {
|
|
188
|
+
view: mat4x4f,
|
|
189
|
+
projection: mat4x4f,
|
|
190
|
+
viewPos: vec3f,
|
|
191
|
+
_padding: f32,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
struct Light {
|
|
195
|
+
direction: vec3f,
|
|
196
|
+
_padding1: f32,
|
|
197
|
+
color: vec3f,
|
|
198
|
+
intensity: f32,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
struct LightUniforms {
|
|
202
|
+
ambient: f32,
|
|
203
|
+
lightCount: f32,
|
|
204
|
+
_padding1: f32,
|
|
205
|
+
_padding2: f32,
|
|
206
|
+
lights: array<Light, 4>,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
struct MaterialUniforms {
|
|
210
|
+
alpha: f32,
|
|
211
|
+
_padding1: f32,
|
|
212
|
+
_padding2: f32,
|
|
213
|
+
_padding3: f32,
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
struct VertexOutput {
|
|
217
|
+
@builtin(position) position: vec4f,
|
|
218
|
+
@location(0) normal: vec3f,
|
|
219
|
+
@location(1) uv: vec2f,
|
|
220
|
+
@location(2) worldPos: vec3f,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
224
|
+
@group(0) @binding(1) var<uniform> light: LightUniforms;
|
|
225
|
+
@group(0) @binding(2) var diffuseTexture: texture_2d<f32>;
|
|
226
|
+
@group(0) @binding(3) var diffuseSampler: sampler;
|
|
227
|
+
@group(0) @binding(4) var<storage, read> skinMats: array<mat4x4f>;
|
|
228
|
+
@group(0) @binding(5) var toonTexture: texture_2d<f32>;
|
|
229
|
+
@group(0) @binding(6) var toonSampler: sampler;
|
|
230
|
+
@group(0) @binding(7) var<uniform> material: MaterialUniforms;
|
|
231
|
+
|
|
232
|
+
@vertex fn vs(
|
|
233
|
+
@location(0) position: vec3f,
|
|
234
|
+
@location(1) normal: vec3f,
|
|
235
|
+
@location(2) uv: vec2f,
|
|
236
|
+
@location(3) joints0: vec4<u32>,
|
|
237
|
+
@location(4) weights0: vec4<f32>
|
|
238
|
+
) -> VertexOutput {
|
|
239
|
+
var output: VertexOutput;
|
|
240
|
+
let pos4 = vec4f(position, 1.0);
|
|
241
|
+
|
|
242
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
243
|
+
var normalizedWeights: vec4f;
|
|
244
|
+
if (weightSum > 0.0001) {
|
|
245
|
+
normalizedWeights = weights0 / weightSum;
|
|
246
|
+
} else {
|
|
247
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
251
|
+
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
252
|
+
for (var i = 0u; i < 4u; i++) {
|
|
253
|
+
let j = joints0[i];
|
|
254
|
+
let w = normalizedWeights[i];
|
|
255
|
+
let m = skinMats[j];
|
|
256
|
+
skinnedPos += (m * pos4) * w;
|
|
257
|
+
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
258
|
+
skinnedNrm += (r3 * normal) * w;
|
|
259
|
+
}
|
|
260
|
+
let worldPos = skinnedPos.xyz;
|
|
261
|
+
output.position = camera.projection * camera.view * vec4f(worldPos, 1.0);
|
|
262
|
+
output.normal = normalize(skinnedNrm);
|
|
263
|
+
output.uv = uv;
|
|
264
|
+
output.worldPos = worldPos;
|
|
265
|
+
return output;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@fragment fn fs(input: VertexOutput) -> @location(0) vec4f {
|
|
269
|
+
let n = normalize(input.normal);
|
|
270
|
+
let albedo = textureSample(diffuseTexture, diffuseSampler, input.uv).rgb;
|
|
271
|
+
|
|
272
|
+
var lightAccum = vec3f(light.ambient);
|
|
273
|
+
let numLights = u32(light.lightCount);
|
|
274
|
+
for (var i = 0u; i < numLights; i++) {
|
|
275
|
+
let l = -light.lights[i].direction;
|
|
276
|
+
let nDotL = max(dot(n, l), 0.0);
|
|
277
|
+
let toonUV = vec2f(nDotL, 0.5);
|
|
278
|
+
let toonFactor = textureSample(toonTexture, toonSampler, toonUV).rgb;
|
|
279
|
+
let radiance = light.lights[i].color * light.lights[i].intensity;
|
|
280
|
+
lightAccum += toonFactor * radiance * nDotL;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let color = albedo * lightAccum;
|
|
284
|
+
let finalAlpha = material.alpha;
|
|
285
|
+
if (finalAlpha < 0.001) {
|
|
286
|
+
discard;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// For hair-over-eyes effect: simple half-transparent overlay
|
|
290
|
+
// Use 50% opacity to create a semi-transparent hair color overlay
|
|
291
|
+
let overlayAlpha = finalAlpha * 0.5;
|
|
292
|
+
|
|
293
|
+
return vec4f(clamp(color, vec3f(0.0), vec3f(1.0)), overlayAlpha);
|
|
294
|
+
}
|
|
295
295
|
`,
|
|
296
296
|
});
|
|
297
297
|
// Create explicit bind group layout for all pipelines using the main shader
|
|
@@ -383,72 +383,72 @@ export class Engine {
|
|
|
383
383
|
});
|
|
384
384
|
const outlineShaderModule = this.device.createShaderModule({
|
|
385
385
|
label: "outline shaders",
|
|
386
|
-
code: /* wgsl */ `
|
|
387
|
-
struct CameraUniforms {
|
|
388
|
-
view: mat4x4f,
|
|
389
|
-
projection: mat4x4f,
|
|
390
|
-
viewPos: vec3f,
|
|
391
|
-
_padding: f32,
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
struct MaterialUniforms {
|
|
395
|
-
edgeColor: vec4f,
|
|
396
|
-
edgeSize: f32,
|
|
397
|
-
_padding1: f32,
|
|
398
|
-
_padding2: f32,
|
|
399
|
-
_padding3: f32,
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
403
|
-
@group(0) @binding(1) var<uniform> material: MaterialUniforms;
|
|
404
|
-
@group(0) @binding(2) var<storage, read> skinMats: array<mat4x4f>;
|
|
405
|
-
|
|
406
|
-
struct VertexOutput {
|
|
407
|
-
@builtin(position) position: vec4f,
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
@vertex fn vs(
|
|
411
|
-
@location(0) position: vec3f,
|
|
412
|
-
@location(1) normal: vec3f,
|
|
413
|
-
@location(2) uv: vec2f,
|
|
414
|
-
@location(3) joints0: vec4<u32>,
|
|
415
|
-
@location(4) weights0: vec4<f32>
|
|
416
|
-
) -> VertexOutput {
|
|
417
|
-
var output: VertexOutput;
|
|
418
|
-
let pos4 = vec4f(position, 1.0);
|
|
419
|
-
|
|
420
|
-
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
421
|
-
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
422
|
-
var normalizedWeights: vec4f;
|
|
423
|
-
if (weightSum > 0.0001) {
|
|
424
|
-
normalizedWeights = weights0 / weightSum;
|
|
425
|
-
} else {
|
|
426
|
-
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
430
|
-
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
431
|
-
for (var i = 0u; i < 4u; i++) {
|
|
432
|
-
let j = joints0[i];
|
|
433
|
-
let w = normalizedWeights[i];
|
|
434
|
-
let m = skinMats[j];
|
|
435
|
-
skinnedPos += (m * pos4) * w;
|
|
436
|
-
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
437
|
-
skinnedNrm += (r3 * normal) * w;
|
|
438
|
-
}
|
|
439
|
-
let worldPos = skinnedPos.xyz;
|
|
440
|
-
let worldNormal = normalize(skinnedNrm);
|
|
441
|
-
|
|
442
|
-
// MMD invert hull: expand vertices outward along normals
|
|
443
|
-
let scaleFactor = 0.01;
|
|
444
|
-
let expandedPos = worldPos + worldNormal * material.edgeSize * scaleFactor;
|
|
445
|
-
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
446
|
-
return output;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
@fragment fn fs() -> @location(0) vec4f {
|
|
450
|
-
return material.edgeColor;
|
|
451
|
-
}
|
|
386
|
+
code: /* wgsl */ `
|
|
387
|
+
struct CameraUniforms {
|
|
388
|
+
view: mat4x4f,
|
|
389
|
+
projection: mat4x4f,
|
|
390
|
+
viewPos: vec3f,
|
|
391
|
+
_padding: f32,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
struct MaterialUniforms {
|
|
395
|
+
edgeColor: vec4f,
|
|
396
|
+
edgeSize: f32,
|
|
397
|
+
_padding1: f32,
|
|
398
|
+
_padding2: f32,
|
|
399
|
+
_padding3: f32,
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
@group(0) @binding(0) var<uniform> camera: CameraUniforms;
|
|
403
|
+
@group(0) @binding(1) var<uniform> material: MaterialUniforms;
|
|
404
|
+
@group(0) @binding(2) var<storage, read> skinMats: array<mat4x4f>;
|
|
405
|
+
|
|
406
|
+
struct VertexOutput {
|
|
407
|
+
@builtin(position) position: vec4f,
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
@vertex fn vs(
|
|
411
|
+
@location(0) position: vec3f,
|
|
412
|
+
@location(1) normal: vec3f,
|
|
413
|
+
@location(2) uv: vec2f,
|
|
414
|
+
@location(3) joints0: vec4<u32>,
|
|
415
|
+
@location(4) weights0: vec4<f32>
|
|
416
|
+
) -> VertexOutput {
|
|
417
|
+
var output: VertexOutput;
|
|
418
|
+
let pos4 = vec4f(position, 1.0);
|
|
419
|
+
|
|
420
|
+
// Normalize weights to ensure they sum to 1.0 (handles floating-point precision issues)
|
|
421
|
+
let weightSum = weights0.x + weights0.y + weights0.z + weights0.w;
|
|
422
|
+
var normalizedWeights: vec4f;
|
|
423
|
+
if (weightSum > 0.0001) {
|
|
424
|
+
normalizedWeights = weights0 / weightSum;
|
|
425
|
+
} else {
|
|
426
|
+
normalizedWeights = vec4f(1.0, 0.0, 0.0, 0.0);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
var skinnedPos = vec4f(0.0, 0.0, 0.0, 0.0);
|
|
430
|
+
var skinnedNrm = vec3f(0.0, 0.0, 0.0);
|
|
431
|
+
for (var i = 0u; i < 4u; i++) {
|
|
432
|
+
let j = joints0[i];
|
|
433
|
+
let w = normalizedWeights[i];
|
|
434
|
+
let m = skinMats[j];
|
|
435
|
+
skinnedPos += (m * pos4) * w;
|
|
436
|
+
let r3 = mat3x3f(m[0].xyz, m[1].xyz, m[2].xyz);
|
|
437
|
+
skinnedNrm += (r3 * normal) * w;
|
|
438
|
+
}
|
|
439
|
+
let worldPos = skinnedPos.xyz;
|
|
440
|
+
let worldNormal = normalize(skinnedNrm);
|
|
441
|
+
|
|
442
|
+
// MMD invert hull: expand vertices outward along normals
|
|
443
|
+
let scaleFactor = 0.01;
|
|
444
|
+
let expandedPos = worldPos + worldNormal * material.edgeSize * scaleFactor;
|
|
445
|
+
output.position = camera.projection * camera.view * vec4f(expandedPos, 1.0);
|
|
446
|
+
return output;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
@fragment fn fs() -> @location(0) vec4f {
|
|
450
|
+
return material.edgeColor;
|
|
451
|
+
}
|
|
452
452
|
`,
|
|
453
453
|
});
|
|
454
454
|
this.outlinePipeline = this.device.createRenderPipeline({
|
|
@@ -888,31 +888,31 @@ export class Engine {
|
|
|
888
888
|
createSkinMatrixComputePipeline() {
|
|
889
889
|
const computeShader = this.device.createShaderModule({
|
|
890
890
|
label: "skin matrix compute",
|
|
891
|
-
code: /* wgsl */ `
|
|
892
|
-
struct BoneCountUniform {
|
|
893
|
-
count: u32,
|
|
894
|
-
_padding1: u32,
|
|
895
|
-
_padding2: u32,
|
|
896
|
-
_padding3: u32,
|
|
897
|
-
_padding4: vec4<u32>,
|
|
898
|
-
};
|
|
899
|
-
|
|
900
|
-
@group(0) @binding(0) var<uniform> boneCount: BoneCountUniform;
|
|
901
|
-
@group(0) @binding(1) var<storage, read> worldMatrices: array<mat4x4f>;
|
|
902
|
-
@group(0) @binding(2) var<storage, read> inverseBindMatrices: array<mat4x4f>;
|
|
903
|
-
@group(0) @binding(3) var<storage, read_write> skinMatrices: array<mat4x4f>;
|
|
904
|
-
|
|
905
|
-
@compute @workgroup_size(64)
|
|
906
|
-
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
907
|
-
let boneIndex = globalId.x;
|
|
908
|
-
// Bounds check: we dispatch workgroups (64 threads each), so some threads may be out of range
|
|
909
|
-
if (boneIndex >= boneCount.count) {
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
let worldMat = worldMatrices[boneIndex];
|
|
913
|
-
let invBindMat = inverseBindMatrices[boneIndex];
|
|
914
|
-
skinMatrices[boneIndex] = worldMat * invBindMat;
|
|
915
|
-
}
|
|
891
|
+
code: /* wgsl */ `
|
|
892
|
+
struct BoneCountUniform {
|
|
893
|
+
count: u32,
|
|
894
|
+
_padding1: u32,
|
|
895
|
+
_padding2: u32,
|
|
896
|
+
_padding3: u32,
|
|
897
|
+
_padding4: vec4<u32>,
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
@group(0) @binding(0) var<uniform> boneCount: BoneCountUniform;
|
|
901
|
+
@group(0) @binding(1) var<storage, read> worldMatrices: array<mat4x4f>;
|
|
902
|
+
@group(0) @binding(2) var<storage, read> inverseBindMatrices: array<mat4x4f>;
|
|
903
|
+
@group(0) @binding(3) var<storage, read_write> skinMatrices: array<mat4x4f>;
|
|
904
|
+
|
|
905
|
+
@compute @workgroup_size(64)
|
|
906
|
+
fn main(@builtin(global_invocation_id) globalId: vec3<u32>) {
|
|
907
|
+
let boneIndex = globalId.x;
|
|
908
|
+
// Bounds check: we dispatch workgroups (64 threads each), so some threads may be out of range
|
|
909
|
+
if (boneIndex >= boneCount.count) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
let worldMat = worldMatrices[boneIndex];
|
|
913
|
+
let invBindMat = inverseBindMatrices[boneIndex];
|
|
914
|
+
skinMatrices[boneIndex] = worldMat * invBindMat;
|
|
915
|
+
}
|
|
916
916
|
`,
|
|
917
917
|
});
|
|
918
918
|
this.skinMatrixComputePipeline = this.device.createComputePipeline({
|
|
@@ -1389,7 +1389,7 @@ export class Engine {
|
|
|
1389
1389
|
pass.setIndexBuffer(this.indexBuffer, "uint32");
|
|
1390
1390
|
this.drawCallCount = 0;
|
|
1391
1391
|
// === PASS 1: Opaque non-eye, non-hair (face, body, etc) ===
|
|
1392
|
-
this.drawOutlines(pass, false)
|
|
1392
|
+
// this.drawOutlines(pass, false) // Opaque outlines
|
|
1393
1393
|
pass.setPipeline(this.pipeline);
|
|
1394
1394
|
for (const draw of this.opaqueNonEyeNonHairDraws) {
|
|
1395
1395
|
if (draw.count > 0) {
|
|
@@ -1450,6 +1450,7 @@ export class Engine {
|
|
|
1450
1450
|
pass.drawIndexed(draw.count, 1, draw.firstIndex, 0, 0);
|
|
1451
1451
|
}
|
|
1452
1452
|
}
|
|
1453
|
+
this.drawOutlines(pass, false); // Opaque outlines
|
|
1453
1454
|
// === PASS 4: Transparent non-eye, non-hair ===
|
|
1454
1455
|
pass.setPipeline(this.pipeline);
|
|
1455
1456
|
for (const draw of this.transparentNonEyeNonHairDraws) {
|
package/dist/pmx-loader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pmx-loader.d.ts","sourceRoot":"","sources":["../src/pmx-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA+C,MAAM,SAAS,CAAA;AAI5E,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,mBAAmB,CAAI;IAC/B,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,mBAAmB,CAA4B;IACvD,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO;WAIM,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAK9C,OAAO,CAAC,KAAK;IAgBb,OAAO,CAAC,WAAW;IA+CnB,OAAO,CAAC,aAAa;IA+FrB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"pmx-loader.d.ts","sourceRoot":"","sources":["../src/pmx-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA+C,MAAM,SAAS,CAAA;AAI5E,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAU;IACtB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,mBAAmB,CAAI;IAC/B,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,mBAAmB,CAA4B;IACvD,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAc;IAE5B,OAAO;WAIM,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAK9C,OAAO,CAAC,KAAK;IAgBb,OAAO,CAAC,WAAW;IA+CnB,OAAO,CAAC,aAAa;IA+FrB,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IAyFtB,OAAO,CAAC,UAAU;IA2IlB,OAAO,CAAC,UAAU;IAyGlB,OAAO,CAAC,iBAAiB;IAgDzB,OAAO,CAAC,gBAAgB;IAyFxB,OAAO,CAAC,WAAW;IAmGnB,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,OAAO;IA2If,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,cAAc;IAWtB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,OAAO;IAmBf,OAAO,CAAC,QAAQ;CAIjB"}
|