matrix-engine-wgpu 1.3.12 → 1.3.13
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/package.json +1 -1
- package/readme.md +12 -0
- package/src/engine/engine.js +8 -1
- package/src/engine/lights.js +126 -0
- package/src/engine/materials.js +34 -0
- package/src/engine/mesh-obj.js +30 -169
- package/src/shaders/fragment.video.wgsl.js +33 -1
- package/src/shaders/fragment.wgsl.js +39 -14
- package/src/world.js +64 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matrix-engine-wgpu",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.13",
|
|
4
4
|
"description": "obj sequence anim +HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
package/readme.md
CHANGED
|
@@ -338,6 +338,18 @@ TEST.loadVideoTexture({
|
|
|
338
338
|
});
|
|
339
339
|
```
|
|
340
340
|
|
|
341
|
+
For canvasinline attach this to arg (example for direct draw on canvas2d and passing intro webgpu pipeline):
|
|
342
|
+
```js
|
|
343
|
+
canvaInlineProgram: (ctx, canvas) => {
|
|
344
|
+
ctx.fillStyle = 'black';
|
|
345
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
346
|
+
ctx.fillStyle = 'white';
|
|
347
|
+
ctx.font = '20px Orbitron';
|
|
348
|
+
ctx.fillText(`FPS: ${Math.round(performance.now() % 60)}`, 10, 30);
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
|
|
341
353
|
<pre>
|
|
342
354
|
| Scenario | Best Approach |
|
|
343
355
|
| ------------------------------ | ---------------------------------- |
|
package/src/engine/engine.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// 'wgpu-matrix' library, so produces many temporary vectors and matrices.
|
|
3
3
|
// This is intentional, as this sample prefers readability over performance.
|
|
4
4
|
import { mat4, vec3 } from 'wgpu-matrix';
|
|
5
|
+
import {LOG_INFO} from './utils';
|
|
5
6
|
// import Input from './input';
|
|
6
7
|
|
|
7
8
|
// // Common interface for camera implementations
|
|
@@ -47,6 +48,10 @@ class CameraBase {
|
|
|
47
48
|
mat4.copy(mat, this.matrix_);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
setProjection(fov = (2 * Math.PI) / 5, aspect = 1, near = 1, far = 1000) {
|
|
52
|
+
this.projectionMatrix = mat4.perspective(fov, aspect, near, far);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
// Returns the camera view matrix
|
|
51
56
|
get view() {
|
|
52
57
|
return this.view_;
|
|
@@ -135,7 +140,9 @@ export class WASDCamera extends CameraBase {
|
|
|
135
140
|
const forward = vec3.normalize(vec3.sub(target, position));
|
|
136
141
|
this.recalculateAngles(forward);
|
|
137
142
|
this.position = position;
|
|
138
|
-
|
|
143
|
+
|
|
144
|
+
this.setProjection()
|
|
145
|
+
console.log(`%cCamera constructor : ${position}`, LOG_INFO);
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {mat4, vec3} from 'wgpu-matrix';
|
|
2
|
+
|
|
3
|
+
export class SpotLight {
|
|
4
|
+
position;
|
|
5
|
+
target;
|
|
6
|
+
up;
|
|
7
|
+
direction;
|
|
8
|
+
|
|
9
|
+
viewMatrix;
|
|
10
|
+
projectionMatrix;
|
|
11
|
+
viewProjMatrix;
|
|
12
|
+
|
|
13
|
+
fov;
|
|
14
|
+
aspect;
|
|
15
|
+
near;
|
|
16
|
+
far;
|
|
17
|
+
|
|
18
|
+
innerCutoff;
|
|
19
|
+
outerCutoff;
|
|
20
|
+
|
|
21
|
+
spotlightUniformBuffer;
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
position = vec3.create(0, 5, 10),
|
|
25
|
+
target = vec3.create(0, 0, 0),
|
|
26
|
+
fov = 45,
|
|
27
|
+
aspect = 1.0,
|
|
28
|
+
near = 0.1,
|
|
29
|
+
far = 100
|
|
30
|
+
) {
|
|
31
|
+
this.position = position;
|
|
32
|
+
this.target = target;
|
|
33
|
+
this.up = vec3.create(0, 1, 0);
|
|
34
|
+
this.direction = vec3.normalize(vec3.subtract(target, position));
|
|
35
|
+
|
|
36
|
+
this.viewMatrix = mat4.lookAt(position, target, this.up);
|
|
37
|
+
this.projectionMatrix = mat4.perspective(
|
|
38
|
+
(fov * Math.PI) / 180,
|
|
39
|
+
aspect,
|
|
40
|
+
near,
|
|
41
|
+
far
|
|
42
|
+
);
|
|
43
|
+
this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
|
|
44
|
+
|
|
45
|
+
this.fov = fov;
|
|
46
|
+
this.aspect = aspect;
|
|
47
|
+
this.near = near;
|
|
48
|
+
this.far = far;
|
|
49
|
+
|
|
50
|
+
this.innerCutoff = Math.cos((Math.PI / 180) * 12.5);
|
|
51
|
+
this.outerCutoff = Math.cos((Math.PI / 180) * 17.5);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
update() {
|
|
55
|
+
this.direction = vec3.normalize(vec3.subtract(this.target, this.position));
|
|
56
|
+
this.viewMatrix = mat4.lookAt(this.position, this.target, this.up);
|
|
57
|
+
this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
|
|
58
|
+
console.log('test light update this.position : ', this.position)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
updateSceneUniforms = (sceneUniformBuffer, camera, inputHandler) => {
|
|
62
|
+
|
|
63
|
+
console.log('test camera.view ', camera.view)
|
|
64
|
+
// Update spotlight matrices
|
|
65
|
+
this.update();
|
|
66
|
+
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
|
|
69
|
+
// Get camera matrices
|
|
70
|
+
camera.update(now, inputHandler()); // Your camera class should also update view/projection
|
|
71
|
+
const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
|
|
72
|
+
|
|
73
|
+
// Prepare float data
|
|
74
|
+
const sceneData = new Float32Array(16 + 16 + 4); // 2 matrices + vec3
|
|
75
|
+
|
|
76
|
+
// Light view-proj matrix
|
|
77
|
+
sceneData.set(this.viewProjMatrix, 0);
|
|
78
|
+
|
|
79
|
+
// Camera view-proj matrix
|
|
80
|
+
sceneData.set(camVP, 16);
|
|
81
|
+
|
|
82
|
+
// Light position (vec3f + padding)
|
|
83
|
+
sceneData.set(this.position, 32);
|
|
84
|
+
|
|
85
|
+
// Write to GPU
|
|
86
|
+
this.device.queue.writeBuffer(
|
|
87
|
+
sceneUniformBuffer,
|
|
88
|
+
0,
|
|
89
|
+
sceneData.buffer,
|
|
90
|
+
sceneData.byteOffset,
|
|
91
|
+
sceneData.byteLength
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
console.log('light.viewProj[0..3]', this.viewProjMatrix.slice(0, 4));
|
|
95
|
+
console.log('camera.vp[0..3]', camVP.slice(0, 4));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
prepareBuffer(device) {
|
|
99
|
+
this.device = device;
|
|
100
|
+
this.spotlightUniformBuffer = device.createBuffer({
|
|
101
|
+
size: 16 * 4, // 64 bytes
|
|
102
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const spotlightData = this.getLightDataBuffer();
|
|
106
|
+
|
|
107
|
+
device.queue.writeBuffer(
|
|
108
|
+
this.spotlightUniformBuffer,
|
|
109
|
+
0,
|
|
110
|
+
spotlightData.buffer,
|
|
111
|
+
spotlightData.byteOffset,
|
|
112
|
+
spotlightData.byteLength
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getLightDataBuffer() {
|
|
117
|
+
return new Float32Array([
|
|
118
|
+
...this.position, 0.0,
|
|
119
|
+
...this.direction, 0.0,
|
|
120
|
+
this.innerCutoff,
|
|
121
|
+
this.outerCutoff,
|
|
122
|
+
0.0,
|
|
123
|
+
0.0,
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/engine/materials.js
CHANGED
|
@@ -22,6 +22,25 @@ export default class Materials {
|
|
|
22
22
|
magFilter: 'linear',
|
|
23
23
|
minFilter: 'linear',
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
// FX effect
|
|
27
|
+
this.postFXModeBuffer = this.device.createBuffer({
|
|
28
|
+
size: 4, // u32 = 4 bytes
|
|
29
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Dymmy buffer
|
|
33
|
+
this.dummySpotlightUniformBuffer = this.device.createBuffer({
|
|
34
|
+
size: 64, // Must match size in shader
|
|
35
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
36
|
+
});
|
|
37
|
+
this.device.queue.writeBuffer(this.dummySpotlightUniformBuffer, 0, new Float32Array(16));
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
updatePostFXMode(mode) {
|
|
42
|
+
const arrayBuffer = new Uint32Array([mode]);
|
|
43
|
+
this.device.queue.writeBuffer(this.postFXModeBuffer, 0, arrayBuffer);
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
async loadTex0(texturesPaths) {
|
|
@@ -190,6 +209,7 @@ export default class Materials {
|
|
|
190
209
|
binding: 4,
|
|
191
210
|
resource: this.videoSampler,
|
|
192
211
|
},
|
|
212
|
+
{binding: 5, resource: {buffer: this.postFXModeBuffer}}
|
|
193
213
|
],
|
|
194
214
|
});
|
|
195
215
|
} else {
|
|
@@ -216,6 +236,10 @@ export default class Materials {
|
|
|
216
236
|
binding: 4,
|
|
217
237
|
resource: this.imageSampler,
|
|
218
238
|
},
|
|
239
|
+
{
|
|
240
|
+
binding: 5,
|
|
241
|
+
resource: {buffer: this.lightContainer.length == 0 ? this.dummySpotlightUniformBuffer : this.lightContainer[0].spotlightUniformBuffer},
|
|
242
|
+
},
|
|
219
243
|
],
|
|
220
244
|
});
|
|
221
245
|
}
|
|
@@ -251,6 +275,11 @@ export default class Materials {
|
|
|
251
275
|
visibility: GPUShaderStage.FRAGMENT,
|
|
252
276
|
sampler: {type: 'filtering'}, // for video sampling
|
|
253
277
|
},
|
|
278
|
+
{
|
|
279
|
+
binding: 5,
|
|
280
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
281
|
+
buffer: {type: 'uniform'},
|
|
282
|
+
}
|
|
254
283
|
]
|
|
255
284
|
: [ // IMAGE
|
|
256
285
|
{
|
|
@@ -266,6 +295,11 @@ export default class Materials {
|
|
|
266
295
|
visibility: GPUShaderStage.FRAGMENT,
|
|
267
296
|
sampler: {type: 'filtering'},
|
|
268
297
|
},
|
|
298
|
+
{
|
|
299
|
+
binding: 5,
|
|
300
|
+
visibility: GPUShaderStage.FRAGMENT,
|
|
301
|
+
buffer: {type: 'uniform'},
|
|
302
|
+
}
|
|
269
303
|
])
|
|
270
304
|
],
|
|
271
305
|
});
|
package/src/engine/mesh-obj.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {mat4, vec3} from 'wgpu-matrix';
|
|
2
2
|
import {Position, Rotation} from "./matrix-class";
|
|
3
|
-
import {createInputHandler} from "./engine";
|
|
4
3
|
import {vertexShadowWGSL} from '../shaders/vertexShadow.wgsl';
|
|
5
4
|
import {fragmentWGSL} from '../shaders/fragment.wgsl';
|
|
6
5
|
import {vertexWGSL} from '../shaders/vertex.wgsl';
|
|
7
6
|
import {degToRad, genName, LOG_FUNNY_SMALL} from './utils';
|
|
8
7
|
import Materials from './materials';
|
|
9
8
|
import {fragmentVideoWGSL} from '../shaders/fragment.video.wgsl';
|
|
9
|
+
import {createInputHandler} from './engine';
|
|
10
10
|
|
|
11
11
|
export default class MEMeshObj extends Materials {
|
|
12
|
-
constructor(canvas, device, context, o) {
|
|
12
|
+
constructor(canvas, device, context, o, sceneUniformBuffer) {
|
|
13
13
|
super(device);
|
|
14
14
|
if(typeof o.name === 'undefined') o.name = genName(9);
|
|
15
15
|
if(typeof o.raycast === 'undefined') {
|
|
@@ -30,6 +30,9 @@ export default class MEMeshObj extends Materials {
|
|
|
30
30
|
// comes from engine not from args
|
|
31
31
|
this.clearColor = "red";
|
|
32
32
|
|
|
33
|
+
|
|
34
|
+
// this.lightTest = new SpotLight();
|
|
35
|
+
// this.lightTest.prepareBuffer(this.device);
|
|
33
36
|
this.video = null;
|
|
34
37
|
|
|
35
38
|
// Mesh stuff - for single mesh or t-posed (fiktive-first in loading order)
|
|
@@ -47,7 +50,7 @@ export default class MEMeshObj extends Materials {
|
|
|
47
50
|
this.drawElements = this.drawElementsAnim;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
this.inputHandler =
|
|
53
|
+
this.inputHandler = null;
|
|
51
54
|
this.cameras = o.cameras;
|
|
52
55
|
|
|
53
56
|
this.mainCameraParams = {
|
|
@@ -55,14 +58,10 @@ export default class MEMeshObj extends Materials {
|
|
|
55
58
|
responseCoef: o.mainCameraParams.responseCoef
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
// touchCoordinate.enabled = true;
|
|
59
|
-
|
|
60
61
|
this.lastFrameMS = 0;
|
|
61
62
|
this.texturesPaths = [];
|
|
62
63
|
o.texturesPaths.forEach((t) => {this.texturesPaths.push(t)})
|
|
63
|
-
|
|
64
64
|
this.presentationFormat = navigator.gpu.getPreferredCanvasFormat();
|
|
65
|
-
|
|
66
65
|
this.position = new Position(o.position.x, o.position.y, o.position.z);
|
|
67
66
|
this.rotation = new Rotation(o.rotation.x, o.rotation.y, o.rotation.z);
|
|
68
67
|
this.rotation.rotationSpeed.x = o.rotationSpeed.x;
|
|
@@ -74,7 +73,7 @@ export default class MEMeshObj extends Materials {
|
|
|
74
73
|
return new Promise(async (resolve) => {
|
|
75
74
|
this.shadowDepthTextureSize = 1024;
|
|
76
75
|
const aspect = canvas.width / canvas.height;
|
|
77
|
-
this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 2000.0);
|
|
76
|
+
// this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 2000.0);
|
|
78
77
|
this.modelViewProjectionMatrix = mat4.create();
|
|
79
78
|
// console.log('cube added texturesPaths: ', this.texturesPaths)
|
|
80
79
|
this.loadTex0(this.texturesPaths).then(() => {
|
|
@@ -235,43 +234,13 @@ export default class MEMeshObj extends Materials {
|
|
|
235
234
|
// Create a bind group layout which holds the scene uniforms and
|
|
236
235
|
// the texture+sampler for depth. We create it manually because the WebPU
|
|
237
236
|
// implementation doesn't infer this from the shader (yet).
|
|
238
|
-
this.createLayoutForRender()
|
|
239
|
-
|
|
237
|
+
this.createLayoutForRender();
|
|
240
238
|
this.setupPipeline();
|
|
241
|
-
// this.pipeline = this.device.createRenderPipeline({
|
|
242
|
-
// layout: this.device.createPipelineLayout({
|
|
243
|
-
// bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout],
|
|
244
|
-
// }),
|
|
245
|
-
// vertex: {
|
|
246
|
-
// module: this.device.createShaderModule({
|
|
247
|
-
// code: vertexWGSL,
|
|
248
|
-
// }),
|
|
249
|
-
// buffers: this.vertexBuffers,
|
|
250
|
-
// },
|
|
251
|
-
// fragment: {
|
|
252
|
-
// module: this.device.createShaderModule({
|
|
253
|
-
// code: fragmentWGSL,
|
|
254
|
-
// }),
|
|
255
|
-
// targets: [
|
|
256
|
-
// {
|
|
257
|
-
// format: presentationFormat,
|
|
258
|
-
// },
|
|
259
|
-
// ],
|
|
260
|
-
// constants: {
|
|
261
|
-
// shadowDepthTextureSize: this.shadowDepthTextureSize,
|
|
262
|
-
// },
|
|
263
|
-
// },
|
|
264
|
-
// depthStencil: {
|
|
265
|
-
// depthWriteEnabled: true,
|
|
266
|
-
// depthCompare: 'less',
|
|
267
|
-
// format: 'depth24plus-stencil8',
|
|
268
|
-
// },
|
|
269
|
-
// primitive,
|
|
270
|
-
// });
|
|
271
239
|
|
|
272
240
|
const depthTexture = this.device.createTexture({
|
|
273
241
|
size: [canvas.width, canvas.height],
|
|
274
|
-
format: 'depth24plus-stencil8',
|
|
242
|
+
// format: 'depth24plus-stencil8',
|
|
243
|
+
format: 'depth24plus',
|
|
275
244
|
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
276
245
|
});
|
|
277
246
|
|
|
@@ -290,9 +259,9 @@ export default class MEMeshObj extends Materials {
|
|
|
290
259
|
depthClearValue: 1.0,
|
|
291
260
|
depthLoadOp: 'clear',
|
|
292
261
|
depthStoreOp: 'store',
|
|
293
|
-
stencilClearValue: 0,
|
|
294
|
-
stencilLoadOp: 'clear',
|
|
295
|
-
stencilStoreOp: 'store',
|
|
262
|
+
// stencilClearValue: 0,
|
|
263
|
+
// stencilLoadOp: 'clear',
|
|
264
|
+
// stencilStoreOp: 'store',
|
|
296
265
|
},
|
|
297
266
|
};
|
|
298
267
|
|
|
@@ -309,6 +278,9 @@ export default class MEMeshObj extends Materials {
|
|
|
309
278
|
size: 2 * 4 * 16 + 4 * 4,
|
|
310
279
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
311
280
|
});
|
|
281
|
+
// cant be global
|
|
282
|
+
// console.log('sceneUniformBuffer', sceneUniformBuffer)
|
|
283
|
+
// this.sceneUniformBuffer = sceneUniformBuffer;
|
|
312
284
|
|
|
313
285
|
this.sceneBindGroupForShadow = this.device.createBindGroup({
|
|
314
286
|
layout: this.uniformBufferBindGroupLayout,
|
|
@@ -322,7 +294,6 @@ export default class MEMeshObj extends Materials {
|
|
|
322
294
|
],
|
|
323
295
|
});
|
|
324
296
|
|
|
325
|
-
// --------------------------
|
|
326
297
|
this.createBindGroupForRender();
|
|
327
298
|
|
|
328
299
|
this.modelBindGroup = this.device.createBindGroup({
|
|
@@ -364,7 +335,9 @@ export default class MEMeshObj extends Materials {
|
|
|
364
335
|
mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
|
|
365
336
|
// console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
|
|
366
337
|
}
|
|
367
|
-
|
|
338
|
+
|
|
339
|
+
// console.info('NOT camera.projectionMatrix: ', camera.projectionMatrix )
|
|
340
|
+
mat4.multiply(camera.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
|
|
368
341
|
return this.modelViewProjectionMatrix;
|
|
369
342
|
}
|
|
370
343
|
|
|
@@ -387,62 +360,16 @@ export default class MEMeshObj extends Materials {
|
|
|
387
360
|
return modelMatrix;
|
|
388
361
|
};
|
|
389
362
|
|
|
390
|
-
this.upVector = vec3.fromValues(0, 1, 0);
|
|
391
|
-
this.origin = vec3.fromValues(0, 0, 0);
|
|
392
|
-
|
|
393
|
-
this.lightPosition = vec3.fromValues(0, 0, 0);
|
|
394
|
-
this.lightViewMatrix = mat4.lookAt(this.lightPosition, this.origin, this.upVector);
|
|
395
|
-
const lightProjectionMatrix = mat4.create();
|
|
396
|
-
|
|
397
|
-
var myLMargin = 100;
|
|
398
|
-
{
|
|
399
|
-
const left = -myLMargin;
|
|
400
|
-
const right = myLMargin;
|
|
401
|
-
const bottom = -myLMargin;
|
|
402
|
-
const top = myLMargin;
|
|
403
|
-
const near = -200;
|
|
404
|
-
const far = 300;
|
|
405
|
-
mat4.ortho(left, right, bottom, top, near, far, lightProjectionMatrix);
|
|
406
|
-
// test
|
|
407
|
-
// mat4.ortho(right, left, top, bottom, near, far, lightProjectionMatrix);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
this.lightViewProjMatrix = mat4.multiply(
|
|
411
|
-
lightProjectionMatrix,
|
|
412
|
-
this.lightViewMatrix
|
|
413
|
-
);
|
|
414
|
-
|
|
415
363
|
// looks like affect on transformations for now const 0
|
|
416
364
|
const modelMatrix = mat4.translation([0, 0, 0]);
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
lightMatrixData.byteLength
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
const lightData = this.lightPosition;
|
|
429
|
-
this.device.queue.writeBuffer(
|
|
430
|
-
this.sceneUniformBuffer,
|
|
431
|
-
128,
|
|
432
|
-
lightData.buffer,
|
|
433
|
-
lightData.byteOffset,
|
|
434
|
-
lightData.byteLength
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
const modelData = modelMatrix;
|
|
438
|
-
this.device.queue.writeBuffer(
|
|
439
|
-
this.modelUniformBuffer,
|
|
440
|
-
0,
|
|
441
|
-
modelData.buffer,
|
|
442
|
-
modelData.byteOffset,
|
|
443
|
-
modelData.byteLength
|
|
444
|
-
);
|
|
445
|
-
}
|
|
365
|
+
const modelData = modelMatrix;
|
|
366
|
+
this.device.queue.writeBuffer(
|
|
367
|
+
this.modelUniformBuffer,
|
|
368
|
+
0,
|
|
369
|
+
modelData.buffer,
|
|
370
|
+
modelData.byteOffset,
|
|
371
|
+
modelData.byteLength
|
|
372
|
+
);
|
|
446
373
|
|
|
447
374
|
this.shadowPassDescriptor = {
|
|
448
375
|
colorAttachments: [],
|
|
@@ -463,73 +390,8 @@ export default class MEMeshObj extends Materials {
|
|
|
463
390
|
})
|
|
464
391
|
}
|
|
465
392
|
|
|
466
|
-
updateLightsTest = (position) => {
|
|
467
|
-
console.log('Update light position.', position)
|
|
468
|
-
this.lightPosition = vec3.fromValues(position[0], position[1], position[2]);
|
|
469
|
-
this.lightViewMatrix = mat4.lookAt(this.lightPosition, this.origin, this.upVector);
|
|
470
|
-
|
|
471
|
-
const lightProjectionMatrix = mat4.create();
|
|
472
|
-
{
|
|
473
|
-
const left = -80;
|
|
474
|
-
const right = 80;
|
|
475
|
-
const bottom = -80;
|
|
476
|
-
const top = 80;
|
|
477
|
-
const near = -200;
|
|
478
|
-
const far = 300;
|
|
479
|
-
mat4.ortho(left, right, bottom, top, near, far, lightProjectionMatrix);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
this.lightViewProjMatrix = mat4.multiply(
|
|
483
|
-
lightProjectionMatrix,
|
|
484
|
-
this.lightViewMatrix
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
// looks like affect on transformations for now const 0
|
|
488
|
-
const modelMatrix = mat4.translation([0, 0, 0]);
|
|
489
|
-
// The camera/light aren't moving, so write them into buffers now.
|
|
490
|
-
{
|
|
491
|
-
const lightMatrixData = this.lightViewProjMatrix; // as Float32Array;
|
|
492
|
-
this.device.queue.writeBuffer(
|
|
493
|
-
this.sceneUniformBuffer,
|
|
494
|
-
0, // 0 ori
|
|
495
|
-
lightMatrixData.buffer,
|
|
496
|
-
lightMatrixData.byteOffset,
|
|
497
|
-
lightMatrixData.byteLength
|
|
498
|
-
);
|
|
499
|
-
|
|
500
|
-
const lightData = this.lightPosition;
|
|
501
|
-
this.device.queue.writeBuffer(
|
|
502
|
-
this.sceneUniformBuffer,
|
|
503
|
-
256,
|
|
504
|
-
lightData.buffer,
|
|
505
|
-
lightData.byteOffset,
|
|
506
|
-
lightData.byteLength
|
|
507
|
-
);
|
|
508
|
-
|
|
509
|
-
const modelData = modelMatrix;
|
|
510
|
-
this.device.queue.writeBuffer(
|
|
511
|
-
this.modelUniformBuffer,
|
|
512
|
-
0,
|
|
513
|
-
modelData.buffer,
|
|
514
|
-
modelData.byteOffset,
|
|
515
|
-
modelData.byteLength
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
this.shadowPassDescriptor = {
|
|
520
|
-
colorAttachments: [],
|
|
521
|
-
depthStencilAttachment: {
|
|
522
|
-
view: this.shadowDepthTextureView,
|
|
523
|
-
depthClearValue: 1.0, // ori 1.0
|
|
524
|
-
depthLoadOp: 'clear',
|
|
525
|
-
depthStoreOp: 'store',
|
|
526
|
-
},
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
///////////////////////
|
|
530
|
-
}
|
|
531
|
-
|
|
532
393
|
setupPipeline = () => {
|
|
394
|
+
console.log('test >>>>>>>>>>>>>>>>>>>FORMAT>✅' + this.presentationFormat);
|
|
533
395
|
this.pipeline = this.device.createRenderPipeline({
|
|
534
396
|
layout: this.device.createPipelineLayout({
|
|
535
397
|
bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout],
|
|
@@ -558,7 +420,8 @@ export default class MEMeshObj extends Materials {
|
|
|
558
420
|
depthStencil: {
|
|
559
421
|
depthWriteEnabled: true,
|
|
560
422
|
depthCompare: 'less',
|
|
561
|
-
format: 'depth24plus-stencil8',
|
|
423
|
+
// format: 'depth24plus-stencil8',
|
|
424
|
+
format: 'depth24plus',
|
|
562
425
|
},
|
|
563
426
|
primitive: this.primitive,
|
|
564
427
|
});
|
|
@@ -586,7 +449,6 @@ export default class MEMeshObj extends Materials {
|
|
|
586
449
|
renderPass.drawIndexed(this.indexCount);
|
|
587
450
|
}
|
|
588
451
|
|
|
589
|
-
// test
|
|
590
452
|
createGPUBuffer(dataArray, usage) {
|
|
591
453
|
if(!dataArray || typeof dataArray.length !== 'number') {
|
|
592
454
|
throw new Error('Invalid data array passed to createGPUBuffer');
|
|
@@ -613,7 +475,6 @@ export default class MEMeshObj extends Materials {
|
|
|
613
475
|
return buffer;
|
|
614
476
|
}
|
|
615
477
|
|
|
616
|
-
|
|
617
478
|
updateMeshListBuffers() {
|
|
618
479
|
for(const key in this.objAnim.meshList) {
|
|
619
480
|
const mesh = this.objAnim.meshList[key];
|
|
@@ -12,6 +12,8 @@ struct Scene {
|
|
|
12
12
|
@group(0) @binding(3) var meshTexture: texture_external;
|
|
13
13
|
@group(0) @binding(4) var meshSampler: sampler;
|
|
14
14
|
|
|
15
|
+
@group(0) @binding(5) var<uniform> postFXMode: u32;
|
|
16
|
+
|
|
15
17
|
// ❌ No binding(4) here!
|
|
16
18
|
|
|
17
19
|
struct FragmentInput {
|
|
@@ -45,7 +47,37 @@ fn main(input : FragmentInput) -> @location(0) vec4f {
|
|
|
45
47
|
|
|
46
48
|
// ✅ Correct way to sample video texture
|
|
47
49
|
let textureColor = textureSampleBaseClampToEdge(meshTexture, meshSampler, input.uv);
|
|
50
|
+
let color: vec4f = vec4(textureColor.rgb * lightingFactor * albedo, 1.0);
|
|
51
|
+
|
|
52
|
+
switch (postFXMode) {
|
|
53
|
+
case 0: {
|
|
54
|
+
// Default
|
|
55
|
+
return color;
|
|
56
|
+
}
|
|
57
|
+
case 1: {
|
|
58
|
+
// Invert
|
|
59
|
+
return vec4f(1.0 - color.rgb, color.a);
|
|
60
|
+
}
|
|
61
|
+
case 2: {
|
|
62
|
+
// Grayscale
|
|
63
|
+
let gray = dot(color.rgb, vec3f(0.299, 0.587, 0.114));
|
|
64
|
+
return vec4f(vec3f(gray), color.a);
|
|
65
|
+
}
|
|
66
|
+
case 3: {
|
|
67
|
+
// Chroma Key
|
|
68
|
+
let keyColor = vec3f(0.0, 1.0, 0.0);
|
|
69
|
+
let threshold = 0.3;
|
|
70
|
+
let diff = distance(color.rgb, keyColor);
|
|
71
|
+
if (diff < threshold) {
|
|
72
|
+
return vec4f(0.0, 0.0, 0.0, 0.0);
|
|
73
|
+
}
|
|
74
|
+
return color;
|
|
75
|
+
}
|
|
76
|
+
default: {
|
|
77
|
+
return color;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
48
80
|
|
|
49
|
-
return
|
|
81
|
+
// return color;
|
|
50
82
|
}
|
|
51
83
|
`;
|
|
@@ -4,6 +4,17 @@ struct Scene {
|
|
|
4
4
|
lightViewProjMatrix : mat4x4f,
|
|
5
5
|
cameraViewProjMatrix : mat4x4f,
|
|
6
6
|
lightPos : vec3f,
|
|
7
|
+
padding : f32, // Required for alignment
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
struct SpotLight {
|
|
11
|
+
position: vec3f,
|
|
12
|
+
_pad1: f32,
|
|
13
|
+
direction: vec3f,
|
|
14
|
+
_pad2: f32,
|
|
15
|
+
innerCutoff: f32,
|
|
16
|
+
outerCutoff: f32,
|
|
17
|
+
_pad3: vec2f,
|
|
7
18
|
}
|
|
8
19
|
|
|
9
20
|
@group(0) @binding(0) var<uniform> scene : Scene;
|
|
@@ -11,6 +22,7 @@ struct Scene {
|
|
|
11
22
|
@group(0) @binding(2) var shadowSampler: sampler_comparison;
|
|
12
23
|
@group(0) @binding(3) var meshTexture: texture_2d<f32>;
|
|
13
24
|
@group(0) @binding(4) var meshSampler: sampler;
|
|
25
|
+
@group(0) @binding(5) var<uniform> spotlight: SpotLight;
|
|
14
26
|
|
|
15
27
|
struct FragmentInput {
|
|
16
28
|
@location(0) shadowPos : vec3f,
|
|
@@ -22,16 +34,22 @@ struct FragmentInput {
|
|
|
22
34
|
const albedo = vec3f(0.9);
|
|
23
35
|
const ambientFactor = 0.7;
|
|
24
36
|
|
|
37
|
+
fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
|
|
38
|
+
let L = normalize(light.position - fragPos);
|
|
39
|
+
let theta = dot(L, normalize(-light.direction));
|
|
40
|
+
let epsilon = light.innerCutoff - light.outerCutoff;
|
|
41
|
+
let intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
|
|
42
|
+
return intensity;
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
@fragment
|
|
26
46
|
fn main(input : FragmentInput) -> @location(0) vec4f {
|
|
27
|
-
//
|
|
28
|
-
// to smooth the result.
|
|
47
|
+
// Shadow PFC
|
|
29
48
|
var visibility = 0.0;
|
|
30
|
-
let
|
|
49
|
+
let oneOverSize = 1.0 / shadowDepthTextureSize;
|
|
31
50
|
for (var y = -1; y <= 1; y++) {
|
|
32
51
|
for (var x = -1; x <= 1; x++) {
|
|
33
|
-
let offset = vec2f(vec2(x, y)) *
|
|
34
|
-
|
|
52
|
+
let offset = vec2f(vec2(x, y)) * oneOverSize;
|
|
35
53
|
visibility += textureSampleCompare(
|
|
36
54
|
shadowMap, shadowSampler,
|
|
37
55
|
input.shadowPos.xy + offset, input.shadowPos.z - 0.007
|
|
@@ -39,12 +57,19 @@ fn main(input : FragmentInput) -> @location(0) vec4f {
|
|
|
39
57
|
}
|
|
40
58
|
}
|
|
41
59
|
visibility /= 9.0;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
|
|
61
|
+
// Lambert
|
|
62
|
+
let norm = normalize(input.fragNorm);
|
|
63
|
+
let lightDir = normalize(scene.lightPos - input.fragPos);
|
|
64
|
+
let lambert = max(dot(norm, lightDir), 0.0);
|
|
65
|
+
|
|
66
|
+
// Spotlight effect
|
|
67
|
+
let spotlightFactor = calculateSpotlightFactor(spotlight, input.fragPos);
|
|
68
|
+
|
|
69
|
+
// Combine
|
|
70
|
+
let lightIntensity = ambientFactor + lambert * visibility * spotlightFactor;
|
|
71
|
+
let texColor = textureSample(meshTexture, meshSampler, input.uv);
|
|
72
|
+
|
|
73
|
+
return vec4f(texColor.rgb * lightIntensity * albedo, 1.0);
|
|
74
|
+
}
|
|
75
|
+
`
|
package/src/world.js
CHANGED
|
@@ -2,18 +2,19 @@ import {vec3} from "wgpu-matrix";
|
|
|
2
2
|
import MEBall from "./engine/ball.js";
|
|
3
3
|
import MECube from './engine/cube.js';
|
|
4
4
|
import {ArcballCamera, WASDCamera} from "./engine/engine.js";
|
|
5
|
-
import
|
|
5
|
+
import {createInputHandler} from "./engine/engine.js";
|
|
6
6
|
import MEMeshObj from "./engine/mesh-obj.js";
|
|
7
7
|
import MatrixAmmo from "./physics/matrix-ammo.js";
|
|
8
8
|
import {LOG_WARN, genName, mb, scriptManager, urlQuery} from "./engine/utils.js";
|
|
9
9
|
import {MultiLang} from "./multilang/lang.js";
|
|
10
10
|
import {MatrixSounds} from "./sounds/sounds.js";
|
|
11
11
|
import {play} from "./engine/loader-obj.js";
|
|
12
|
+
import {SpotLight} from "./engine/lights.js";
|
|
12
13
|
|
|
13
14
|
export default class MatrixEngineWGPU {
|
|
14
15
|
|
|
15
16
|
mainRenderBundle = [];
|
|
16
|
-
|
|
17
|
+
lightContainer = [];
|
|
17
18
|
frame = () => {};
|
|
18
19
|
|
|
19
20
|
entityHolder = [];
|
|
@@ -131,11 +132,23 @@ export default class MatrixEngineWGPU {
|
|
|
131
132
|
this.frame = this.framePassPerObject;
|
|
132
133
|
}
|
|
133
134
|
|
|
135
|
+
// Global SCENE BUFFER
|
|
136
|
+
this.sceneUniformBuffer = this.device.createBuffer({
|
|
137
|
+
// Two 4x4 viewProj matrices,
|
|
138
|
+
// one for the camera and one for the light.
|
|
139
|
+
// Then a vec3 for the light position.
|
|
140
|
+
// Rounded to the nearest multiple of 16.
|
|
141
|
+
size: 2 * 4 * 16 + 4 * 4,
|
|
142
|
+
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.inputHandler = createInputHandler(window, canvas);
|
|
146
|
+
|
|
134
147
|
this.run(callback)
|
|
135
148
|
};
|
|
136
149
|
|
|
137
150
|
getSceneObjectByName(name) {
|
|
138
|
-
return this.mainRenderBundle.find((sceneObject) => sceneObject.name === name
|
|
151
|
+
return this.mainRenderBundle.find((sceneObject) => sceneObject.name === name)
|
|
139
152
|
}
|
|
140
153
|
|
|
141
154
|
// Not in use for now
|
|
@@ -238,27 +251,15 @@ export default class MatrixEngineWGPU {
|
|
|
238
251
|
this.mainRenderBundle.push(myBall1);
|
|
239
252
|
}
|
|
240
253
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if(typeof o.mainCameraParams === 'undefined') {o.mainCameraParams = this.mainCameraParams}
|
|
248
|
-
if(typeof o.scale === 'undefined') {o.scale = 1;}
|
|
249
|
-
o.entityArgPass = this.entityArgPass;
|
|
250
|
-
o.cameras = this.cameras;
|
|
251
|
-
if(typeof o.name === 'undefined') {o.name = 'random' + Math.random();}
|
|
252
|
-
if(typeof o.mesh === 'undefined') {
|
|
253
|
-
throw console.error('arg mesh is empty...');
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
console.log('Mesh procedure', o)
|
|
257
|
-
let myMesh1 = new MEMesh(this.canvas, this.device, this.context, o)
|
|
258
|
-
this.mainRenderBundle.push(myMesh1);
|
|
254
|
+
addLight(o) {
|
|
255
|
+
// test light global; entity
|
|
256
|
+
let newLight = new SpotLight();
|
|
257
|
+
newLight.prepareBuffer(this.device);
|
|
258
|
+
this.lightContainer.push(newLight);
|
|
259
|
+
console.log('Add light : ', newLight);
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
addMeshObj = (o
|
|
262
|
+
addMeshObj = (o, clearColor = this.options.clearColor) => {
|
|
262
263
|
if(typeof o.name === 'undefined') {o.name = genName(9)}
|
|
263
264
|
if(typeof o.position === 'undefined') {o.position = {x: 0, y: 0, z: -4}}
|
|
264
265
|
if(typeof o.rotation === 'undefined') {o.rotation = {x: 0, y: 0, z: 0}}
|
|
@@ -311,7 +312,9 @@ export default class MatrixEngineWGPU {
|
|
|
311
312
|
}
|
|
312
313
|
}
|
|
313
314
|
}
|
|
314
|
-
let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o);
|
|
315
|
+
let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o, this.sceneUniformBuffer);
|
|
316
|
+
myMesh1.lightContainer = this.lightContainer;
|
|
317
|
+
myMesh1.inputHandler = this.inputHandler;
|
|
315
318
|
myMesh1.clearColor = clearColor;
|
|
316
319
|
if(o.physics.enabled == true) {
|
|
317
320
|
this.matrixAmmo.addPhysics(myMesh1, o.physics)
|
|
@@ -330,31 +333,56 @@ export default class MatrixEngineWGPU {
|
|
|
330
333
|
}
|
|
331
334
|
|
|
332
335
|
frameSinglePass = () => {
|
|
333
|
-
if(typeof this.mainRenderBundle == 'undefined')
|
|
336
|
+
if(typeof this.mainRenderBundle == 'undefined' || this.mainRenderBundle.length == 0) {
|
|
337
|
+
setTimeout(() => {requestAnimationFrame(this.frame)}, 200);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
334
340
|
try {
|
|
335
341
|
let shadowPass = null;
|
|
336
342
|
let renderPass;
|
|
337
343
|
let commandEncoder = this.device.createCommandEncoder();
|
|
338
344
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
345
|
+
// 1️⃣ Update light data (position, direction, uniforms)
|
|
346
|
+
for(const light of this.lightContainer) {
|
|
347
|
+
// light.updateSceneUniforms(this.sceneUniformBuffer, this.cameras.WASD, this.inputHandler);
|
|
348
|
+
}
|
|
342
349
|
|
|
343
|
-
|
|
350
|
+
this.mainRenderBundle.forEach((meItem, index) => {meItem.position.update()})
|
|
351
|
+
if(this.matrixAmmo) this.matrixAmmo.updatePhysics();
|
|
352
|
+
|
|
353
|
+
// no cast WORKING
|
|
354
|
+
// this.mainRenderBundle.forEach((meItem, index) => {
|
|
355
|
+
// meItem.draw(commandEncoder);
|
|
356
|
+
|
|
357
|
+
// shadowPass = commandEncoder.beginRenderPass(meItem.shadowPassDescriptor);
|
|
358
|
+
// shadowPass.setPipeline(meItem.shadowPipeline);
|
|
359
|
+
// meItem.drawShadows(shadowPass);
|
|
360
|
+
// shadowPass.end();
|
|
361
|
+
// })
|
|
362
|
+
|
|
363
|
+
// cast!
|
|
364
|
+
const firstItem = this.mainRenderBundle[0];
|
|
365
|
+
shadowPass = commandEncoder.beginRenderPass(firstItem.shadowPassDescriptor);
|
|
366
|
+
shadowPass.setPipeline(firstItem.shadowPipeline);
|
|
367
|
+
for(const meItem of this.mainRenderBundle) {
|
|
368
|
+
// meItem.draw(commandEncoder);
|
|
369
|
+
meItem.drawShadows(shadowPass); // Draw ALL objects
|
|
370
|
+
}
|
|
371
|
+
shadowPass.end();
|
|
344
372
|
|
|
345
373
|
this.mainRenderBundle.forEach((meItem, index) => {
|
|
346
|
-
|
|
374
|
+
if(index == 0) {
|
|
347
375
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
376
|
+
// meItem. updateSceneUniforms
|
|
377
|
+
meItem.draw(commandEncoder);
|
|
378
|
+
|
|
379
|
+
meItem.renderPassDescriptor.colorAttachments[0].view =
|
|
380
|
+
this.context.getCurrentTexture().createView();
|
|
353
381
|
|
|
354
|
-
this.mainRenderBundle.forEach((meItem, index) => {
|
|
355
|
-
if(index == 0) {
|
|
356
382
|
renderPass = commandEncoder.beginRenderPass(meItem.renderPassDescriptor);
|
|
357
383
|
renderPass.setPipeline(meItem.pipeline);
|
|
384
|
+
} else {
|
|
385
|
+
meItem.draw(commandEncoder);
|
|
358
386
|
}
|
|
359
387
|
})
|
|
360
388
|
|