@footgun/cobalt 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,6 @@ import readSpriteSheet from './read-spritesheet.js'
6
6
 
7
7
  export default {
8
8
  type: 'cobalt:spritesheet',
9
- refs: [],
10
9
 
11
10
  // @params Object cobalt renderer world object
12
11
  // @params Object options optional data passed when initing this node
@@ -9,7 +9,6 @@ const _buf = new Float32Array(8) //(136) // tile instance data stored in a UBO
9
9
  // shared tile atlas resource, used by each tile render node
10
10
  export default {
11
11
  type: 'cobalt:tileAtlas',
12
- refs: [],
13
12
 
14
13
  // @params Object cobalt renderer world object
15
14
  // @params Object options optional data passed when initing this node
@@ -15,12 +15,13 @@ Because this processing can happen completely in the fragment shader, there's no
15
15
  Inspired by/ported from https://blog.tojicode.com/2012/07/sprite-tile-maps-on-gpu.html
16
16
  */
17
17
 
18
+ /**
19
+ * Refs:
20
+ * tileAtlas (textureView, rgba8unorm, read) - tile atlas texture
21
+ * out (textureView, rgba16float, write) - render target
22
+ */
18
23
  export default {
19
24
  type: 'cobalt:tileHDR',
20
- refs: [
21
- { name: 'tileAtlas', type: 'textureView', format: 'rgba8unorm', access: 'read' },
22
- { name: 'hdr', type: 'textureView', format: 'rgba16float', access: 'write' },
23
- ],
24
25
 
25
26
  // @params Object cobalt renderer world object
26
27
  // @params Object options optional data passed when initing this node
@@ -174,8 +175,8 @@ function draw(cobalt, nodeData, commandEncoder) {
174
175
  label: 'tile',
175
176
  colorAttachments: [
176
177
  {
177
- // hdr is passsed as a node || FRAME_TEXTURE_VIEW
178
- view: nodeData.refs.hdr.data?.view || nodeData.refs.hdr,
178
+ // out is passsed as a node || FRAME_TEXTURE_VIEW
179
+ view: nodeData.refs.out.data?.view || nodeData.refs.out,
179
180
  clearValue: cobalt.clearValue,
180
181
  loadOp,
181
182
  storeOp: 'store',
@@ -1,230 +0,0 @@
1
- import { LightsBuffer } from "./lights-buffer.js";
2
- import { LightsTexture } from "./texture/lights-texture.js";
3
-
4
- class LightsRenderer {
5
- device;
6
- ambientLight = [0.2, 0.2, 0.2];
7
- targetTexture;
8
- renderPipeline;
9
- uniformsBufferGpu;
10
- bindgroup0;
11
- bindgroup1;
12
- renderBundle;
13
- lightsBuffer;
14
- lightsTexture;
15
- constructor(params) {
16
- this.device = params.device;
17
- this.targetTexture = params.targetTexture;
18
- this.lightsBuffer = params.lightsBuffer;
19
- this.lightsTexture = new LightsTexture(params.device, params.lightsBuffer, params.lightsTextureProperties);
20
- this.uniformsBufferGpu = params.device.createBuffer({
21
- label: "LightsRenderer uniforms buffer",
22
- size: 80,
23
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
24
- });
25
- const shaderModule = params.device.createShaderModule({
26
- label: "LightsRenderer shader module",
27
- code: `
28
- struct Uniforms { // align(16) size(80)
29
- invertViewMatrix: mat4x4<f32>, // offset(0) align(16) size(64)
30
- ambientLight: vec3<f32>, // offset(64) align(16) size(12)
31
- };
32
-
33
- ${LightsBuffer.structs.definition}
34
-
35
- @group(0) @binding(0) var<uniform> uniforms: Uniforms;
36
- @group(0) @binding(1) var<storage,read> lightsBuffer: LightsBuffer;
37
- @group(0) @binding(2) var lightsTexture: texture_2d<f32>;
38
- @group(0) @binding(3) var lightsTextureSampler: sampler;
39
-
40
- @group(1) @binding(0) var albedoTexture: texture_2d<f32>;
41
- @group(1) @binding(1) var albedoSampler: sampler;
42
-
43
- struct VertexIn {
44
- @builtin(vertex_index) vertexIndex: u32,
45
- };
46
-
47
- struct VertexOut {
48
- @builtin(position) position: vec4<f32>,
49
- @location(0) worldPosition: vec2<f32>,
50
- @location(1) uv: vec2<f32>,
51
- };
52
-
53
- @vertex
54
- fn main_vertex(in: VertexIn) -> VertexOut {
55
- const corners = array<vec2<f32>, 4>(
56
- vec2<f32>(-1, -1),
57
- vec2<f32>(1, -1),
58
- vec2<f32>(-1, 1),
59
- vec2<f32>(1, 1),
60
- );
61
- let screenPosition = corners[in.vertexIndex];
62
-
63
- var out: VertexOut;
64
- out.position = vec4<f32>(screenPosition, 0.0, 1.0);
65
- out.worldPosition = (uniforms.invertViewMatrix * out.position).xy;
66
- out.uv = 0.5 + 0.5 * screenPosition * vec2<f32>(1.0, -1.0);
67
- return out;
68
- }
69
-
70
- struct FragmentOut {
71
- @location(0) color: vec4<f32>,
72
- };
73
-
74
- const cellsGridSizeU = vec2<u32>(${this.lightsTexture.gridSize.x}, ${this.lightsTexture.gridSize.y});
75
- const cellsGridSizeF = vec2<f32>(${this.lightsTexture.gridSize.x}, ${this.lightsTexture.gridSize.y});
76
-
77
- fn sampleLightBaseIntensity(lightId: u32, localUv: vec2<f32>) -> f32 {
78
- let cellIndex = lightId / 4u;
79
- let indexInCell = lightId % 4u;
80
-
81
- let cellIdU = vec2<u32>(
82
- cellIndex % cellsGridSizeU.x,
83
- cellIndex / cellsGridSizeU.x,
84
- );
85
- let cellIdF = vec2<f32>(cellIdU);
86
- let uv = (cellIdF + localUv) / cellsGridSizeF;
87
- let uvYInverted = vec2<f32>(uv.x, 1.0 - uv.y);
88
- let sample = textureSampleLevel(lightsTexture, lightsTextureSampler, uvYInverted, 0.0);
89
- let channel = vec4<f32>(
90
- vec4<u32>(indexInCell) == vec4<u32>(0u, 1u, 2u, 3u),
91
- );
92
- return dot(sample, channel);
93
- }
94
-
95
- fn compute_lights(worldPosition: vec2<f32>) -> vec3<f32> {
96
- var color = vec3<f32>(uniforms.ambientLight);
97
-
98
- const maxUvDistance = f32(${1 - 2 / params.lightsTextureProperties.resolutionPerLight});
99
-
100
- let lightsCount = lightsBuffer.count;
101
- for (var iLight = 0u; iLight < lightsCount; iLight++) {
102
- let light = lightsBuffer.lights[iLight];
103
- let lightSize = f32(${params.lightsTextureProperties.resolutionPerLight});
104
- let relativePosition = (worldPosition - light.position) / lightSize;
105
- if (max(abs(relativePosition.x), abs(relativePosition.y)) < maxUvDistance) {
106
- let localUv = 0.5 + 0.5 * relativePosition;
107
- let lightIntensity = light.intensity * sampleLightBaseIntensity(iLight, localUv);
108
- color += lightIntensity * light.color;
109
- }
110
- }
111
-
112
- return color;
113
- }
114
-
115
- @fragment
116
- fn main_fragment(in: VertexOut) -> FragmentOut {
117
- let light = compute_lights(in.worldPosition);
118
- let albedo = textureSample(albedoTexture, albedoSampler, in.uv);
119
- let color = albedo.rgb * light;
120
-
121
- var out: FragmentOut;
122
- out.color = vec4<f32>(color, 1.0);
123
- return out;
124
- }
125
- `,
126
- });
127
- this.renderPipeline = params.device.createRenderPipeline({
128
- label: "LightsRenderer renderpipeline",
129
- layout: "auto",
130
- vertex: {
131
- module: shaderModule,
132
- entryPoint: "main_vertex",
133
- },
134
- fragment: {
135
- module: shaderModule,
136
- entryPoint: "main_fragment",
137
- targets: [{
138
- format: this.targetTexture.format,
139
- }],
140
- },
141
- primitive: {
142
- cullMode: "none",
143
- topology: "triangle-strip",
144
- },
145
- });
146
- const bindgroupLayout = this.renderPipeline.getBindGroupLayout(0);
147
- this.bindgroup0 = params.device.createBindGroup({
148
- label: "LightsRenderer bindgroup 0",
149
- layout: bindgroupLayout,
150
- entries: [
151
- {
152
- binding: 0,
153
- resource: { buffer: this.uniformsBufferGpu },
154
- },
155
- {
156
- binding: 1,
157
- resource: { buffer: this.lightsBuffer.gpuBuffer },
158
- },
159
- {
160
- binding: 2,
161
- resource: this.lightsTexture.texture.createView({ label: "LightsRenderer lightsTexture view" }),
162
- },
163
- {
164
- binding: 3,
165
- resource: params.device.createSampler({
166
- label: "LightsRenderer sampler",
167
- addressModeU: "clamp-to-edge",
168
- addressModeV: "clamp-to-edge",
169
- magFilter: params.lightsTextureProperties.filtering,
170
- minFilter: params.lightsTextureProperties.filtering,
171
- }),
172
- },
173
- ]
174
- });
175
- this.bindgroup1 = this.buildBindgroup1(params.albedo);
176
- this.renderBundle = this.buildRenderBundle();
177
- }
178
- computeLightsTexture(commandEncoder) {
179
- this.lightsTexture.update(commandEncoder);
180
- }
181
- render(renderpassEncoder, invertVpMatrix) {
182
- const uniformsBufferCpu = new ArrayBuffer(80);
183
- new Float32Array(uniformsBufferCpu, 0, 16).set(invertVpMatrix);
184
- new Float32Array(uniformsBufferCpu, 64, 3).set(this.ambientLight);
185
- this.device.queue.writeBuffer(this.uniformsBufferGpu, 0, uniformsBufferCpu);
186
- renderpassEncoder.executeBundles([this.renderBundle]);
187
- }
188
- setAlbedo(albedo) {
189
- this.bindgroup1 = this.buildBindgroup1(albedo);
190
- this.renderBundle = this.buildRenderBundle();
191
- }
192
- setAmbientLight(color) {
193
- this.ambientLight = [...color];
194
- }
195
- setObstacles(segments) {
196
- this.lightsTexture.setObstacles(segments);
197
- }
198
- destroy() {
199
- this.uniformsBufferGpu.destroy();
200
- this.lightsTexture.destroy();
201
- }
202
- buildBindgroup1(albedo) {
203
- return this.device.createBindGroup({
204
- label: "LightsRenderer bindgroup 1",
205
- layout: this.renderPipeline.getBindGroupLayout(1),
206
- entries: [
207
- {
208
- binding: 0,
209
- resource: albedo.view,
210
- },
211
- {
212
- binding: 1,
213
- resource: albedo.sampler,
214
- },
215
- ]
216
- });
217
- }
218
- buildRenderBundle() {
219
- const renderBundleEncoder = this.device.createRenderBundleEncoder({
220
- label: "LightsRenderer renderbundle encoder",
221
- colorFormats: [this.targetTexture.format],
222
- });
223
- renderBundleEncoder.setPipeline(this.renderPipeline);
224
- renderBundleEncoder.setBindGroup(0, this.bindgroup0);
225
- renderBundleEncoder.setBindGroup(1, this.bindgroup1);
226
- renderBundleEncoder.draw(4);
227
- return renderBundleEncoder.finish({ label: "LightsRenderer renderbundle" });
228
- }
229
- }
230
- export { LightsRenderer };
@@ -1,175 +0,0 @@
1
- import { LightsBuffer } from "../lights-buffer.js";
2
-
3
- class LightsTextureInitializer {
4
- lightsBuffer;
5
- renderPipeline;
6
- bindgroup;
7
- renderBundle;
8
- constructor(device, lightsBuffer, lightsTexture, maxLightSize) {
9
- this.lightsBuffer = lightsBuffer;
10
- const shaderModule = device.createShaderModule({
11
- label: "LightsTextureInitializer shader module",
12
- code: `
13
- ${LightsBuffer.structs.definition}
14
-
15
- @group(0) @binding(0) var<storage,read> lightsBuffer: LightsBuffer;
16
-
17
- struct VertexIn {
18
- @builtin(vertex_index) vertexIndex: u32,
19
- };
20
-
21
- struct VertexOut {
22
- @builtin(position) position: vec4<f32>,
23
- @location(0) uv: vec2<f32>,
24
- };
25
-
26
- const cellsGridSizeU = vec2<u32>(${lightsTexture.gridSize.x}, ${lightsTexture.gridSize.y});
27
- const cellsGridSizeF = vec2<f32>(${lightsTexture.gridSize.x}, ${lightsTexture.gridSize.y});
28
-
29
- @vertex
30
- fn main_vertex(in: VertexIn) -> VertexOut {
31
- const corners = array<vec2<f32>, 4>(
32
- vec2<f32>(-1, -1),
33
- vec2<f32>(1, -1),
34
- vec2<f32>(-1, 1),
35
- vec2<f32>(1, 1),
36
- );
37
- let screenPosition = corners[in.vertexIndex];
38
-
39
- var out: VertexOut;
40
- out.position = vec4<f32>(screenPosition, 0.0, 1.0);
41
- out.uv = (0.5 + 0.5 * screenPosition) * cellsGridSizeF;
42
- return out;
43
- }
44
-
45
- struct FragmentOut {
46
- @location(0) color: vec4<f32>,
47
- };
48
-
49
- struct LightProperties {
50
- radius: f32,
51
- intensity: f32,
52
- attenuationLinear: f32,
53
- attenuationExp: f32,
54
- };
55
-
56
- fn get_light_properties(lightId: u32) -> LightProperties {
57
- var lightProperties: LightProperties;
58
- if (lightId < lightsBuffer.count) {
59
- let light = lightsBuffer.lights[lightId];
60
- lightProperties.radius = light.radius;
61
- lightProperties.intensity = 1.0;
62
- lightProperties.attenuationLinear = light.attenuationLinear;
63
- lightProperties.attenuationExp = light.attenuationExp;
64
- } else {
65
- lightProperties.radius = 0.0;
66
- lightProperties.intensity = 0.0;
67
- lightProperties.attenuationLinear = 0.0;
68
- lightProperties.attenuationExp = 0.0;
69
- }
70
- return lightProperties;
71
- }
72
-
73
- @fragment
74
- fn main_fragment(in: VertexOut) -> FragmentOut {
75
- let cellId = vec2<u32>(in.uv);
76
-
77
- let lightIdFrom = 4u * (cellId.x + cellId.y * cellsGridSizeU.x);
78
- let lightProperties = array<LightProperties, 4>(
79
- get_light_properties(lightIdFrom + 0u),
80
- get_light_properties(lightIdFrom + 1u),
81
- get_light_properties(lightIdFrom + 2u),
82
- get_light_properties(lightIdFrom + 3u),
83
- );
84
-
85
- let sizes = vec4<f32>(
86
- lightProperties[0].radius,
87
- lightProperties[1].radius,
88
- lightProperties[2].radius,
89
- lightProperties[3].radius,
90
- );
91
-
92
- let localUv = fract(in.uv);
93
- let fromCenter = 2.0 * localUv - 1.0;
94
- let uvDistanceFromCenter = distance(vec2<f32>(0,0), fromCenter);
95
- let distancesFromCenter = vec4<f32>(uvDistanceFromCenter / sizes * f32(${maxLightSize}));
96
-
97
- let intensities = vec4<f32>(
98
- lightProperties[0].intensity * (1.0 + 1.0 * step(uvDistanceFromCenter, 0.01)),
99
- lightProperties[1].intensity * (1.0 + 1.0 * step(uvDistanceFromCenter, 0.01)),
100
- lightProperties[2].intensity * (1.0 + 1.0 * step(uvDistanceFromCenter, 0.01)),
101
- lightProperties[3].intensity * (1.0 + 1.0 * step(uvDistanceFromCenter, 0.01)),
102
- );
103
- let attenuationsLinear = vec4<f32>(
104
- lightProperties[0].attenuationLinear,
105
- lightProperties[1].attenuationLinear,
106
- lightProperties[2].attenuationLinear,
107
- lightProperties[3].attenuationLinear,
108
- );
109
- let attenuationsExp = vec4<f32>(
110
- lightProperties[0].attenuationExp,
111
- lightProperties[1].attenuationExp,
112
- lightProperties[2].attenuationExp,
113
- lightProperties[3].attenuationExp,
114
- );
115
-
116
- var lightIntensities = intensities / (1.0 + distancesFromCenter * (attenuationsLinear + distancesFromCenter * attenuationsExp)); // base intensity equation
117
- lightIntensities *= cos(distancesFromCenter * ${Math.PI / 2}); // soft limit;
118
- lightIntensities *= step(distancesFromCenter, vec4<f32>(1.0)); // hard limit
119
-
120
- var out: FragmentOut;
121
- out.color = lightIntensities;
122
- return out;
123
- }
124
- `,
125
- });
126
- this.renderPipeline = device.createRenderPipeline({
127
- label: "LightsTextureInitializer renderpipeline",
128
- layout: "auto",
129
- vertex: {
130
- module: shaderModule,
131
- entryPoint: "main_vertex",
132
- },
133
- fragment: {
134
- module: shaderModule,
135
- entryPoint: "main_fragment",
136
- targets: [{
137
- format: lightsTexture.format,
138
- }],
139
- },
140
- primitive: {
141
- cullMode: "none",
142
- topology: "triangle-strip",
143
- },
144
- multisample: {
145
- count: lightsTexture.sampleCount,
146
- },
147
- });
148
- this.bindgroup = device.createBindGroup({
149
- label: "LightsTextureInitializer bindgroup 0",
150
- layout: this.renderPipeline.getBindGroupLayout(0),
151
- entries: [
152
- {
153
- binding: 0,
154
- resource: { buffer: this.lightsBuffer.gpuBuffer },
155
- },
156
- ]
157
- });
158
- const renderBundleEncoder = device.createRenderBundleEncoder({
159
- label: "LightsTextureInitializer renderbundle encoder",
160
- colorFormats: [lightsTexture.format],
161
- sampleCount: lightsTexture.sampleCount,
162
- });
163
- renderBundleEncoder.setPipeline(this.renderPipeline);
164
- renderBundleEncoder.setBindGroup(0, this.bindgroup);
165
- renderBundleEncoder.draw(4);
166
- this.renderBundle = renderBundleEncoder.finish({ label: "LightsTextureInitializer renderbundle" });
167
- }
168
- getRenderBundle() {
169
- return this.renderBundle;
170
- }
171
- destroy() {
172
- // nothing to do
173
- }
174
- }
175
- export { LightsTextureInitializer };
@@ -1,85 +0,0 @@
1
- import { LightsTextureInitializer } from "./lights-texture-initializer.js";
2
- import { LightsTextureMask } from "./lights-texture-mask.js";
3
-
4
- class LightsTexture {
5
- lightsBuffer;
6
- texture;
7
- gridSize;
8
- textureMultisampled = null;
9
- textureRenderpassDescriptor;
10
- textureInitializer;
11
- textureMask;
12
- constructor(device, lightsBuffer, lightsTextureProperties) {
13
- this.lightsBuffer = lightsBuffer;
14
- const cellsCount = this.lightsBuffer.maxLightsCount / 4;
15
- const gridSize = {
16
- x: Math.ceil(Math.sqrt(cellsCount)),
17
- y: 0,
18
- };
19
- gridSize.y = Math.ceil(cellsCount / gridSize.x);
20
- this.gridSize = gridSize;
21
- const lightTextureSize = {
22
- width: gridSize.x * lightsTextureProperties.resolutionPerLight,
23
- height: gridSize.y * lightsTextureProperties.resolutionPerLight,
24
- };
25
- const format = lightsTextureProperties.textureFormat;
26
- this.texture = device.createTexture({
27
- label: "LightsTextureMask texture",
28
- size: [lightTextureSize.width, lightTextureSize.height],
29
- format,
30
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
31
- });
32
- if (lightsTextureProperties.antialiased) {
33
- this.textureMultisampled = device.createTexture({
34
- label: "LightsTextureMask texture multisampled",
35
- size: [lightTextureSize.width, lightTextureSize.height],
36
- format,
37
- usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
38
- sampleCount: 4,
39
- });
40
- }
41
- const textureToRenderTo = this.textureMultisampled ?? this.texture;
42
- const textureRenderpassColorAttachment = {
43
- view: textureToRenderTo.createView(),
44
- clearValue: [0, 0, 0, 1],
45
- loadOp: "load",
46
- storeOp: "store",
47
- };
48
- if (lightsTextureProperties.antialiased) {
49
- textureRenderpassColorAttachment.resolveTarget = this.texture.createView();
50
- }
51
- this.textureRenderpassDescriptor = {
52
- label: "lights-renderer render to texture renderpass",
53
- colorAttachments: [textureRenderpassColorAttachment],
54
- };
55
- const lightsTexture = {
56
- gridSize,
57
- format,
58
- sampleCount: this.textureMultisampled?.sampleCount ?? 1,
59
- };
60
- this.textureInitializer = new LightsTextureInitializer(device, lightsBuffer, lightsTexture, lightsTextureProperties.maxLightSize);
61
- this.textureMask = new LightsTextureMask(device, lightsBuffer, lightsTexture, lightsTextureProperties.maxLightSize);
62
- }
63
- update(commandEncoder) {
64
- this.textureMask.setLightsCount(this.lightsBuffer.lightsCount);
65
- const renderpassEncoder = commandEncoder.beginRenderPass(this.textureRenderpassDescriptor);
66
- const [textureWidth, textureHeight] = [this.texture.width, this.texture.height];
67
- renderpassEncoder.setViewport(0, 0, textureWidth, textureHeight, 0, 1);
68
- renderpassEncoder.setScissorRect(0, 0, textureWidth, textureHeight);
69
- renderpassEncoder.executeBundles([
70
- this.textureInitializer.getRenderBundle(),
71
- this.textureMask.getRenderBundle(),
72
- ]);
73
- renderpassEncoder.end();
74
- }
75
- setObstacles(segments) {
76
- this.textureMask.setObstacles(segments);
77
- }
78
- destroy() {
79
- this.texture.destroy();
80
- this.textureMultisampled?.destroy();
81
- this.textureInitializer.destroy();
82
- this.textureMask.destroy();
83
- }
84
- }
85
- export { LightsTexture };
@@ -1,40 +0,0 @@
1
- import * as wgpuMatrix from "wgpu-matrix";
2
-
3
- class Viewport {
4
- invViewProjectionMatrix = wgpuMatrix.mat4.identity();
5
- viewportSize = { width: 1, height: 1 };
6
- topLeft = [0, 0];
7
- zoom = 1;
8
- constructor(params) {
9
- this.setViewportSize(params.viewportSize.width, params.viewportSize.height);
10
- const initialTopLeft = params.center ?? this.topLeft;
11
- this.setTopLeft(...initialTopLeft);
12
- const initialZoom = params.zoom ?? 1;
13
- this.setZoom(initialZoom);
14
- }
15
- get invertViewProjectionMatrix() {
16
- return this.invViewProjectionMatrix;
17
- }
18
- setViewportSize(width, height) {
19
- this.viewportSize.width = width;
20
- this.viewportSize.height = height;
21
- this.updateMatrices();
22
- }
23
- setTopLeft(x, y) {
24
- this.topLeft[0] = x;
25
- this.topLeft[1] = y;
26
- this.updateMatrices();
27
- }
28
- setZoom(zoom) {
29
- this.zoom = zoom;
30
- this.updateMatrices();
31
- }
32
- updateMatrices() {
33
- wgpuMatrix.mat4.identity(this.invViewProjectionMatrix);
34
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling([1, -1, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
35
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([1, 1, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
36
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling([0.5 * this.viewportSize.width / this.zoom, 0.5 * this.viewportSize.height / this.zoom, 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
37
- wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation([this.topLeft[0], this.topLeft[1], 0]), this.invViewProjectionMatrix, this.invViewProjectionMatrix);
38
- }
39
- }
40
- export { Viewport };