diff --git a/apps/typegpu-docs/.gitignore b/apps/typegpu-docs/.gitignore index b15ba8429a..52cb68c9c7 100644 --- a/apps/typegpu-docs/.gitignore +++ b/apps/typegpu-docs/.gitignore @@ -26,3 +26,7 @@ src/content/docs/api # tests tests/artifacts !tests/artifacts/README.md + +# generated transformed files +*.tsnotover.ts +*.tsnotover.tsx diff --git a/apps/typegpu-docs/package.json b/apps/typegpu-docs/package.json index 51bc663e03..7045152623 100644 --- a/apps/typegpu-docs/package.json +++ b/apps/typegpu-docs/package.json @@ -4,8 +4,9 @@ "version": "0.0.1", "private": true, "scripts": { - "dev": "astro dev", - "build": "astro check && astro build", + "transform-overloads": "find . -type f -name '*.tsnotover.ts' -delete && node scripts/transform-overloads.ts", + "dev": "pnpm run transform-overloads && astro dev", + "build": "pnpm run transform-overloads && astro check && astro build", "test:types": "astro check", "preview": "astro preview", "astro": "astro" @@ -78,6 +79,7 @@ "@webgpu/types": "catalog:types", "astro-vtbot": "^2.1.10", "autoprefixer": "^10.4.21", + "magic-string": "^0.30.21", "tailwindcss": "^4.1.11", "tailwindcss-motion": "^1.1.1", "vite-imagetools": "catalog:frontend", diff --git a/apps/typegpu-docs/scripts/transform-overloads.ts b/apps/typegpu-docs/scripts/transform-overloads.ts new file mode 100644 index 0000000000..4ba4a7b9ba --- /dev/null +++ b/apps/typegpu-docs/scripts/transform-overloads.ts @@ -0,0 +1,288 @@ +import ts from 'typescript'; +import MagicString from 'magic-string'; +import { readdir } from 'fs/promises'; +import { basename, dirname, extname, join, relative } from 'path'; +import { fileURLToPath } from 'url'; +import { writeFile } from 'fs/promises'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const projectRoot = join(__dirname, '..'); +const examplesDir = join(projectRoot, 'src', 'examples'); + +const operatorToMethod: Record = { + [ts.SyntaxKind.PlusToken]: 'add', + [ts.SyntaxKind.PlusEqualsToken]: 'add', + [ts.SyntaxKind.MinusToken]: 'sub', + [ts.SyntaxKind.MinusEqualsToken]: 'sub', + [ts.SyntaxKind.AsteriskToken]: 'mul', + [ts.SyntaxKind.AsteriskEqualsToken]: 'mul', + [ts.SyntaxKind.SlashToken]: 'div', + [ts.SyntaxKind.SlashEqualsToken]: 'div', + [ts.SyntaxKind.AsteriskAsteriskToken]: 'pow', + [ts.SyntaxKind.AsteriskAsteriskEqualsToken]: 'pow', +}; + +const assignmentOperators = [ + ts.SyntaxKind.PlusEqualsToken, + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.SlashEqualsToken, +]; + +async function findTypeScriptFiles(dir: string): Promise { + const files: string[] = []; + + async function walk(currentDir: string): Promise { + const entries = await readdir(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + const fullPath = join(currentDir, entry.name); + + if (entry.isDirectory()) { + await walk(fullPath); + } else if (entry.isFile()) { + const ext = extname(entry.name); + if ( + (ext === '.ts' || ext === '.tsx') && + !entry.name.endsWith('.d.ts') && + !entry.name.endsWith('.d.tsx') && + !entry.name.endsWith('.tsnotover.ts') && + !entry.name.endsWith('.tsnotover.tsx') + ) { + files.push(fullPath); + } + } + } + } + + await walk(dir); + return files; +} + +type Pattern = + | 'left.op(right)' // e.g. vec + 2 => vec.add(2) + | 'right.op(left)' // e.g. 2 * vec => vec.mul(2) + | 'std.op(left, right)'; // e.g. 2 / vec => std.div(2, vec) + +function getOverloadPattern( + checker: ts.TypeChecker, + node: ts.BinaryExpression, +): Pattern | undefined { + const methodName = operatorToMethod[node.operatorToken.kind]; + if (!methodName) { + // Not overlaoded + return undefined; + } + + // Get the types of both operands + const leftType = checker.getTypeAtLocation(node.left); + const rightType = checker.getTypeAtLocation(node.right); + + if ( + !checker.__tsover__couldHaveOverloadedOperators( + node.left, + node.operatorToken.kind, + node.right, + leftType, + rightType, + ) + ) { + // Not overlaoded + return undefined; + } + + // For non-commutative operators, use the standard library function + if (methodName === 'div' || methodName === 'pow') { + return 'std.op(left, right)'; + } + + // Since other supported operators are commutative, prefer left method, fall back to right + const leftHasMethod = leftType.getProperty(methodName) !== undefined; + + return leftHasMethod ? 'left.op(right)' : 'right.op(left)'; +} + +function createProgram(allFiles: string[]): ts.Program { + const configPath = join(projectRoot, 'tsconfig.json'); + const configText = ts.sys.readFile(configPath); + + if (!configText) { + throw new Error(`Could not read tsconfig.json at ${configPath}`); + } + + const { config } = ts.parseConfigFileTextToJson(configPath, configText); + const parsedConfig = ts.parseJsonConfigFileContent( + config, + ts.sys, + projectRoot, + ); + + const compilerOptions: ts.CompilerOptions = { + ...parsedConfig.options, + noEmit: true, + }; + + const host = ts.createCompilerHost(compilerOptions, true); + + return ts.createProgram(allFiles, compilerOptions, host); +} + +function isStdDeclared(sourceFile: ts.SourceFile): boolean { + for (const stmt of sourceFile.statements) { + if (!ts.isImportDeclaration(stmt)) { + continue; + } + const moduleSpecifier = stmt.moduleSpecifier; + if (!ts.isStringLiteral(moduleSpecifier)) { + continue; + } + const namedBindings = stmt.importClause?.namedBindings; + // Case 1: import { std } from 'typegpu' + if ( + moduleSpecifier.text === 'typegpu' && + namedBindings && + ts.isNamedImports(namedBindings) && + namedBindings.elements.some((el) => el.name.text === 'std') + ) { + return true; + } + // Case 2: import * as std from 'typegpu/std' + if ( + moduleSpecifier.text === 'typegpu/std' && + namedBindings && + ts.isNamespaceImport(namedBindings) && + namedBindings.name.text === 'std' + ) { + return true; + } + } + return false; +} + +function transformFile( + sourceFilePath: string, + program: ts.Program, +): { code: string; hasChanges: boolean } { + const checker = program.getTypeChecker(); + const sourceFile = program.getSourceFile(sourceFilePath); + + if (!sourceFile) { + throw new Error(`Could not get source file for ${sourceFilePath}`); + } + + const sourceText = sourceFile.text; + const magic = new MagicString(sourceText); + let hasChanges = false; + let needsStd = false; + + function visit(node: ts.Node): void { + // Visit all children of the node first, then process the node itself + ts.forEachChild(node, visit); + + if (!ts.isBinaryExpression(node)) { + return; + } + + const pattern = getOverloadPattern(checker, node); + const methodName = operatorToMethod[node.operatorToken.kind]; + + if (!pattern || !methodName) { + return; + } + + hasChanges = true; + + const start = node.getStart(); + const end = node.getEnd(); + const leftStart = node.left.getStart(); + const leftEnd = node.left.getEnd(); + const rightStart = node.right.getStart(); + const rightEnd = node.right.getEnd(); + + const leftText = magic.slice(leftStart, leftEnd); + const rightText = magic.slice(rightStart, rightEnd); + + let replacement = ''; + if (pattern === 'std.op(left, right)') { + needsStd = true; + replacement = `std.${methodName}(${leftText}, ${rightText})`; + } else if (pattern === 'left.op(right)') { + replacement = `${leftText}.${methodName}(${rightText})`; + } else if (pattern === 'right.op(left)') { + replacement = `${rightText}.${methodName}(${leftText})`; + } else { + throw new Error(`Unsupported pattern: ${pattern}`); + } + + if (assignmentOperators.includes(node.operatorToken.kind)) { + // E.g. transforms a += b into a = a.add(b) + magic.overwrite(start, end, `${leftText} = ${replacement}`); + } else { + magic.overwrite(start, end, replacement); + } + } + + visit(sourceFile); + + if (needsStd && !isStdDeclared(sourceFile)) { + magic.prepend("import { std } from 'typegpu';\n"); + } + + return { code: magic.toString(), hasChanges }; +} + +async function main() { + console.log('Finding TypeScript files in examples directory...'); + + const allFiles = await findTypeScriptFiles(examplesDir); + + console.log(`Found ${allFiles.length} files to process`); + console.log('Creating TypeScript program...'); + + const program = createProgram(allFiles); + + console.log('Transforming files...'); + + let transformedCount = 0; + let errorCount = 0; + + for (const filePath of allFiles) { + try { + const { code, hasChanges } = transformFile(filePath, program); + + const ext = filePath.endsWith('.tsx') ? '.tsx' : '.ts'; + const baseName = basename(filePath, ext); + const dir = dirname(filePath); + const outputPath = join(dir, `${baseName}.tsnotover${ext}`); + + if (hasChanges) { + transformedCount++; + await writeFile(outputPath, code, 'utf-8'); + console.log(`Transformed: ${relative(projectRoot, filePath)}`); + } + } catch (error) { + errorCount++; + const errorMessage = error instanceof Error + ? error.message + : String(error); + console.error( + `Error processing ${relative(projectRoot, filePath)}: ${errorMessage}`, + ); + throw new Error( + `Failed to transform ${ + relative(projectRoot, filePath) + }: ${errorMessage}`, + { cause: error }, + ); + } + } + + console.log( + `\nDone! Transformed ${transformedCount} files, ${errorCount} errors.`, + ); +} + +main().catch((error) => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/apps/typegpu-docs/src/components/CodeEditor.tsx b/apps/typegpu-docs/src/components/CodeEditor.tsx index 7e3c7b4d7a..a11d327a18 100644 --- a/apps/typegpu-docs/src/components/CodeEditor.tsx +++ b/apps/typegpu-docs/src/components/CodeEditor.tsx @@ -84,7 +84,7 @@ const createCodeEditorComponent = ( > = {}; for (const file of example.tsFiles) { - tsFiles[`src/${file.path}`] = file.content; + tsFiles[`src/${file.path}`] = file.tsnotoverContent ?? file.content; } for (const file of common) { - tsFiles[`src/common/${file.path}`] = file.content; + tsFiles[`src/common/${file.path}`] = file.tsnotoverContent ?? file.content; } for (const key of Object.keys(tsFiles)) { diff --git a/apps/typegpu-docs/src/content/docs/ecosystem/typegpu-three/index.mdx b/apps/typegpu-docs/src/content/docs/ecosystem/typegpu-three/index.mdx index 94efced101..26f3ad740e 100644 --- a/apps/typegpu-docs/src/content/docs/ecosystem/typegpu-three/index.mdx +++ b/apps/typegpu-docs/src/content/docs/ecosystem/typegpu-three/index.mdx @@ -148,7 +148,7 @@ const material = new THREE.MeshBasicNodeMaterial(); material.colorNode = t3.toTSL(() => { 'use gpu'; - const coords = t3.uv().$.mul(2); + const coords = t3.uv().$ * 2; const pattern = perlin3d.sample(d.vec3f(coords, t3.time.$ * 0.2)); return d.vec4f(std.tanh(pattern * 5), 0.2, 0.4, 1); }); @@ -244,12 +244,28 @@ Loop({ start: ptrStart, end: ptrEnd, type: 'uint', condition: '<' }, ({ i }) => ``` TypeGPU: -```ts +```ts twoslash +'use tsover'; +import * as t3 from '@typegpu/three'; +import * as THREE from 'three/webgpu'; +import { d, std } from 'typegpu'; +type TSLStorageAccessor = t3.TSLAccessor< + T, + THREE.StorageBufferNode +>; +declare const springListBuffer: TSLStorageAccessor>; +declare const springForceBuffer: TSLStorageAccessor>; +declare const springVertexIdBuffer: TSLStorageAccessor> +declare const ptrStart: number; +declare const ptrEnd: number; +declare const idx: number; +declare let force: d.v3f; +// ---cut--- for (let i = ptrStart; i < ptrEnd; i++) { const springId = springListBuffer.$[i]; const springForce = springForceBuffer.$[springId]; const springVertexIds = springVertexIdBuffer.$[springId]; const factor = std.select(-1, 1, springVertexIds.x === idx); - force = force.add(springForce.mul(d.f32(factor))); + force += springForce * factor; } ``` diff --git a/apps/typegpu-docs/src/examples/exampleContent.ts b/apps/typegpu-docs/src/examples/exampleContent.ts index 1e0f2b74df..94133ef2c7 100644 --- a/apps/typegpu-docs/src/examples/exampleContent.ts +++ b/apps/typegpu-docs/src/examples/exampleContent.ts @@ -47,26 +47,13 @@ function pathToExampleKey(path: string): string { ); } -function globToExampleFiles( - record: Record, -): Record { - return R.pipe( - record, - R.mapValues((content, key): ExampleSrcFile => { - const pathRelToExamples = pathe.relative('./', key); - const categoryDir = pathRelToExamples.split('/')[0]; - const exampleDir = pathRelToExamples.split('/')[1]; - const examplePath = pathe.join(categoryDir, exampleDir); - - return { - exampleKey: pathToExampleKey(key), - path: pathe.relative(examplePath, key), - content, - }; - }), - R.values(), - R.groupBy(R.prop('exampleKey')), - ); +function pathToRelativePath(path: string): string { + const pathRelToExamples = pathe.relative('./', path); + const categoryDir = pathRelToExamples.split('/')[0]; + const exampleDir = pathRelToExamples.split('/')[1]; + const examplePath = pathe.join(categoryDir, exampleDir); + + return pathe.relative(examplePath, path); } const metaFiles = R.pipe( @@ -77,6 +64,14 @@ const metaFiles = R.pipe( R.mapKeys(pathToExampleKey), ); +// Files that were generated by stripping away use of overloaded operators in .ts files +// './//.tsnotover.ts' +const exampleTsnotoverFiles = import.meta.glob('./*/*/*.tsnotover.ts', { + query: 'raw', + eager: true, + import: 'default', +}) as Record; + const exampleTsFiles = R.pipe( // './//.ts' import.meta.glob('./*/*/*.ts', { @@ -84,7 +79,15 @@ const exampleTsFiles = R.pipe( eager: true, import: 'default', }) as Record, - globToExampleFiles, + R.entries(), + R.filter(([key]) => !key.endsWith('.tsnotover.ts')), + R.map(([key, content]): ExampleSrcFile => ({ + exampleKey: pathToExampleKey(key), + path: pathToRelativePath(key), + content, + tsnotoverContent: exampleTsnotoverFiles[`${key}notover.ts`], + })), + R.groupBy(R.prop('exampleKey')), ); const tsFilesImportFunctions = R.pipe( @@ -101,7 +104,13 @@ const htmlFiles = R.pipe( eager: true, import: 'default', }) as Record, - globToExampleFiles, + R.entries(), + R.map(([key, content]): ExampleSrcFile => ({ + exampleKey: pathToExampleKey(key), + path: pathToRelativePath(key), + content, + })), + R.groupBy(R.prop('exampleKey')), ); const thumbnailFiles = R.pipe( diff --git a/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts b/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts index 26bd494ad0..f753579d8b 100644 --- a/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts +++ b/apps/typegpu-docs/src/examples/image-processing/ascii-filter/index.ts @@ -30,8 +30,9 @@ const displayModes = { * https://www.shadertoy.com/view/lssGDj */ const characterFn = tgpu.fn([d.u32, d.vec2f], d.f32)((n, p) => { + 'use gpu'; // Transform texture coordinates to character bitmap coordinates (5x5 grid) - const pos = std.floor(p.mul(d.vec2f(-4, 4)).add(2.5)); + const pos = std.floor(p * d.vec2f(-4, 4) + 2.5); // Check if position is outside the 5x5 character bitmap bounds if (pos.x < 0 || pos.x > 4 || pos.y < 0 || pos.y > 4) { @@ -58,17 +59,16 @@ const pipeline = root.createRenderPipeline({ */ fragment: ({ uv }) => { 'use gpu'; - const uv2 = std.mul(uvTransformBuffer.$, uv.sub(0.5)).add(0.5); + const uv2 = uvTransformBuffer.$ * (uv - 0.5) + 0.5; const textureSize = d.vec2f( std.textureDimensions(layout.$.externalTexture), ); - const pix = uv2.mul(textureSize); + const pix = uv2 * textureSize; const cellSize = d.f32(glyphSize.$); const halfCell = cellSize * 0.5; - const blockCoord = std.floor(pix.div(cellSize)) - .mul(cellSize).div(textureSize); + const blockCoord = std.floor(pix / cellSize) * cellSize / textureSize; const color = std.textureSampleBaseClampToEdge( layout.$.externalTexture, @@ -77,7 +77,7 @@ const pipeline = root.createRenderPipeline({ ); const rawGray = 0.3 * color.x + 0.59 * color.y + 0.11 * color.z; - const gray = std.pow(rawGray, gammaCorrection.$); + const gray = rawGray ** gammaCorrection.$; let n = d.u32(4096); if (charsetExtended.$ === 0) { @@ -143,7 +143,7 @@ const pipeline = root.createRenderPipeline({ let resultColor = d.vec3f(1); // Color mode if (displayMode.$ === displayModes.color) { - resultColor = color.mul(charValue).rgb; + resultColor = color.rgb * charValue; } // Grayscale mode if (displayMode.$ === displayModes.grayscale) { diff --git a/apps/typegpu-docs/src/examples/image-processing/background-segmentation/shaders.ts b/apps/typegpu-docs/src/examples/image-processing/background-segmentation/shaders.ts index 3569da6019..b6737c0926 100644 --- a/apps/typegpu-docs/src/examples/image-processing/background-segmentation/shaders.ts +++ b/apps/typegpu-docs/src/examples/image-processing/background-segmentation/shaders.ts @@ -49,16 +49,17 @@ export const computeFn = tgpu.computeFn({ in: { wid: d.builtin.workgroupId, lid: d.builtin.localInvocationId }, workgroupSize: [32, 1, 1], })(({ wid, lid }) => { + 'use gpu'; const filterOffset = d.i32((filterDim - 1) / 2); const dims = d.vec2i(std.textureDimensions(blurLayout.$.inTexture)); - const baseIndex = d.vec2i( - wid.xy.mul(d.vec2u(blockDim, 4)).add(lid.xy.mul(d.vec2u(4, 1))), - ).sub(d.vec2i(filterOffset, 0)); + const baseIndex = + d.vec2i(wid.xy * d.vec2u(blockDim, 4) + lid.xy * d.vec2u(4, 1)) - + d.vec2i(filterOffset, 0); // Load a tile of pixels into shared memory for (let r = 0; r < 4; r++) { for (let c = 0; c < 4; c++) { - let loadIndex = baseIndex.add(d.vec2i(c, r)); + let loadIndex = baseIndex + d.vec2i(c, r); if (flipAccess.$) { loadIndex = loadIndex.yx; } @@ -66,7 +67,7 @@ export const computeFn = tgpu.computeFn({ tileData.$[r][lid.x * 4 + d.u32(c)] = std.textureSampleLevel( blurLayout.$.inTexture, blurLayout.$.sampler, - d.vec2f(d.vec2f(loadIndex).add(d.vec2f(0.5)).div(d.vec2f(dims))), + (d.vec2f(loadIndex) + 0.5) / d.vec2f(dims), 0, ).rgb; } @@ -77,7 +78,7 @@ export const computeFn = tgpu.computeFn({ // Apply the horizontal blur filter and write to the output texture for (let r = 0; r < 4; r++) { for (let c = 0; c < 4; c++) { - let writeIndex = baseIndex.add(d.vec2i(c, r)); + let writeIndex = baseIndex + d.vec2i(c, r); if (flipAccess.$) { writeIndex = writeIndex.yx; } @@ -91,7 +92,7 @@ export const computeFn = tgpu.computeFn({ let acc = d.vec3f(); for (let f = 0; f < filterDim; f++) { const i = center + f - filterOffset; - acc = acc.add(tileData.$[r][i].mul(1 / filterDim)); + acc += tileData.$[r][i] / filterDim; } std.textureStore(blurLayout.$.outTexture, writeIndex, d.vec4f(acc, 1)); } diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts index d69c6547e0..152667c077 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/compute.ts @@ -20,31 +20,28 @@ export const simulate = (fishIndex: number) => { } const other = layout.$.currentFishData[i]; - const dist = std.length(fishData.position.sub(other.position)); + const dist = std.distance(fishData.position, other.position); if (dist < layout.$.fishBehavior.separationDist) { - separation = separation.add(fishData.position.sub(other.position)); + separation += fishData.position - other.position; } if (dist < layout.$.fishBehavior.alignmentDist) { - alignment = alignment.add(other.direction); + alignment = alignment + other.direction; alignmentCount = alignmentCount + 1; } if (dist < layout.$.fishBehavior.cohesionDist) { - cohesion = cohesion.add(other.position); + cohesion = cohesion + other.position; cohesionCount = cohesionCount + 1; } } if (alignmentCount > 0) { - alignment = alignment.mul(1 / d.f32(alignmentCount)); + alignment = alignment / alignmentCount; } if (cohesionCount > 0) { - cohesion = std.sub( - std.mul(1 / d.f32(cohesionCount), cohesion), - fishData.position, - ); + cohesion = cohesion / cohesionCount - fishData.position; } for (let i = 0; i < 3; i += 1) { const repulsion = d.vec3f(); - repulsion[i] = 1.0; + repulsion[i] = 1; const axisAquariumSize = p.aquariumSize[i] / 2; const axisPosition = fishData.position[i]; @@ -52,50 +49,34 @@ export const simulate = (fishIndex: number) => { if (axisPosition > axisAquariumSize - distance) { const str = axisPosition - (axisAquariumSize - distance); - wallRepulsion = wallRepulsion.sub(repulsion.mul(str)); + wallRepulsion = wallRepulsion - (repulsion * str); } if (axisPosition < -axisAquariumSize + distance) { const str = -axisAquariumSize + distance - axisPosition; - wallRepulsion = wallRepulsion.add(repulsion.mul(str)); + wallRepulsion = wallRepulsion + (repulsion * str); } } - const proj = projectPointOnLine( - fishData.position, - layout.$.mouseRay, - ); - const diff = fishData.position.sub(proj); + const proj = projectPointOnLine(fishData.position, layout.$.mouseRay); + const diff = fishData.position - proj; const limit = p.fishMouseRayRepulsionDistance; const str = std.pow(2, std.clamp(limit - std.length(diff), 0, limit)) - 1; - rayRepulsion = std.normalize(diff).mul(str); + rayRepulsion = std.normalize(diff) * str; let direction = d.vec3f(fishData.direction); - direction = direction.add( - separation.mul(layout.$.fishBehavior.separationStr), - ); - direction = direction.add( - alignment.mul(layout.$.fishBehavior.alignmentStr), - ); - direction = direction.add( - cohesion.mul(layout.$.fishBehavior.cohesionStr), - ); - direction = direction.add( - wallRepulsion.mul(p.fishWallRepulsionStrength), - ); - direction = direction.add( - rayRepulsion.mul(p.fishMouseRayRepulsionStrength), - ); - direction = std.normalize(direction).mul( - std.clamp(std.length(fishData.direction), 0, 0.01), - ); + direction += separation * layout.$.fishBehavior.separationStr; + direction += alignment * layout.$.fishBehavior.alignmentStr; + direction += cohesion * layout.$.fishBehavior.cohesionStr; + direction += wallRepulsion * p.fishWallRepulsionStrength; + direction += rayRepulsion * p.fishMouseRayRepulsionStrength; + direction = std.normalize(direction) * + (std.clamp(std.length(fishData.direction), 0, 0.01)); - const translation = direction.mul( - d.f32(std.min(999, layout.$.timePassed)) / 8, - ); + const translation = direction * (std.min(999, layout.$.timePassed) / 8); const nextFishData = layout.$.nextFishData[fishIndex]; - nextFishData.position = fishData.position.add(translation); + nextFishData.position = fishData.position + translation; nextFishData.direction = d.vec3f(direction); }; diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts index c619569fa7..e4a46a12fd 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/render.ts @@ -12,6 +12,7 @@ export const vertexShader = tgpu.vertexFn({ in: { ...ModelVertexInput, instanceIndex: d.builtin.instanceIndex }, out: ModelVertexOutput, })((input) => { + 'use gpu'; // rotate the model so that it aligns with model's direction of movement // https://simple.wikipedia.org/wiki/Pitch,_yaw,_and_roll const currentModelData = layout.$.modelData[input.instanceIndex]; @@ -42,32 +43,18 @@ export const vertexShader = tgpu.vertexFn({ const yawMatrix = d.mat4x4f.rotationY(yaw); const translationMatrix = d.mat4x4f.translation(currentModelData.position); - const worldPosition = std.mul( - translationMatrix, - std.mul( - yawMatrix, - std.mul( - pitchMatrix, - std.mul( - scaleMatrix, - d.vec4f(wavedVertex.position, 1), - ), - ), - ), - ); + const worldPosition = translationMatrix * yawMatrix * pitchMatrix * + scaleMatrix * d.vec4f(wavedVertex.position, 1); // calculate where the normal vector points to const worldNormal = std.normalize( - std.mul(yawMatrix, std.mul(pitchMatrix, d.vec4f(wavedVertex.normal, 1))) - .xyz, + (yawMatrix * pitchMatrix * d.vec4f(wavedVertex.normal, 1)).xyz, ); // project the world position into the camera const worldPositionUniform = worldPosition; - const canvasPosition = std.mul( - layout.$.camera.projection, - std.mul(layout.$.camera.view, worldPositionUniform), - ); + const canvasPosition = layout.$.camera.projection * layout.$.camera.view * + worldPositionUniform; return { canvasPosition: canvasPosition, @@ -95,32 +82,28 @@ export const fragmentShader = tgpu.fragmentFn({ input.textureUV, ); const textureColor = textureColorWithAlpha.rgb; - - const ambient = std.mul(0.5, std.mul(textureColor, p.lightColor)); + const ambient = 0.5 * textureColor * p.lightColor; const cosTheta = std.dot(input.worldNormal, p.lightDirection); - const diffuse = std.mul( - std.max(0, cosTheta), - std.mul(textureColor, p.lightColor), - ); + const diffuse = std.max(0, cosTheta) * textureColor * p.lightColor; const viewSource = std.normalize( - std.sub(layout.$.camera.position.xyz, input.worldPosition), + layout.$.camera.position.xyz - input.worldPosition, ); const reflectSource = std.normalize( - std.reflect(std.mul(-1, p.lightDirection), input.worldNormal), + std.reflect(-1 * p.lightDirection, input.worldNormal), ); const specularStrength = std.pow( std.max(0, std.dot(viewSource, reflectSource)), 16, ); - const specular = std.mul(specularStrength, p.lightColor); + const specular = specularStrength * p.lightColor; - const lightedColor = std.add(ambient, std.add(diffuse, specular)); + const lightedColor = ambient + diffuse + specular; // apply desaturation const distanceFromCamera = std.length( - std.sub(layout.$.camera.position.xyz, input.worldPosition), + layout.$.camera.position.xyz - input.worldPosition, ); let desaturatedColor = d.vec3f(lightedColor); diff --git a/apps/typegpu-docs/src/examples/rendering/3d-fish/tgsl-helpers.ts b/apps/typegpu-docs/src/examples/rendering/3d-fish/tgsl-helpers.ts index 8860c939c3..eb50ab93c6 100644 --- a/apps/typegpu-docs/src/examples/rendering/3d-fish/tgsl-helpers.ts +++ b/apps/typegpu-docs/src/examples/rendering/3d-fish/tgsl-helpers.ts @@ -1,12 +1,11 @@ -import { d } from 'typegpu'; -import { add, cos, dot, normalize, sin } from 'typegpu/std'; +import { d, std } from 'typegpu'; import type { Line3 } from './schemas.ts'; export const projectPointOnLine = (point: d.v3f, line: Line3): d.v3f => { 'use gpu'; - const pointVector = point.sub(line.origin); - const projection = dot(pointVector, line.dir); - return line.origin.add(line.dir.mul(projection)); + const pointVector = point - line.origin; + const projection = std.dot(pointVector, line.dir); + return line.origin + line.dir * projection; }; type PosAndNormal = d.Infer; @@ -27,18 +26,15 @@ export const applySinWave = ( // z += sin(index + (time / a + x) / b) / c const posMod = d.vec3f(); - posMod.z = sin(d.f32(index) + (time / a + vertex.position.x) / b) / c; + posMod.z = std.sin(d.f32(index) + (time / a + vertex.position.x) / b) / c; - const coeff = cos(d.f32(index) + (time / a + vertex.position.x) / b) / c; - const newOX = normalize(d.vec3f(1, 0, coeff)); + const coeff = std.cos(d.f32(index) + (time / a + vertex.position.x) / b) / c; + const newOX = std.normalize(d.vec3f(1, 0, coeff)); const newOZ = d.vec3f(-newOX.z, 0, newOX.x); - const newNormalXZ = add( - newOX.mul(vertex.normal.x), - newOZ.mul(vertex.normal.z), - ); + const newNormalXZ = newOX * vertex.normal.x + newOZ * vertex.normal.z; const wavedNormal = d.vec3f(newNormalXZ.x, vertex.normal.y, newNormalXZ.z); - const wavedPosition = vertex.position.add(posMod); + const wavedPosition = vertex.position + posMod; return PosAndNormal({ position: wavedPosition, diff --git a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts index 5d8ea746f7..3296bbba18 100644 --- a/apps/typegpu-docs/src/examples/rendering/caustics/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/caustics/index.ts @@ -22,17 +22,17 @@ const mainVertex = tgpu.vertexFn({ const tilePattern = (uv: d.v2f): number => { 'use gpu'; const tiledUv = std.fract(uv); - const proximity = std.abs(std.sub(std.mul(tiledUv, 2), 1)); + const proximity = std.abs((tiledUv * 2) - 1); const maxProximity = std.max(proximity.x, proximity.y); - return std.saturate(std.pow(1 - maxProximity, 0.6) * 5); + return std.saturate(((1 - maxProximity) ** 0.6) * 5); }; const caustics = (uv: d.v2f, time: number, profile: d.v3f): d.v3f => { 'use gpu'; - const distortion = perlin3d.sample(d.vec3f(std.mul(uv, 0.5), time * 0.2)); + const distortion = perlin3d.sample(d.vec3f(uv * 0.5, time * 0.2)); // Distorting UV coordinates - const uv2 = std.add(uv, distortion); - const noise = std.abs(perlin3d.sample(d.vec3f(std.mul(uv2, 5), time))); + const uv2 = uv + distortion; + const noise = std.abs(perlin3d.sample(d.vec3f(uv2 * 5, time))); return std.pow(d.vec3f(1 - noise), profile); }; @@ -65,6 +65,7 @@ const mainFragment = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { + 'use gpu'; /** * A transformation matrix that skews the perspective a bit * when applied to UV coordinates @@ -73,8 +74,8 @@ const mainFragment = tgpu.fragmentFn({ d.vec2f(std.cos(angle), std.sin(angle)), d.vec2f(-std.sin(angle) * 10 + uv.x * 3, std.cos(angle) * 5), ); - const skewedUv = std.mul(skewMat, uv); - const tile = tilePattern(std.mul(skewedUv, tileDensity.$)); + const skewedUv = skewMat * uv; + const tile = tilePattern(skewedUv * tileDensity.$); const albedo = std.mix(d.vec3f(0.1), d.vec3f(1), tile); // Transforming coordinates to simulate perspective squash @@ -83,49 +84,38 @@ const mainFragment = tgpu.fragmentFn({ std.pow((uv.y * 1.5 + 0.1) * 1.5, 3) * 1, ); // Generating two layers of caustics (large scale, and small scale) - const c1 = std.mul( - caustics(cuv, time.$ * 0.2, /* profile */ d.vec3f(4, 4, 1)), + const c1 = caustics(cuv, time.$ * 0.2, /* profile */ d.vec3f(4, 4, 1)) * // Tinting - d.vec3f(0.4, 0.65, 1), - ); - const c2 = std.mul( - caustics(std.mul(cuv, 2), time.$ * 0.4, /* profile */ d.vec3f(16, 1, 4)), + d.vec3f(0.4, 0.65, 1); + const c2 = caustics(cuv * 2, time.$ * 0.4, /* profile */ d.vec3f(16, 1, 4)) * // Tinting - d.vec3f(0.18, 0.3, 0.5), - ); + d.vec3f(0.18, 0.3, 0.5); // -- BLEND -- - const blendCoord = d.vec3f(std.mul(uv, d.vec2f(5, 10)), time.$ * 0.2 + 5); + const blendCoord = d.vec3f(uv * d.vec2f(5, 10), time.$ * 0.2 + 5); // A smooth blending factor, so that caustics only appear at certain spots const blend = std.saturate(perlin3d.sample(blendCoord) + 0.3); // -- FOG -- - const noFogColor = std.mul( - albedo, - std.mix(ambientColor, std.add(c1, c2), blend), - ); + const noFogColor = albedo * std.mix(ambientColor, c1 + c2, blend); // Fog blending factor, based on the height of the pixels - const fog = std.min(std.pow(uv.y, 0.5) * 1.2, 1); + const fog = std.min((uv.y ** 0.5) * 1.2, 1); // -- GOD RAYS -- - const godRayUv = std.mul(std.mul(rotateXY(-0.3), uv), d.vec2f(15, 3)); - const godRayFactor = std.pow(uv.y, 1); - const godRay1 = std.mul( - std.add(perlin3d.sample(d.vec3f(godRayUv, time.$ * 0.5)), 1), + const godRayUv = rotateXY(-0.3) * uv * d.vec2f(15, 3); + const godRayFactor = uv.y; + const godRay1 = (perlin3d.sample(d.vec3f(godRayUv, time.$ * 0.5)) + 1) * // Tinting - std.mul(d.vec3f(0.18, 0.3, 0.5), godRayFactor), - ); - const godRay2 = std.mul( - std.add(perlin3d.sample(d.vec3f(std.mul(godRayUv, 2), time.$ * 0.3)), 1), + d.vec3f(0.18, 0.3, 0.5) * godRayFactor; + const godRay2 = (perlin3d.sample(d.vec3f(godRayUv * 2, time.$ * 0.3)) + 1) * // Tinting - std.mul(d.vec3f(0.18, 0.3, 0.5), godRayFactor * 0.4), - ); - const godRays = std.add(godRay1, godRay2); + d.vec3f(0.18, 0.3, 0.5) * godRayFactor * 0.4; + const godRays = godRay1 + godRay2; - return d.vec4f(std.add(std.mix(noFogColor, fogColor, fog), godRays), 1); + return d.vec4f(std.mix(noFogColor, fogColor, fog) + godRays, 1); }); const canvas = document.querySelector('canvas') as HTMLCanvasElement; diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts index 10ad41e2cd..94cad5f03a 100644 --- a/apps/typegpu-docs/src/examples/rendering/clouds/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/clouds/index.ts @@ -55,47 +55,40 @@ const bindGroup = root.createBindGroup(cloudsLayout, { sampler, }); -const mainFragment = tgpu.fragmentFn({ - in: { uv: d.vec2f }, - out: d.vec4f, -})(({ uv }) => { - randf.seed2(uv.mul(cloudsLayout.$.params.time)); - const screenRes = resolutionUniform.$; - const aspect = screenRes.x / screenRes.y; - - let screenPos = std.mul(std.sub(uv, 0.5), 2.0); - screenPos = d.vec2f( - screenPos.x * std.max(aspect, 1.0), - screenPos.y * std.max(1.0 / aspect, 1.0), - ); - - const sunDir = std.normalize(SUN_DIRECTION); - const time = cloudsLayout.$.params.time; - const rayOrigin = d.vec3f( - std.sin(time * 0.6) * 0.5, - std.cos(time * 0.8) * 0.5 - 1, - time * WIND_SPEED, - ); - const rayDir = std.normalize(d.vec3f(screenPos.x, screenPos.y, FOV_FACTOR)); - - const sunDot = std.clamp(std.dot(rayDir, sunDir), 0.0, 1.0); - const sunGlow = std.pow( - sunDot, - 1.0 / (SUN_BRIGHTNESS * SUN_BRIGHTNESS * SUN_BRIGHTNESS), - ); - - let skyCol = std.sub(SKY_HORIZON, std.mul(SKY_ZENITH_TINT, rayDir.y * 0.35)); - skyCol = std.add(skyCol, std.mul(SUN_GLOW, sunGlow)); - - const cloudCol = raymarch(rayOrigin, rayDir, sunDir); - const finalCol = std.add(std.mul(skyCol, 1.1 - cloudCol.a), cloudCol.rgb); - - return d.vec4f(finalCol, 1.0); -}); - const pipeline = root.createRenderPipeline({ vertex: common.fullScreenTriangle, - fragment: mainFragment, + fragment: ({ uv }) => { + 'use gpu'; + randf.seed2(uv * cloudsLayout.$.params.time); + const screenRes = resolutionUniform.$; + const aspect = screenRes.x / screenRes.y; + + let screenPos = (uv - 0.5) * 2; + screenPos = d.vec2f( + screenPos.x * std.max(aspect, 1), + screenPos.y * std.max(1 / aspect, 1), + ); + + const sunDir = std.normalize(SUN_DIRECTION); + const time = cloudsLayout.$.params.time; + const rayOrigin = d.vec3f( + std.sin(time * 0.6) * 0.5, + std.cos(time * 0.8) * 0.5 - 1, + time * WIND_SPEED, + ); + const rayDir = std.normalize(d.vec3f(screenPos.x, screenPos.y, FOV_FACTOR)); + + const sunDot = std.saturate(std.dot(rayDir, sunDir)); + const sunGlow = sunDot ** (1 / SUN_BRIGHTNESS ** 3); + + let skyCol = SKY_HORIZON - SKY_ZENITH_TINT * rayDir.y * 0.35; + skyCol += SUN_GLOW * sunGlow; + + const cloudCol = raymarch(rayOrigin, rayDir, sunDir); + const finalCol = skyCol * (1.1 - cloudCol.a) + cloudCol.rgb; + + return d.vec4f(finalCol, 1.0); + }, targets: { format: presentationFormat }, }); diff --git a/apps/typegpu-docs/src/examples/rendering/clouds/utils.ts b/apps/typegpu-docs/src/examples/rendering/clouds/utils.ts index 71a1bec9b5..1108708372 100644 --- a/apps/typegpu-docs/src/examples/rendering/clouds/utils.ts +++ b/apps/typegpu-docs/src/examples/rendering/clouds/utils.ts @@ -23,13 +23,15 @@ const sampleDensity = tgpu.fn([d.vec3f], d.f32)((pos) => { return std.saturate(fbm(pos) + coverage) - 0.5; }); -const sampleDensityCheap = tgpu.fn([d.vec3f], d.f32)((pos) => { - const noise = noise3d(std.mul(pos, CLOUD_FREQUENCY)) * CLOUD_AMPLITUDE; - return std.clamp(noise + CLOUD_COVERAGE - 0.5, 0.0, 1.0); -}); +const sampleDensityCheap = (pos: d.v3f): number => { + 'use gpu'; + const noise = noise3d(pos * CLOUD_FREQUENCY) * CLOUD_AMPLITUDE; + return std.saturate(noise + CLOUD_COVERAGE - 0.5); +}; export const raymarch = tgpu.fn([d.vec3f, d.vec3f, d.vec3f], d.vec4f)( (rayOrigin, rayDir, sunDir) => { + 'use gpu'; let accum = d.vec4f(); const params = cloudsLayout.$.params; @@ -40,27 +42,23 @@ export const raymarch = tgpu.fn([d.vec3f, d.vec3f, d.vec3f], d.vec4f)( let dist = randf.sample() * stepSize; for (let i = 0; i < maxSteps; i++) { - const samplePos = std.add(rayOrigin, std.mul(rayDir, dist * maxDepth)); + const samplePos = rayOrigin + rayDir * dist * maxDepth; const cloudDensity = sampleDensity(samplePos); if (cloudDensity > 0.0) { - const shadowPos = std.add(samplePos, sunDir); + const shadowPos = samplePos + sunDir; const shadowDensity = sampleDensityCheap(shadowPos); - const shadow = std.clamp(cloudDensity - shadowDensity, 0.0, 1.0); + const shadow = std.saturate(cloudDensity - shadowDensity); const lightVal = std.mix(0.3, 1.0, shadow); - const light = std.add( - std.mul(SKY_AMBIENT, 1.1), - std.mul(SUN_COLOR, lightVal * SUN_BRIGHTNESS), - ); + const light = SKY_AMBIENT * 1.1 + + SUN_COLOR * lightVal * SUN_BRIGHTNESS; const color = std.mix(CLOUD_BRIGHT, CLOUD_DARK, cloudDensity); - const lit = std.mul(color, light); + const lit = color * light; - const contrib = std.mul( - d.vec4f(lit, 1), - cloudDensity * (LIGHT_ABSORPTION - accum.a), - ); - accum = std.add(accum, contrib); + const contrib = d.vec4f(lit, 1) * cloudDensity * + (LIGHT_ABSORPTION - accum.a); + accum += contrib; if (accum.a >= LIGHT_ABSORPTION - 0.001) { break; @@ -73,12 +71,13 @@ export const raymarch = tgpu.fn([d.vec3f, d.vec3f, d.vec3f], d.vec4f)( ); const fbm = tgpu.fn([d.vec3f], d.f32)((pos) => { + 'use gpu'; let sum = d.f32(); let amp = d.f32(CLOUD_AMPLITUDE); let freq = d.f32(CLOUD_FREQUENCY); for (let i = 0; i < FBM_OCTAVES; i++) { - sum += noise3d(std.mul(pos, freq)) * amp; + sum += noise3d(pos * freq) * amp; amp *= FBM_PERSISTENCE; freq *= FBM_LACUNARITY; } @@ -86,39 +85,31 @@ const fbm = tgpu.fn([d.vec3f], d.f32)((pos) => { }); const noise3d = tgpu.fn([d.vec3f], d.f32)((pos) => { + 'use gpu'; const idx = std.floor(pos); const frac = std.fract(pos); - const smooth = std.mul(std.mul(frac, frac), std.sub(3.0, std.mul(2.0, frac))); + const smooth = frac * frac * (3 - 2 * frac); const texCoord0 = std.fract( - std.div( - std.add(std.add(idx.xy, frac.xy), std.mul(NOISE_Z_OFFSET, idx.z)), - NOISE_TEXTURE_SIZE, - ), + (idx.xy + frac.xy + NOISE_Z_OFFSET * idx.z) / NOISE_TEXTURE_SIZE, ); const texCoord1 = std.fract( - std.div( - std.add( - std.add(idx.xy, frac.xy), - std.mul(NOISE_Z_OFFSET, std.add(idx.z, 1.0)), - ), - NOISE_TEXTURE_SIZE, - ), + (idx.xy + frac.xy + NOISE_Z_OFFSET * (idx.z + 1)) / NOISE_TEXTURE_SIZE, ); const val0 = std.textureSampleLevel( cloudsLayout.$.noiseTexture, cloudsLayout.$.sampler, texCoord0, - 0.0, + 0, ).x; const val1 = std.textureSampleLevel( cloudsLayout.$.noiseTexture, cloudsLayout.$.sampler, texCoord1, - 0.0, + 0, ).x; - return std.mix(val0, val1, smooth.z) * 2.0 - 1.0; + return std.mix(val0, val1, smooth.z) * 2 - 1; }); diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/constants.ts b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/constants.ts new file mode 100644 index 0000000000..6e91a19a25 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/constants.ts @@ -0,0 +1,18 @@ +export const MAX_STEPS = 48; +export const MAX_DIST = 5; +export const SURF_DIST = 0.001; +export const PI = Math.PI; +export const BLUR_RADIUS = 8; +export const TAA_BLEND = 0.85; + +export function halton(index: number, base: number): number { + let result = 0; + let f = 1 / base; + let i = index; + while (i > 0) { + result += f * (i % base); + i = Math.floor(i / base); + f /= base; + } + return result; +} diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.html b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.html new file mode 100644 index 0000000000..581d6789f8 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.html @@ -0,0 +1 @@ + diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.ts b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.ts new file mode 100644 index 0000000000..cc872fb4c0 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/index.ts @@ -0,0 +1,409 @@ +import * as sdf from '@typegpu/sdf'; +import tgpu, { common, d, std } from 'typegpu'; +import { Camera, setupOrbitCamera } from '../../common/setup-orbit-camera.ts'; +import { Material, Ray } from './types.ts'; +import { perlin2d, perlin3d } from '@typegpu/noise'; +import { shade, SUN } from './pbr.ts'; + +const root = await tgpu.init(); +const canvas = document.querySelector('canvas') as HTMLCanvasElement; +const context = root.configureContext({ canvas, alphaMode: 'premultiplied' }); + +const time = root.createUniform(d.f32); +const resolution = root.createUniform(d.vec2f); +const cameraUniform = root.createUniform(Camera); + +const MAX_STEPS = 1000; +const MAX_DIST = 30; +const SURF_DIST = 0.001; + +const skyColor = d.vec4f(0.7, 0.8, 0.9, 1); + +// Structure to hold both distance and color +type Shape = d.InferGPU; +const Shape = d.struct({ + material: Material, + dist: d.f32, +}); + +const checkerBoard = (uv: d.v2f): number => { + 'use gpu'; + const fuv = std.floor(uv); + return std.abs(fuv.x + fuv.y) % 2; +}; + +const smoothShapeUnion = (a: Shape, b: Shape, k: number): Shape => { + 'use gpu'; + const h = std.max(k - std.abs(a.dist - b.dist), 0) / k; + const m = h * h; + + // Smooth min for distance + const dist = std.min(a.dist, b.dist) - m * k * (1 / d.f32(4)); + + // Blend colors based on relative distances and smoothing + const weight = m + std.select(0, 1 - m, a.dist > b.dist); + const albedo = std.mix(a.material.albedo, b.material.albedo, weight); + const ao = std.mix(a.material.ao, b.material.ao, weight); + const metallic = std.mix(a.material.metallic, b.material.metallic, weight); + const roughness = std.mix(a.material.roughness, b.material.roughness, weight); + + return Shape({ + dist, + material: { albedo, ao, metallic, roughness }, + }); +}; + +const shapeUnion = (a: Shape, b: Shape) => { + 'use gpu'; + // deno-fmt-ignore + return Shape({ + material: { + albedo: std.select(a.material.albedo, b.material.albedo, a.dist > b.dist), + ao: std.select(a.material.ao, b.material.ao, a.dist > b.dist), + metallic: std.select(a.material.metallic, b.material.metallic, a.dist > b.dist), + roughness: std.select(a.material.roughness, b.material.roughness, a.dist > b.dist), + }, + dist: std.min(a.dist, b.dist), + }); +}; + +const sdRing = (_p: d.v2f, n: d.v2f, r: number, th: number): number => { + 'use gpu'; + let p = d.vec2f(_p); + p.x = std.abs(p.x); + p = d.mat2x2f(n.x, n.y, -n.y, n.x) * p; + return std.max( + std.abs(std.length(p) - r) - th * 0.5, + std.length(d.vec2f(p.x, std.max(0.0, std.abs(r - p.y) - th * 0.5))) * + std.sign(p.x), + ); +}; + +const PIE = { + baseHalfHeight: 0.005, + cheeseHalfHeight: 0.005, + radius: 0.5, + baseRoundness: 0.01, + cheeseRoundness: 0.0025, + + stringDomain: 0.06, + stringHalfWidth: 0.012, + stringThickness: 0.002, +}; + +const cheeseMaterial = Material({ + albedo: d.vec3f(1, 0.9, 0.5), + ao: 1, + metallic: 0.0, + roughness: 0.1, +}); + +const crustMaterialBase = Material({ + albedo: d.vec3f(0.35, 0.2, 0.1), + ao: 1, + metallic: 0.0, + roughness: 1, +}); + +const pepperoniMaterialBase = Material({ + albedo: d.vec3f(0.6, 0.1, 0.1), + ao: 0.2, + metallic: 0.0, + roughness: 1, +}); + +const sdPizzaCheeseStrings = (p: d.v3f): Shape => { + 'use gpu'; + let dp = rotateXZ(0.5) * p; + dp += perlin2d.sample(p.xz * 10) * 0.05; + const mp = (std.fract(dp.xz / PIE.stringDomain) - 0.5) * PIE.stringDomain; + const linesSd = std.min(std.abs(mp.x), std.abs(mp.y)) - PIE.stringHalfWidth; + const cutLines = std.max(linesSd, sdf.sdDisk(p.xz, PIE.radius * 0.8)); + + return Shape({ + dist: sdf.opExtrudeY(p, cutLines, 0) - PIE.stringThickness, + material: cheeseMaterial, + }); +}; + +const sdPizzaCheese = (p: d.v3f, angle: number): Shape => { + 'use gpu'; + const pieBase2d = sdf.sdPie( + p.xz, + d.vec2f(std.sin(angle / 2), std.cos(angle / 2)), + PIE.radius * 0.9, + ) + PIE.cheeseRoundness; + + const pieBaseSd = sdf.opExtrudeY( + p - d.vec3f(0, PIE.baseHalfHeight + 0.01, 0), + pieBase2d, + PIE.cheeseHalfHeight, + ) - PIE.cheeseRoundness; + + const pieBase = Shape({ + dist: pieBaseSd + perlin3d.sample(p * 5) * 0.01, + material: cheeseMaterial, + }); + + return pieBase; +}; + +const pepperoniDomain = 0.28; +const sdPepperoni = (p: d.v3f, cutoutDist: number): Shape => { + 'use gpu'; + const wp = std.floor(p.xz / pepperoniDomain); + const mp = (std.fract(p.xz / pepperoniDomain) - 0.5) * pepperoniDomain; + const off = + d.vec2f(perlin2d.sample(wp * 0.5), perlin2d.sample(wp * 0.5 + 2)) * + pepperoniDomain * 0.6; + const dist2d = std.max(cutoutDist, sdf.sdDisk(mp + off, 0.07)); + + return Shape({ + dist: sdf.opExtrudeY(p, dist2d, 0.006), + material: pepperoniMaterialBase, + }); +}; + +const sdPizzaCrust = (p: d.v3f, angle: number): Shape => { + 'use gpu'; + const pieBase2d = sdf.sdPie( + p.xz - d.vec2f(0, 0.005), + d.vec2f(std.sin(angle / 2), std.cos(angle / 2)), + PIE.radius, + ) + PIE.baseRoundness; + const toppingsCutout = sdf.sdPie( + p.xz - d.vec2f(0, 0.005), + d.vec2f(std.sin(angle / 2), std.cos(angle / 2)), + PIE.radius * 0.8, + ); + const crust2d = sdRing( + p.xz, + d.vec2f(std.cos(angle / 2 - 0.05), std.sin(angle / 2 - 0.05)), + PIE.radius, + 0.05, + ); + const crustMaterial = Material(crustMaterialBase); + const crustOffset = perlin3d.sample(p * 10) * 0.02; + crustMaterial.albedo *= 1 - crustOffset * 20; + + const pieBase = Shape({ + dist: sdf.opExtrudeY(p, pieBase2d, PIE.baseHalfHeight) - PIE.baseRoundness, + material: crustMaterial, + }); + + const crust = Shape({ + dist: sdf.opExtrudeY( + p - d.vec3f(0, crustOffset, 0), + crust2d, + PIE.baseHalfHeight * 5, + ) - 0.01, + material: crustMaterial, + }); + + return shapeUnion( + smoothShapeUnion(pieBase, crust, 0.1), + sdPepperoni(p - d.vec3f(0, 0.03, 0), toppingsCutout), + ); +}; + +/** + * Returns a transformation matrix that represents an `angle` rotation + * in the XZ plane (around the Y axis) + */ +const rotateXZ = tgpu.fn([d.f32], d.mat3x3f)((angle) => + d.mat3x3f( + /* right */ d.vec3f(std.cos(angle), 0, std.sin(angle)), + /* up */ d.vec3f(0, 1, 0), + /* forward */ d.vec3f(-std.sin(angle), 0, std.cos(angle)), + ) +); + +const getMorphingShape = (p: d.v3f, t: number): Shape => { + 'use gpu'; + // Center position + const center = d.vec3f(0, PIE.baseHalfHeight + 0.1, 0); + const localP = std.sub(p, center); + + const a1 = 3 * Math.PI / 2; + const p1 = localP; + const p2 = localP * d.vec3f(-1, 1, -1) - + d.vec3f(0, 0, std.abs(std.sin(t * 2)) * 0.1); + const a2 = Math.PI / 2; + + const pull = d.vec3f(0, 0, std.abs(std.sin(t * 2)) * 0.1); + + const pizzaCrust = shapeUnion(sdPizzaCrust(p1, a1), sdPizzaCrust(p2, a2)); + let pizzaCheese = smoothShapeUnion( + sdPizzaCheese(p1, a1), + sdPizzaCheese(p2, a2), + 0.02, + ); + let stringP = d.vec3f(localP); + stringP += std.max(0, -std.dot(stringP, pull)) ** 0.75 * pull * 5; + pizzaCheese = smoothShapeUnion( + pizzaCheese, + sdPizzaCheeseStrings(stringP), + 0.03, + ); + return shapeUnion(pizzaCrust, pizzaCheese); +}; + +const getSceneDist = (p: d.v3f): Shape => { + 'use gpu'; + const shape = getMorphingShape(p, time.$); + const floor = Shape({ + dist: sdf.sdPlane(p, d.vec3f(0, 1, 0), 0), + material: { + albedo: std.mix( + d.vec3f(1), + d.vec3f(0.8), + checkerBoard(std.mul(p.xz, 2)), + ), + ao: 1, + metallic: 0, + roughness: 0, + }, + }); + + return shapeUnion(shape, floor); +}; + +const rayMarch = (ro: d.v3f, rd: d.v3f): Shape => { + 'use gpu'; + let dO = d.f32(0); + const result = Shape({ + dist: d.f32(MAX_DIST), + material: Material(), + }); + + for (let i = 0; i < MAX_STEPS; i++) { + const p = ro.add(rd.mul(dO)); + const scene = getSceneDist(p); + dO += scene.dist; + + if (dO > MAX_DIST || scene.dist < SURF_DIST) { + result.dist = dO; + result.material = Material(scene.material); + break; + } + } + + return result; +}; + +const softShadow = ( + ro: d.v3f, + rd: d.v3f, + minT: number, + maxT: number, + k: number, +): number => { + 'use gpu'; + let res = d.f32(1); + let t = minT; + + for (let i = 0; i < 100; i++) { + if (t >= maxT) break; + const h = getSceneDist(ro.add(rd.mul(t))).dist; + if (h < 0.001) return 0; + res = std.min(res, k * h / t); + t += std.max(h, 0.001); + } + + return res; +}; + +const getNormal = (p: d.v3f): d.v3f => { + 'use gpu'; + const dist = getSceneDist(p).dist; + const e = 0.01; + + const n = d.vec3f( + getSceneDist(p.add(d.vec3f(e, 0, 0))).dist - dist, + getSceneDist(p.add(d.vec3f(0, e, 0))).dist - dist, + getSceneDist(p.add(d.vec3f(0, 0, e))).dist - dist, + ); + + return std.normalize(n); +}; + +const getRayForUV = (uv: d.v2f) => { + 'use gpu'; + const camera = cameraUniform.$; + const ndc = uv.mul(2).sub(1).mul(d.vec2f(1, -1)); + const farView = camera.projectionInverse.mul(d.vec4f(ndc.xy, 1, 1)); + const farWorld = camera.viewInverse.mul( + d.vec4f(farView.xyz.div(farView.w), 1), + ); + const direction = std.normalize(farWorld.xyz - camera.position.xyz); + return Ray({ origin: camera.position.xyz, direction }); +}; + +const fragmentMain = tgpu.fragmentFn({ + in: { uv: d.vec2f }, + out: d.vec4f, +})(({ uv }) => { + 'use gpu'; + const ray = getRayForUV(uv); + + // Ray origin and direction + const march = rayMarch(ray.origin, ray.direction); + + const fog = std.min(march.dist / MAX_DIST, 1) ** 0.7; + + const p = ray.origin + ray.direction * march.dist; + const n = getNormal(p); + + // Lighting with orbiting light + + const l = std.normalize(SUN.$.position); + + // Soft shadows + const shadowRo = p; + const shadowRd = l; + const shadowDist = 4; // approximate + const shadow = softShadow(shadowRo, shadowRd, 0.02, shadowDist, d.f32(16)); + // const shadow = 1; + + const v = std.normalize(ray.direction.mul(-1)); // TODO: use unary negation when it becomes available + const shadedColor = shade(p, n, v, shadow, march.material); + + const finalColor = std.mix(d.vec4f(shadedColor, 1), skyColor, fog); + return finalColor; +}); + +const cameraResult = setupOrbitCamera( + canvas, + { initPos: d.vec4f(0.4, 3, 0, 1), maxZoom: 4, minZoom: 1 }, + (newProps) => cameraUniform.writePartial(newProps), +); + +const perlinCache2d = perlin2d.staticCache({ root, size: d.vec2u(16, 16) }); +const perlinCache3d = perlin3d.staticCache({ root, size: d.vec3u(16, 16, 16) }); +const renderPipeline = root + .pipe(perlinCache2d.inject()) + .pipe(perlinCache3d.inject()) + .createRenderPipeline({ + vertex: common.fullScreenTriangle, + fragment: fragmentMain, + }); + +let animationFrame: number; +function run(timestamp: number) { + time.write(timestamp / 1000 % 1000); + resolution.write(d.vec2f(canvas.width, canvas.height)); + + renderPipeline + .withColorAttachment({ view: context }) + .draw(3); + + animationFrame = requestAnimationFrame(run); +} + +animationFrame = requestAnimationFrame(run); + +export function onCleanup() { + cancelAnimationFrame(animationFrame); + cameraResult.cleanupCamera(); + root.destroy(); +} diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/meta.json b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/meta.json new file mode 100644 index 0000000000..cfd65212d1 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Pizza Pie Chart", + "category": "rendering", + "tags": [ + "experimental", + "fragment shader", + "shadows", + "sdf", + "ray marching", + "sphere tracing" + ] +} diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/pbr.ts b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/pbr.ts new file mode 100644 index 0000000000..49e4083024 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/pbr.ts @@ -0,0 +1,117 @@ +import { perlin3d } from '@typegpu/noise'; +import tgpu, { d, std } from 'typegpu'; +import { PI } from './constants.ts'; + +import { Light, Material } from './types.ts'; + +export const SUN = tgpu.const(Light, { + color: d.vec3f(4), + position: d.vec3f(-3, 10, 0), +}); + +export const distributionGGX = (ndoth: number, roughness: number): number => { + 'use gpu'; + const a = roughness ** 2; + const a2 = a ** 2; + const denom = std.max(ndoth ** 2 * (a2 - 1) + 1, 1e-4); + return a2 / (PI * denom ** 2); +}; + +export const geometrySchlickGGX = (ndot: number, roughness: number): number => { + 'use gpu'; + const k = (roughness + 1) ** 2 / 8; + return ndot / (ndot * (1 - k) + k); +}; + +export const geometrySmith = ( + ndotv: number, + ndotl: number, + roughness: number, +): number => { + 'use gpu'; + return ( + geometrySchlickGGX(ndotv, roughness) * geometrySchlickGGX(ndotl, roughness) + ); +}; + +export const fresnelSchlick = (cosTheta: number, f0: d.v3f): d.v3f => { + 'use gpu'; + return f0 + (1 - f0) * ((1 - cosTheta) ** 5); +}; + +export const evaluateDirectionalLight = ( + p: d.v3f, + n: d.v3f, + v: d.v3f, + light: d.Infer, + material: d.Infer, + f0: d.v3f, +): d.v3f => { + 'use gpu'; + const l = std.normalize(light.position - p); + const h = std.normalize(v + l); + const radiance = light.color; + + const ndotl = std.max(std.dot(n, l), 0); + const ndoth = std.max(std.dot(n, h), 0); + const ndotv = std.max(std.dot(n, v), 0.001); + + const ndf = distributionGGX(ndoth, material.roughness); + const g = geometrySmith(ndotv, ndotl, material.roughness); + const fresnel = fresnelSchlick(ndoth, f0); + + const specular = fresnel * (ndf * g) / (4 * ndotv * ndotl + 0.001); + const kd = (1 - fresnel) * (1 - material.metallic); + + return (kd * material.albedo / PI + specular) * radiance * ndotl; +}; + +export const shade = ( + p: d.v3f, + n: d.v3f, + v: d.v3f, + shadow: number, + material: d.Infer, +): d.v3f => { + 'use gpu'; + const f0 = std.mix(d.vec3f(0.04), material.albedo, material.metallic); + + let lo = d.vec3f(0); + // TODO: Do not clone once passing constant references to functions is okay + lo += evaluateDirectionalLight(p, n, v, Light(SUN.$), material, f0); + + const reflectDir = std.reflect(v, n); + + const pScaled = p * 50; + const roughOffset = d.vec3f( + perlin3d.sample(pScaled), + perlin3d.sample(pScaled + 100), + perlin3d.sample(pScaled + 200), + ) * material.roughness * 0.3; + const blurredReflectDir = std.normalize(reflectDir + roughOffset); + + // TODO: Add proper env color + // const envColor = d.vec4f(0, 0, 0, 1); + const envColor = d.vec4f(1, 1, 1, 1); + + const ndotv = std.max(std.dot(n, v), 0); + + const fresnel = fresnelSchlick(ndotv, f0); + + const reflectionTint = std.mix( + d.vec3f(1), + material.albedo, + material.metallic, + ); + + const reflectionStrength = 1 - material.roughness * 0.85; + + const envContribution = envColor.rgb + .mul(fresnel) + .mul(reflectionTint) + .mul(reflectionStrength); + + const ambient = material.albedo * material.ao * 0.05; + const color = ambient + (lo + envContribution) * shadow; + return std.pow(color / (color + 1), d.vec3f(1 / 2.2)); +}; diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/thumbnail.png b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/thumbnail.png new file mode 100644 index 0000000000..c3b59360ab Binary files /dev/null and b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/thumbnail.png differ diff --git a/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/types.ts b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/types.ts new file mode 100644 index 0000000000..e7161340b8 --- /dev/null +++ b/apps/typegpu-docs/src/examples/rendering/pizza-pie-chart/types.ts @@ -0,0 +1,23 @@ +import { d } from 'typegpu'; + +export const Ray = d.struct({ + origin: d.vec3f, + direction: d.vec3f, +}); + +export const Light = d.struct({ + position: d.vec3f, + color: d.vec3f, +}); + +export const Material = d.struct({ + albedo: d.vec3f, + metallic: d.f32, + roughness: d.f32, + ao: d.f32, +}); + +export const BloomParams = d.struct({ + threshold: d.f32, + intensity: d.f32, +}); diff --git a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts index c2cc43bf40..516d9536e2 100644 --- a/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts +++ b/apps/typegpu-docs/src/examples/rendering/xor-dev-centrifuge-2/index.ts @@ -12,7 +12,7 @@ import tgpu, { d } from 'typegpu'; // deno-fmt-ignore: just a list of standard functions -import { abs, atan2, cos, gt, length, normalize, select, sign, sub, tanh } from 'typegpu/std'; +import { abs, atan2, cos, gt, length, normalize, select, sign, tanh } from 'typegpu/std'; import { defineControls } from '../../common/defineControls.ts'; // NOTE: Some APIs are still unstable (are being finalized based on feedback), but @@ -32,16 +32,20 @@ const safeTanh = (v: d.v3f) => { // shaders, etc. const root = await tgpu.init(); -// Uniforms are used to send read-only data to the GPU -const time = root.createUniform(d.f32); -const aspectRatio = root.createUniform(d.f32); +const Params = d.struct({ + time: d.f32, + aspectRatio: d.f32, -const cameraPos = root.createUniform(d.vec2f); -const tunnelDepth = root.createUniform(d.i32); -const bigStrips = root.createUniform(d.f32); -const smallStrips = root.createUniform(d.f32); -const dollyZoom = root.createUniform(d.f32); -const color = root.createUniform(d.vec3f); + cameraPos: d.vec2f, + tunnelDepth: d.i32, + bigStrips: d.f32, + smallStrips: d.f32, + dollyZoom: d.f32, + color: d.vec3f, +}); + +// Uniforms are used to send read-only data to the GPU +const paramsUniform = root.createUniform(Params); const tunnelRadius = 11; const moveSpeed = 5; @@ -50,31 +54,33 @@ const fragmentMain = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { - const ratio = d.vec2f(aspectRatio.$, 1); - const dir = normalize(d.vec3f(uv.mul(ratio), -1)); + 'use gpu'; + const params = paramsUniform.$; + const ratio = d.vec2f(params.aspectRatio, 1); + const dir = normalize(d.vec3f(uv * ratio, -1)); let z = d.f32(0); let acc = d.vec3f(); - for (let i = 0; i < tunnelDepth.$; i++) { - const p = dir.mul(z); - p.x += cameraPos.$.x; - p.y += cameraPos.$.y; + for (let i = 0; i < params.tunnelDepth; i++) { + const p = dir * z; + p.x += params.cameraPos.x; + p.y += params.cameraPos.y; const coords = d.vec3f( - atan2(p.y, p.x) * bigStrips.$ + time.$, - p.z * dollyZoom.$ - moveSpeed * time.$, + atan2(p.y, p.x) * params.bigStrips + params.time, + p.z * params.dollyZoom - moveSpeed * params.time, length(p.xy) - tunnelRadius, ); - const coords2 = cos(coords.add(cos(coords.mul(smallStrips.$)))).sub(1); + const coords2 = cos(coords + cos(coords * params.smallStrips)) - 1; const dd = length(d.vec4f(coords.z, coords2)) * 0.5 - 0.1; - acc = acc.add(sub(1.2, cos(color.$.mul(p.z))).div(dd)); + acc += (1.2 - cos(params.color * p.z)) / dd; z += dd; } // Tone mapping - acc = safeTanh(acc.mul(0.005)); + acc = safeTanh(acc * 0.005); return d.vec4f(acc, 1); }); @@ -107,8 +113,10 @@ let isRunning = true; function draw(timestamp: number) { if (!isRunning) return; - aspectRatio.write(canvas.clientWidth / canvas.clientHeight); - time.write((timestamp * 0.001) % 1000); + paramsUniform.writePartial({ + aspectRatio: canvas.clientWidth / canvas.clientHeight, + time: (timestamp * 0.001) % 1000, + }); pipeline .withColorAttachment({ view: context }) @@ -128,7 +136,7 @@ export const controls = defineControls({ max: 200, step: 1, onSliderChange(v: number) { - tunnelDepth.write(v); + paramsUniform.writePartial({ tunnelDepth: v }); }, }, 'big strips': { @@ -137,7 +145,7 @@ export const controls = defineControls({ max: 60, step: 0.01, onSliderChange(v: number) { - bigStrips.write(v); + paramsUniform.writePartial({ bigStrips: v }); }, }, 'small strips': { @@ -146,7 +154,7 @@ export const controls = defineControls({ max: 10, step: 0.01, onSliderChange(v: number) { - smallStrips.write(v); + paramsUniform.writePartial({ smallStrips: v }); }, }, 'dolly zoom': { @@ -155,7 +163,7 @@ export const controls = defineControls({ max: 1, step: 0.01, onSliderChange(v: number) { - dollyZoom.write(v); + paramsUniform.writePartial({ dollyZoom: v }); }, }, 'camera pos': { @@ -164,13 +172,13 @@ export const controls = defineControls({ initial: d.vec2f(0, -7), step: d.vec2f(0.01, 0.01), onVectorSliderChange(v) { - cameraPos.write(v); + paramsUniform.writePartial({ cameraPos: v }); }, }, color: { initial: d.vec3f(0.2, 0, 0.3), onColorChange(value) { - color.write(value); + paramsUniform.writePartial({ color: value }); }, }, }); diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/pbr.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/pbr.ts index 31867f24b4..75071882e8 100644 --- a/apps/typegpu-docs/src/examples/simple/ripple-cube/pbr.ts +++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/pbr.ts @@ -10,9 +10,7 @@ export const envMapLayout = tgpu.bindGroupLayout({ }); export const materialAccess = tgpu.accessor(Material); -export const lightsAccess = tgpu.accessor( - d.arrayOf(Light, LIGHT_COUNT), -); +export const lightsAccess = tgpu.accessor(d.arrayOf(Light, LIGHT_COUNT)); export const distributionGGX = (ndoth: number, roughness: number): number => { 'use gpu'; @@ -41,12 +39,7 @@ export const geometrySmith = ( export const fresnelSchlick = (cosTheta: number, f0: d.v3f): d.v3f => { 'use gpu'; - return f0.add( - d - .vec3f(1) - .sub(f0) - .mul((1 - cosTheta) ** 5), - ); + return f0 + (1 - f0) * ((1 - cosTheta) ** 5); }; export const evaluateLight = ( @@ -58,11 +51,11 @@ export const evaluateLight = ( f0: d.v3f, ): d.v3f => { 'use gpu'; - const toLight = light.position.sub(p); + const toLight = light.position - p; const dist = std.length(toLight); const l = std.normalize(toLight); - const h = std.normalize(v.add(l)); - const radiance = light.color.div(dist ** 2); + const h = std.normalize(v + l); + const radiance = light.color / (dist ** 2); const ndotl = std.max(std.dot(n, l), 0); const ndoth = std.max(std.dot(n, h), 0); @@ -72,18 +65,10 @@ export const evaluateLight = ( const g = geometrySmith(ndotv, ndotl, material.roughness); const fresnel = fresnelSchlick(ndoth, f0); - const specular = fresnel.mul((ndf * g) / (4 * ndotv * ndotl + 0.001)); - const kd = d - .vec3f(1) - .sub(fresnel) - .mul(1 - material.metallic); - - return kd - .mul(material.albedo) - .div(PI) - .add(specular) - .mul(radiance) - .mul(ndotl); + const specular = fresnel * (ndf * g) / (4 * ndotv * ndotl + 0.001); + const kd = (1 - fresnel) * (1 - material.metallic); + + return (kd * material.albedo / PI + specular) * radiance * ndotl; }; export const shade = (p: d.v3f, n: d.v3f, v: d.v3f): d.v3f => { @@ -93,20 +78,18 @@ export const shade = (p: d.v3f, n: d.v3f, v: d.v3f): d.v3f => { let lo = d.vec3f(0); for (let i = 0; i < LIGHT_COUNT; i++) { - lo = lo.add(evaluateLight(p, n, v, lightsAccess.$[i], material, f0)); + lo += evaluateLight(p, n, v, lightsAccess.$[i], material, f0); } const reflectDir = std.reflect(v, n); - const pScaled = p.mul(50); - const roughOffset = d - .vec3f( - perlin3d.sample(pScaled), - perlin3d.sample(pScaled.add(100)), - perlin3d.sample(pScaled.add(200)), - ) - .mul(material.roughness * 0.3); - const blurredReflectDir = std.normalize(reflectDir.add(roughOffset)); + const pScaled = p * 50; + const roughOffset = d.vec3f( + perlin3d.sample(pScaled), + perlin3d.sample(pScaled + 100), + perlin3d.sample(pScaled + 200), + ) * material.roughness * 0.3; + const blurredReflectDir = std.normalize(reflectDir + roughOffset); const envColor = std.textureSampleLevel( envMapLayout.$.envMap, diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts index 6b5432372f..103c6bbf7e 100644 --- a/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts +++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/post-processing.ts @@ -83,7 +83,7 @@ export function createPostProcessingPipelines( for (let ox = -1; ox <= 1; ox++) { for (let oy = -1; oy <= 1; oy++) { - const sampleCoord = coord.add(d.vec2i(ox, oy)); + const sampleCoord = coord + d.vec2i(ox, oy); const clampedCoord = std.clamp( sampleCoord, d.vec2i(0, 0), @@ -109,45 +109,41 @@ export function createPostProcessingPipelines( ); }); - const copyToHistory = root.createGuardedComputePipeline( - (x, y) => { + const copyToHistory = root.createGuardedComputePipeline((x, y) => { + 'use gpu'; + const color = std.textureLoad( + processLayout.$.inputTexture, + d.vec2i(d.i32(x), d.i32(y)), + 0, + ); + std.textureStore(processLayout.$.outputTexture, d.vec2u(x, y), color); + }); + + const extractBright = root + .with(bloomParamsAccess, bloomUniform) + .createGuardedComputePipeline((x, y) => { 'use gpu'; - const color = std.textureLoad( + const dimensions = std.textureDimensions( + processLayout.$.outputTexture, + ); + const uv = (d.vec2f(x, y) + 0.5) / (d.vec2f(dimensions)); + const color = std.textureSampleLevel( processLayout.$.inputTexture, - d.vec2i(d.i32(x), d.i32(y)), + processLayout.$.sampler, + uv, 0, ); - std.textureStore(processLayout.$.outputTexture, d.vec2u(x, y), color); - }, - ); - - const extractBright = root - .with(bloomParamsAccess, bloomUniform) - .createGuardedComputePipeline( - (x, y) => { - 'use gpu'; - const dimensions = std.textureDimensions( - processLayout.$.outputTexture, - ); - const uv = d.vec2f(x, y).add(0.5).div(d.vec2f(dimensions)); - const color = std.textureSampleLevel( - processLayout.$.inputTexture, - processLayout.$.sampler, - uv, - 0, - ); - const brightness = std.dot(color.rgb, d.vec3f(0.2126, 0.7152, 0.0722)); - const threshold = bloomParamsAccess.$.threshold; - const bright = std.max(brightness - threshold, 0) / - std.max(brightness, 1e-4); - const bloomColor = color.rgb.mul(bright); - std.textureStore( - processLayout.$.outputTexture, - d.vec2u(x, y), - d.vec4f(bloomColor, 1), - ); - }, - ); + const brightness = std.dot(color.rgb, d.vec3f(0.2126, 0.7152, 0.0722)); + const threshold = bloomParamsAccess.$.threshold; + const bright = std.max(brightness - threshold, 0) / + std.max(brightness, 1e-4); + const bloomColor = color.rgb * bright; + std.textureStore( + processLayout.$.outputTexture, + d.vec2u(x, y), + d.vec4f(bloomColor, 1), + ); + }); const blurHorizontal = createBlurPass(root, 'horizontal'); @@ -157,6 +153,7 @@ export function createPostProcessingPipelines( in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { + 'use gpu'; const color = std.textureSample( compositeLayout.$.colorTexture, compositeLayout.$.sampler, @@ -168,13 +165,11 @@ export function createPostProcessingPipelines( uv, ); - let final = color.rgb.add( - bloomColor.rgb.mul(bloomParamsAccess.$.intensity), - ); + let final = color.rgb + bloomColor.rgb * bloomParamsAccess.$.intensity; - const centeredUV = uv.sub(0.5).mul(2); + const centeredUV = (uv - 0.5) * 2; const vignette = 1 - std.dot(centeredUV, centeredUV) * 0.15; - final = final.mul(vignette); + final *= vignette; return d.vec4f(final, 1); }); @@ -256,8 +251,8 @@ function createBlurPass( return root.createGuardedComputePipeline((x, y) => { 'use gpu'; const dimensions = std.textureDimensions(processLayout.$.inputTexture); - const texelSize = d.vec2f(1).div(d.vec2f(dimensions)); - const uv = d.vec2f(x, y).add(0.5).div(d.vec2f(dimensions)); + const texelSize = 1 / d.vec2f(dimensions); + const uv = (d.vec2f(x, y) + 0.5) / d.vec2f(dimensions); const offsetDir = direction === 'horizontal' ? d.vec2f(1, 0) @@ -267,25 +262,21 @@ function createBlurPass( let totalWeight = d.f32(0); for (let i = -BLUR_RADIUS; i <= BLUR_RADIUS; i++) { - const offset = offsetDir.mul(d.f32(i)).mul(texelSize); + const offset = offsetDir * i * texelSize; const weight = std.exp(-d.f32(i * i) / (2 * BLUR_RADIUS)); - result = result.add( - std - .textureSampleLevel( - processLayout.$.inputTexture, - processLayout.$.sampler, - uv.add(offset), - 0, - ) - .rgb.mul(weight), - ); + result += std.textureSampleLevel( + processLayout.$.inputTexture, + processLayout.$.sampler, + uv + offset, + 0, + ).rgb * weight; totalWeight += weight; } std.textureStore( processLayout.$.outputTexture, d.vec2u(x, y), - d.vec4f(result.div(totalWeight), 1), + d.vec4f(result / totalWeight, 1), ); }); } diff --git a/apps/typegpu-docs/src/examples/simple/ripple-cube/sdf-scene.ts b/apps/typegpu-docs/src/examples/simple/ripple-cube/sdf-scene.ts index f102321346..a272cc86a7 100644 --- a/apps/typegpu-docs/src/examples/simple/ripple-cube/sdf-scene.ts +++ b/apps/typegpu-docs/src/examples/simple/ripple-cube/sdf-scene.ts @@ -11,7 +11,7 @@ export const sdfLayout = tgpu.bindGroupLayout({ export const sceneSDF = (p: d.v3f): number => { 'use gpu'; - const uv = std.abs(p).mul(2); + const uv = std.abs(p) * 2; const sdfValue = std.textureSampleLevel( sdfLayout.$.sdfTexture, sdfLayout.$.sdfSampler, @@ -29,9 +29,9 @@ export const getNormal = (p: d.v3f): d.v3f => { const dist = sceneSDF(p); return std.normalize( d.vec3f( - sceneSDF(p.add(d.vec3f(e, 0, 0))) - dist, - sceneSDF(p.add(d.vec3f(0, e, 0))) - dist, - sceneSDF(p.add(d.vec3f(0, 0, e))) - dist, + sceneSDF(p + d.vec3f(e, 0, 0)) - dist, + sceneSDF(p + d.vec3f(0, e, 0)) - dist, + sceneSDF(p + d.vec3f(0, 0, e)) - dist, ), ); }; diff --git a/apps/typegpu-docs/src/examples/simple/stencil/index.ts b/apps/typegpu-docs/src/examples/simple/stencil/index.ts index 69b30b522b..60d1df1083 100644 --- a/apps/typegpu-docs/src/examples/simple/stencil/index.ts +++ b/apps/typegpu-docs/src/examples/simple/stencil/index.ts @@ -34,10 +34,11 @@ const vertexFn = tgpu.vertexFn({ uv: d.vec2f, }, })(({ vid }) => { + 'use gpu'; const pos = triangleData.vertices.$[vid]; const uv = triangleData.uvs.$[vid]; - const rotatedPos = rotationUniform.$.mul(pos); + const rotatedPos = rotationUniform.$ * pos; return { position: d.vec4f(rotatedPos, 0, 1), diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/floor.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/floor.ts index 88b11845e2..3c013b8ee7 100644 --- a/apps/typegpu-docs/src/examples/simple/vaporrave/floor.ts +++ b/apps/typegpu-docs/src/examples/simple/vaporrave/floor.ts @@ -3,6 +3,7 @@ import tgpu, { d, std } from 'typegpu'; import * as c from './constants.ts'; export const grid = tgpu.fn([d.vec2f, d.f32], d.vec3f)((uv, time) => { + 'use gpu'; // time is really an angle, but we are fine as long as it keeps increasing const uvNormalized = std.fract( d.vec2f(uv.x, uv.y + time).div(c.GRID_SEP), @@ -10,10 +11,10 @@ export const grid = tgpu.fn([d.vec2f, d.f32], d.vec3f)((uv, time) => { // x^4 + y^4 = 0.5^4 const diff4 = std.pow( - d.vec2f(0.5, 0.5).sub(uvNormalized), + d.vec2f(0.5, 0.5) - uvNormalized, d.vec2f(4, 4), ); - const sdf = std.pow(diff4.x + diff4.y, 0.25) - 0.5; // -radius + const sdf = ((diff4.x + diff4.y) ** 0.25) - 0.5; // -radius return std.mix( c.gridInnerColor, @@ -34,15 +35,16 @@ const rotateXY = tgpu.fn([d.f32], d.mat2x2f)((angle) => ); export const circles = tgpu.fn([d.vec2f, d.f32], d.vec3f)((uv, angle) => { - const uvRotated = rotateXY(angle).mul(d.vec2f(uv.x, uv.y - c.sphereCenter.z)); + 'use gpu'; + const uvRotated = rotateXY(angle) * d.vec2f(uv.x, uv.y - c.sphereCenter.z); const uvNormalized = std.fract( - d.vec2f(uvRotated.x, uvRotated.y).div(c.GRID_SEP), + d.vec2f(uvRotated.x, uvRotated.y) / c.GRID_SEP, ); // working with circle centered at (0.5, 0.5) - const diff2 = std.pow(d.vec2f(0.5, 0.5).sub(uvNormalized), d.vec2f(2)); - const distO = std.pow(diff2.x + diff2.y, 0.5); + const diff2 = std.pow(d.vec2f(0.5, 0.5) - uvNormalized, d.vec2f(2)); + const distO = (diff2.x + diff2.y) ** 0.5; return std.mix( c.gridInnerColor, diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts index 08968bf197..cde920b7a2 100644 --- a/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts +++ b/apps/typegpu-docs/src/examples/simple/vaporrave/index.ts @@ -47,10 +47,8 @@ const getSceneRay = tgpu.fn([d.vec3f], Ray)((p) => { return rayUnion(floor, sphere); }); -const rayMarch = tgpu.fn( - [d.vec3f, d.vec3f], - LightRay, -)((ro, rd) => { +const rayMarch = (ro: d.v3f, rd: d.v3f) => { + 'use gpu'; let distOrigin = d.f32(); const result = Ray({ dist: d.f32(c.MAX_DIST), @@ -60,7 +58,7 @@ const rayMarch = tgpu.fn( let glow = d.vec3f(); for (let i = 0; i < c.MAX_STEPS; i++) { - const p = rd.mul(distOrigin).add(ro); + const p = rd * distOrigin + ro; const scene = getSceneRay(p); const sphereDist = getSphere( p, @@ -69,9 +67,7 @@ const rayMarch = tgpu.fn( sphereAngleUniform.$, ); - glow = d.vec3f(sphereColorUniform.$) - .mul(std.exp(-sphereDist.dist)) - .add(glow); + glow += d.vec3f(sphereColorUniform.$) * std.exp(-sphereDist.dist); distOrigin += scene.dist; @@ -87,8 +83,8 @@ const rayMarch = tgpu.fn( } } - return { ray: result, glow }; -}); + return LightRay({ ray: result, glow }); +}; const vertexMain = tgpu.vertexFn({ in: { idx: d.builtin.vertexIndex }, @@ -107,7 +103,8 @@ const fragmentMain = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })((input) => { - const uv = input.uv.mul(2).sub(1); + 'use gpu'; + const uv = input.uv * 2 - 1; uv.x *= resolutionUniform.$.x / resolutionUniform.$.y; // ray origin and direction @@ -118,7 +115,7 @@ const fragmentMain = tgpu.fragmentFn({ const march = rayMarch(ro, rd); // sky gradient - const y = rd.mul(march.ray.dist).add(ro).y - 2; // camera at level 2 + const y = rd.y * march.ray.dist + ro.y - 2; // camera at level 2 const sky = std.mix(c.skyColor1, c.skyColor2, y / c.MAX_DIST); // fog coefficient diff --git a/apps/typegpu-docs/src/examples/simple/vaporrave/sphere.ts b/apps/typegpu-docs/src/examples/simple/vaporrave/sphere.ts index 2689c336f7..c827405ba3 100644 --- a/apps/typegpu-docs/src/examples/simple/vaporrave/sphere.ts +++ b/apps/typegpu-docs/src/examples/simple/vaporrave/sphere.ts @@ -33,10 +33,11 @@ export const getSphere = tgpu.fn( [d.vec3f, d.vec3f, d.vec3f, d.f32], Ray, )((p, sphereColor, sphereCenter, angle) => { - const localP = p.sub(sphereCenter); // (0,0) is the center to rotate easily + 'use gpu'; + const localP = p - sphereCenter; // (0,0) is the center to rotate easily const rotMatZ = rotateAroundZ(-angle * 0.3); const rotMatX = rotateAroundX(-angle * 0.7); - const rotatedP = localP.mul(rotMatZ).mul(rotMatX); + const rotatedP = localP * rotMatZ * rotMatX; // breathing effect const radius = d.f32(c.SPHERE_RADIUS) + std.sin(angle); @@ -44,7 +45,7 @@ export const getSphere = tgpu.fn( const rawDist = sdSphere(rotatedP, radius); let noise = d.f32(0); if (rawDist < d.f32(1)) { - noise += perlin3d.sample(rotatedP.add(angle)); + noise += perlin3d.sample(rotatedP + angle); } return { diff --git a/apps/typegpu-docs/src/examples/simulation/boids/index.ts b/apps/typegpu-docs/src/examples/simulation/boids/index.ts index bf30b50c43..5650bfa643 100644 --- a/apps/typegpu-docs/src/examples/simulation/boids/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/boids/index.ts @@ -94,12 +94,13 @@ const mainVert = tgpu.vertexFn({ in: { v: d.vec2f, center: d.vec2f, velocity: d.vec2f }, out: VertexOutput, })((input) => { + 'use gpu'; const angle = getRotationFromVelocity(input.velocity); const rotated = rotate(input.v, angle); - const pos = d.vec4f(rotated.add(input.center), 0, 1); + const pos = d.vec4f(rotated + input.center, 0, 1); const color = d.vec4f( - std.sin(colorPalette.$.add(angle)).mul(0.45).add(0.45), + std.sin(colorPalette.$ + angle) * 0.45 + 0.45, 1, ); @@ -171,7 +172,7 @@ const layout = tgpu.bindGroupLayout({ const simulate = (index: number) => { 'use gpu'; - const instanceInfo = TriangleData(layout.$.currentTrianglePos[index]); + const self = TriangleData(layout.$.currentTrianglePos[index]); let separation = d.vec2f(); let alignment = d.vec2f(); let cohesion = d.vec2f(); @@ -179,62 +180,42 @@ const simulate = (index: number) => { let cohesionCount = 0; for (const other of layout.$.currentTrianglePos) { - const dist = std.distance(instanceInfo.position, other.position); + const dist = std.distance(self.position, other.position); if (dist < params.$.separationDistance) { - separation = std.add( - separation, - std.sub(instanceInfo.position, other.position), - ); + separation += self.position - other.position; } if (dist < params.$.alignmentDistance) { - alignment = std.add(alignment, other.velocity); + alignment += other.velocity; alignmentCount++; } if (dist < params.$.cohesionDistance) { - cohesion = std.add(cohesion, other.position); + cohesion += other.position; cohesionCount++; } } if (alignmentCount > 0) { - alignment = std.mul(1.0 / d.f32(alignmentCount), alignment); + alignment /= d.f32(alignmentCount); } if (cohesionCount > 0) { - cohesion = std.mul(1.0 / d.f32(cohesionCount), cohesion); - cohesion = std.sub(cohesion, instanceInfo.position); + cohesion /= d.f32(cohesionCount); + cohesion -= self.position; } - let velocity = std.mul(params.$.separationStrength, separation); - velocity = std.add( - velocity, - std.mul(params.$.alignmentStrength, alignment), - ); - velocity = std.add( - velocity, - std.mul(params.$.cohesionStrength, cohesion), - ); + const velocity = params.$.separationStrength * separation + + params.$.alignmentStrength * alignment + + params.$.cohesionStrength * cohesion; - instanceInfo.velocity = std.add(instanceInfo.velocity, velocity); - instanceInfo.velocity = std.mul( - std.clamp(std.length(instanceInfo.velocity), 0, 0.01), - std.normalize(instanceInfo.velocity), - ); + self.velocity += velocity; + self.velocity = std.clamp(std.length(self.velocity), 0, 0.01) * + std.normalize(self.velocity); - if (instanceInfo.position.x > 1.0 + triangleSize) { - instanceInfo.position.x = -1.0 - triangleSize; - } - if (instanceInfo.position.y > 1.0 + triangleSize) { - instanceInfo.position.y = -1.0 - triangleSize; - } - if (instanceInfo.position.x < -1.0 - triangleSize) { - instanceInfo.position.x = 1.0 + triangleSize; - } - if (instanceInfo.position.y < -1.0 - triangleSize) { - instanceInfo.position.y = 1.0 + triangleSize; - } + self.position += self.velocity; - instanceInfo.position = std.add(instanceInfo.position, instanceInfo.velocity); + // Wrap the domain + const domain = (1 + triangleSize) * 2; + self.position = (std.fract(self.position / domain + 0.5) - 0.5) * domain; - layout.$.nextTrianglePos[index] = TriangleData(instanceInfo); + layout.$.nextTrianglePos[index] = TriangleData(self); }; const simulatePipeline = root.createGuardedComputePipeline(simulate); diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts b/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts index 705651f389..fc625d44fc 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/compute.ts @@ -25,6 +25,7 @@ export const computeCollisionsShader = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [1], })((input) => { + 'use gpu'; const currentId = input.gid.x; const current = CelestialBody(computeLayout.$.inState[currentId]); @@ -55,21 +56,19 @@ export const computeCollisionsShader = tgpu.computeFn({ // bounce occurs // push the smaller object outside if (isSmaller(currentId, otherId)) { - const dir = std.normalize(current.position.sub(other.position)); - current.position = other.position.add( - dir.mul(radiusOf(current) + radiusOf(other)), - ); + const dir = std.normalize(current.position - other.position); + current.position = other.position + + (dir * (radiusOf(current) + radiusOf(other))); } // bounce with tiny damping - const posDiff = current.position.sub(other.position); - const velDiff = current.velocity.sub(other.velocity); + const posDiff = current.position - other.position; + const velDiff = current.velocity - other.velocity; const posDiffFactor = (((2 * other.mass) / (current.mass + other.mass)) * std.dot(velDiff, posDiff)) / std.dot(posDiff, posDiff); - current.velocity = current.velocity - .sub(posDiff.mul(posDiffFactor)).mul(0.99); + current.velocity = (current.velocity - posDiff * posDiffFactor) * 0.99; } else { // merge occurs const isCurrentAbsorbed = current.collisionBehavior === bounce || @@ -82,10 +81,8 @@ export const computeCollisionsShader = tgpu.computeFn({ // absorbs the other const m1 = current.mass; const m2 = other.mass; - current.velocity = std.add( - current.velocity.mul(m1 / (m1 + m2)), - other.velocity.mul(m2 / (m1 + m2)), - ); + current.velocity = current.velocity * (m1 / (m1 + m2)) + + other.velocity * (m2 / (m1 + m2)); current.mass = m1 + m2; } } @@ -99,6 +96,7 @@ export const computeGravityShader = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [1], })((input) => { + 'use gpu'; const dt = timeAccess.$.passed * timeAccess.$.multiplier; const currentId = input.gid.x; const current = CelestialBody(computeLayout.$.inState[currentId]); @@ -121,13 +119,11 @@ export const computeGravityShader = tgpu.computeFn({ ); const gravityForce = (current.mass * other.mass) / dist / dist; - const direction = std.normalize(other.position.sub(current.position)); - current.velocity = current.velocity.add( - direction.mul((gravityForce / current.mass) * dt), - ); + const direction = std.normalize(other.position - current.position); + current.velocity += direction * (gravityForce / current.mass) * dt; } - current.position = current.position.add(current.velocity.mul(dt)); + current.position += current.velocity * dt; } computeLayout.$.outState[currentId] = CelestialBody(current); diff --git a/apps/typegpu-docs/src/examples/simulation/gravity/render.ts b/apps/typegpu-docs/src/examples/simulation/gravity/render.ts index 716419d2c4..d3c677435e 100644 --- a/apps/typegpu-docs/src/examples/simulation/gravity/render.ts +++ b/apps/typegpu-docs/src/examples/simulation/gravity/render.ts @@ -20,13 +20,11 @@ export const skyBoxVertex = tgpu.vertexFn({ texCoord: d.vec3f, }, })((input) => { - const viewPos = std.mul(cameraAccess.$.view, d.vec4f(input.position, 0)).xyz; + 'use gpu'; + const viewPos = (cameraAccess.$.view * d.vec4f(input.position, 0)).xyz; return { - pos: std.mul( - cameraAccess.$.projection, - d.vec4f(viewPos, 1), - ), + pos: cameraAccess.$.projection * d.vec4f(viewPos, 1), texCoord: input.position.xyz, }; }); @@ -51,16 +49,15 @@ export const mainVertex = tgpu.vertexFn({ }, out: VertexOutput, })((input) => { + 'use gpu'; const currentBody = renderLayout.$.celestialBodies[input.instanceIndex]; - const worldPosition = currentBody.position.add( - input.position.xyz.mul(radiusOf(currentBody)), - ); + const worldPosition = currentBody.position + + input.position.xyz * radiusOf(currentBody); const camera = cameraAccess.$; - const positionOnCanvas = camera.projection - .mul(camera.view) - .mul(d.vec4f(worldPosition, 1)); + const positionOnCanvas = camera.projection * camera.view * + d.vec4f(worldPosition, 1); return { position: positionOnCanvas, @@ -77,6 +74,7 @@ export const mainFragment = tgpu.fragmentFn({ in: VertexOutput, out: d.vec4f, })((input) => { + 'use gpu'; if (input.destroyed === 1) { std.discard(); } @@ -89,16 +87,14 @@ export const mainFragment = tgpu.fragmentFn({ input.sphereTextureIndex, ).rgb; - const ambient = textureColor.mul(lightColor).mul(input.ambientLightFactor); + const ambient = textureColor * lightColor * input.ambientLightFactor; const normal = input.normals; const lightDirection = std.normalize( - lightSourceAccess.$.sub(input.worldPosition), + lightSourceAccess.$ - input.worldPosition, ); const cosTheta = std.dot(normal, lightDirection); - const diffuse = textureColor.mul(lightColor).mul(std.max(0, cosTheta)); - - const litColor = ambient.add(diffuse); + const diffuse = textureColor * lightColor * std.max(0, cosTheta); - return d.vec4f(litColor, 1); + return d.vec4f(ambient + diffuse, 1); }); diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts index 1c08a09028..7eb7dbf63a 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold-3d/index.ts @@ -101,9 +101,9 @@ const mutableAgentsDataBuffers = agentsDataBuffers.map((b) => b.as('mutable')); root.createGuardedComputePipeline((x) => { 'use gpu'; randf.seed(x / NUM_AGENTS); - const pos = randf.inUnitSphere().mul(resolution.x / 4).add(resolution.div(2)); - const center = resolution.div(2); - const dir = std.normalize(center.sub(pos)); + const pos = randf.inUnitSphere() * (resolution.x / 4) + (resolution / 2); + const center = resolution / 2; + const dir = std.normalize(center - pos); mutableAgentsDataBuffers[0].$[x] = Agent({ position: pos, direction: dir }); mutableAgentsDataBuffers[1].$[x] = Agent({ position: pos, direction: dir }); }).dispatchThreads(NUM_AGENTS); @@ -191,19 +191,19 @@ const sense3D = (pos: d.v3f, direction: d.v3f) => { for (let i = 0; i < numSamples; i++) { const theta = (i / numSamples) * 2 * Math.PI; - const coneOffset = perp1.mul(std.cos(theta)).add(perp2.mul(std.sin(theta))); + const coneOffset = perp1 * std.cos(theta) + perp2 * std.sin(theta); const sensorDir = std.normalize( - direction.add(coneOffset.mul(std.sin(params.$.sensorAngle))), + direction + coneOffset * std.sin(params.$.sensorAngle), ); - const sensorPos = pos.add(sensorDir.mul(params.$.sensorDistance)); + const sensorPos = pos + sensorDir * params.$.sensorDistance; const sensorPosInt = d.vec3u( - std.clamp(sensorPos, d.vec3f(), dimsf.sub(d.vec3f(1))), + std.clamp(sensorPos, d.vec3f(), dimsf - 1), ); const weight = std.textureLoad(computeLayout.$.oldState, sensorPosInt).x; - weightedDir = weightedDir.add(sensorDir.mul(weight)); + weightedDir = weightedDir + sensorDir * weight; totalWeight = totalWeight + weight; } @@ -214,6 +214,7 @@ const updateAgents = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [AGENT_WORKGROUP_SIZE], })(({ gid }) => { + 'use gpu'; if (gid.x >= NUM_AGENTS) { return; } @@ -232,15 +233,14 @@ const updateAgents = tgpu.computeFn({ std.normalize(senseResult.weightedDir), senseResult.totalWeight > 0.01, ); - direction = std.normalize(direction.add( - targetDirection.mul(params.$.turnSpeed * params.$.deltaTime), - )); - - const newPos = agent.position.add( - direction.mul(params.$.moveSpeed * params.$.deltaTime), + direction = std.normalize( + direction + (targetDirection * params.$.turnSpeed * params.$.deltaTime), ); - const center = dimsf.div(2); + const newPos = agent.position + + (direction * params.$.moveSpeed * params.$.deltaTime); + + const center = dimsf / 2; if (newPos.x < 0 || newPos.x >= dimsf.x) { newPos.x = std.clamp(newPos.x, 0, dimsf.x - 1); @@ -249,12 +249,10 @@ const updateAgents = tgpu.computeFn({ normal = d.vec3f(-1, 0, 0); } const randomDir = randf.inHemisphere(normal); - const toCenter = std.normalize(center.sub(newPos)); + const toCenter = std.normalize(center - newPos); direction = std.normalize( - randomDir.mul(RANDOM_DIRECTION_WEIGHT).add( - toCenter.mul(CENTER_BIAS_WEIGHT), - ), + randomDir * RANDOM_DIRECTION_WEIGHT + toCenter * CENTER_BIAS_WEIGHT, ); } if (newPos.y < 0 || newPos.y >= dimsf.y) { @@ -264,11 +262,9 @@ const updateAgents = tgpu.computeFn({ normal = d.vec3f(0, -1, 0); } const randomDir = randf.inHemisphere(normal); - const toCenter = std.normalize(center.sub(newPos)); + const toCenter = std.normalize(center - newPos); direction = std.normalize( - randomDir.mul(RANDOM_DIRECTION_WEIGHT).add( - toCenter.mul(CENTER_BIAS_WEIGHT), - ), + randomDir * RANDOM_DIRECTION_WEIGHT + toCenter * CENTER_BIAS_WEIGHT, ); } if (newPos.z < 0 || newPos.z >= dimsf.z) { @@ -278,11 +274,9 @@ const updateAgents = tgpu.computeFn({ normal = d.vec3f(0, 0, -1); } const randomDir = randf.inHemisphere(normal); - const toCenter = std.normalize(center.sub(newPos)); + const toCenter = std.normalize(center - newPos); direction = std.normalize( - randomDir.mul(RANDOM_DIRECTION_WEIGHT).add( - toCenter.mul(CENTER_BIAS_WEIGHT), - ), + randomDir * RANDOM_DIRECTION_WEIGHT + toCenter * CENTER_BIAS_WEIGHT, ); } @@ -318,27 +312,24 @@ const blur = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: BLUR_WORKGROUP_SIZE, })(({ gid }) => { + 'use gpu'; const dims = d.vec3u(std.textureDimensions(blurLayout.$.oldState)); if (gid.x >= dims.x || gid.y >= dims.y || gid.z >= dims.z) return; - const uv = d.vec3f(gid).add(0.5).div(d.vec3f(dims)); + const uv = (d.vec3f(gid) + 0.5) / d.vec3f(dims); let sum = d.f32(); - sum += getSummand(uv, d.vec3f(-1, 0, 0).div(d.vec3f(dims))); - sum += getSummand(uv, d.vec3f(1, 0, 0).div(d.vec3f(dims))); - sum += getSummand(uv, d.vec3f(0, -1, 0).div(d.vec3f(dims))); - sum += getSummand(uv, d.vec3f(0, 1, 0).div(d.vec3f(dims))); - sum += getSummand(uv, d.vec3f(0, 0, -1).div(d.vec3f(dims))); - sum += getSummand(uv, d.vec3f(0, 0, 1).div(d.vec3f(dims))); + sum += getSummand(uv, d.vec3f(-1, 0, 0) / d.vec3f(dims)); + sum += getSummand(uv, d.vec3f(1, 0, 0) / d.vec3f(dims)); + sum += getSummand(uv, d.vec3f(0, -1, 0) / d.vec3f(dims)); + sum += getSummand(uv, d.vec3f(0, 1, 0) / d.vec3f(dims)); + sum += getSummand(uv, d.vec3f(0, 0, -1) / d.vec3f(dims)); + sum += getSummand(uv, d.vec3f(0, 0, 1) / d.vec3f(dims)); - const blurred = sum / 6.0; + const blurred = sum / 6; const newValue = std.saturate(blurred - params.$.evaporationRate); - std.textureStore( - blurLayout.$.newState, - gid.xyz, - d.vec4f(newValue, 0, 0, 1), - ); + std.textureStore(blurLayout.$.newState, gid.xyz, d.vec4f(newValue, 0, 0, 1)); }); // Ray-box intersection @@ -349,9 +340,9 @@ const rayBoxIntersection = ( boxMax: d.v3f, ) => { 'use gpu'; - const invDir = d.vec3f(1).div(rayDir); - const t0 = boxMin.sub(rayOrigin).mul(invDir); - const t1 = boxMax.sub(rayOrigin).mul(invDir); + const invDir = 1 / rayDir; + const t0 = (boxMin - rayOrigin) * invDir; + const t1 = (boxMax - rayOrigin) * invDir; const tmin = std.min(t0, t1); const tmax = std.max(t0, t1); const tNear = std.max(tmin.x, tmin.y, tmin.z); @@ -364,17 +355,18 @@ const fragmentShader = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })(({ uv }) => { + 'use gpu'; randf.seed2(uv); const ndc = d.vec2f(uv.x * 2 - 1, 1 - uv.y * 2); const ndcNear = d.vec4f(ndc, -1, 1); const ndcFar = d.vec4f(ndc, 1, 1); - const worldNear = cameraData.$.invViewProj.mul(ndcNear); - const worldFar = cameraData.$.invViewProj.mul(ndcFar); + const worldNear = cameraData.$.invViewProj * ndcNear; + const worldFar = cameraData.$.invViewProj * ndcFar; - const rayOrigin = worldNear.xyz.div(worldNear.w); - const rayEnd = worldFar.xyz.div(worldFar.w); - const rayDir = std.normalize(rayEnd.sub(rayOrigin)); + const rayOrigin = worldNear.xyz / worldNear.w; + const rayEnd = worldFar.xyz / worldFar.w; + const rayDir = std.normalize(rayEnd - rayOrigin); const boxMin = d.vec3f(); const boxMax = resolution; @@ -416,21 +408,21 @@ const fragmentShader = tgpu.fragmentFn({ let i = d.i32(0); while (i < numSteps && transmittance > TMin) { const t = tStart + (d.f32(i) + 0.5) * stepSize; - const pos = rayOrigin.add(rayDir.mul(t)); - const texCoord = pos.div(resolution); + const pos = rayOrigin + rayDir * t; + const texCoord = pos / resolution; const sampleValue = std .textureSampleLevel(renderLayout.$.state, sampler.$, texCoord, 0) .x; const d0 = std.smoothstep(thresholdLo, thresholdHi, sampleValue); - const density = std.pow(d0, gamma); + const density = d0 ** gamma; const alphaSrc = 1 - std.exp(-sigmaT * density * stepSize); - const contrib = albedo.mul(alphaSrc); + const contrib = albedo * alphaSrc; - accum = accum.add(contrib.mul(transmittance)); + accum += contrib * transmittance; transmittance = transmittance * (1 - alphaSrc); i += 1; diff --git a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts index fda0e7c29e..e2de5cc646 100644 --- a/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts +++ b/apps/typegpu-docs/src/examples/simulation/slime-mold/index.ts @@ -35,9 +35,7 @@ const agentsData = root.createMutable(d.arrayOf(Agent, NUM_AGENTS)); root.createGuardedComputePipeline((x) => { 'use gpu'; randf.seed(x / NUM_AGENTS + 0.1); - const pos = randf.inUnitCircle().mul(resolution.x / 2 - 10).add( - resolution.div(2), - ); + const pos = randf.inUnitCircle() * (resolution.x / 2 - 10) + (resolution / 2); const angle = std.atan2( resolution.y / 2 - pos.y, resolution.x / 2 - pos.x, @@ -73,13 +71,11 @@ const sense = (pos: d.v2f, angle: number, sensorAngleOffset: number) => { 'use gpu'; const sensorAngle = angle + sensorAngleOffset; const sensorDir = d.vec2f(std.cos(sensorAngle), std.sin(sensorAngle)); - const sensorPos = pos.add(sensorDir.mul(params.$.sensorDistance)); + const sensorPos = pos + sensorDir * params.$.sensorDistance; const dims = std.textureDimensions(computeLayout.$.oldState); const dimsf = d.vec2f(dims); - const sensorPosInt = d.vec2u( - std.clamp(sensorPos, d.vec2f(0), dimsf.sub(d.vec2f(1))), - ); + const sensorPosInt = d.vec2u(std.clamp(sensorPos, d.vec2f(0), dimsf - 1)); const color = std.textureLoad(computeLayout.$.oldState, sensorPosInt).rgb; return color.x + color.y + color.z; @@ -89,6 +85,7 @@ const updateAgents = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [64], })(({ gid }) => { + 'use gpu'; if (gid.x >= NUM_AGENTS) return; randf.seed(gid.x / NUM_AGENTS + 0.1); @@ -122,15 +119,13 @@ const updateAgents = tgpu.computeFn({ } const dir = d.vec2f(std.cos(angle), std.sin(angle)); - let newPos = agent.position.add( - dir.mul(params.$.moveSpeed * deltaTime.$), - ); + let newPos = agent.position + dir * params.$.moveSpeed * deltaTime.$; const dimsf = d.vec2f(dims); if ( newPos.x < 0 || newPos.x > dimsf.x || newPos.y < 0 || newPos.y > dimsf.y ) { - newPos = std.clamp(newPos, d.vec2f(0), dimsf.sub(d.vec2f(1))); + newPos = std.clamp(newPos, d.vec2f(0), dimsf - 1); if (newPos.x <= 0 || newPos.x >= dimsf.x - 1) { angle = Math.PI - angle; @@ -149,7 +144,7 @@ const updateAgents = tgpu.computeFn({ const oldState = std.textureLoad(computeLayout.$.oldState, d.vec2u(newPos)).rgb; - const newState = oldState.add(d.vec3f(1)); + const newState = oldState + 1; std.textureStore( computeLayout.$.newState, d.vec2u(newPos), @@ -161,6 +156,7 @@ const blur = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, workgroupSize: [16, 16], })(({ gid }) => { + 'use gpu'; const dims = std.textureDimensions(computeLayout.$.oldState); if (gid.x >= dims.x || gid.y >= dims.y) return; @@ -170,7 +166,7 @@ const blur = tgpu.computeFn({ // 3x3 blur kernel for (let offsetY = -1; offsetY <= 1; offsetY++) { for (let offsetX = -1; offsetX <= 1; offsetX++) { - const samplePos = d.vec2i(gid.xy).add(d.vec2i(offsetX, offsetY)); + const samplePos = d.vec2i(gid.xy) + d.vec2i(offsetX, offsetY); const dimsi = d.vec2i(dims); if ( @@ -179,18 +175,14 @@ const blur = tgpu.computeFn({ ) { const color = std.textureLoad(computeLayout.$.oldState, d.vec2u(samplePos)).rgb; - sum = sum.add(color); - count = count + 1; + sum += color; + count += 1; } } } - const blurred = sum.div(count); - const newColor = std.clamp( - blurred.sub(params.$.evaporationRate), - d.vec3f(0), - d.vec3f(1), - ); + const blurred = sum / count; + const newColor = std.saturate(blurred - params.$.evaporationRate); std.textureStore( computeLayout.$.newState, gid.xy, diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/render.ts b/apps/typegpu-docs/src/examples/simulation/stable-fluid/render.ts index c1de903bbd..fb65412213 100644 --- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/render.ts +++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/render.ts @@ -52,6 +52,7 @@ export const fragmentImageFn = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })((input) => { + 'use gpu'; const pixelStep = d.f32(1) / SIM_N; const leftSample = std.textureSample( @@ -80,10 +81,8 @@ export const fragmentImageFn = tgpu.fragmentFn({ const distortStrength = 0.8; const distortVector = d.vec2f(gradientX, gradientY); - const distortedUV = std.add( - input.uv, - std.mul(distortVector, d.vec2f(distortStrength, -distortStrength)), - ); + const distortedUV = input.uv + + distortVector * d.vec2f(distortStrength, -distortStrength); const outputColor = std.textureSample( renderLayout.$.background, diff --git a/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts b/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts index 21bb70bdbf..5623445b8c 100644 --- a/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts +++ b/apps/typegpu-docs/src/examples/simulation/stable-fluid/simulation.ts @@ -1,24 +1,23 @@ import tgpu, { d, std } from 'typegpu'; import * as p from './params.ts'; -const getNeighbors = tgpu.fn([d.vec2i, d.vec2i], d.arrayOf(d.vec2i, 4))( - (coords, bounds) => { - const adjacentOffsets = [ - d.vec2i(-1, 0), - d.vec2i(0, -1), - d.vec2i(1, 0), - d.vec2i(0, 1), - ]; - for (let i = 0; i < 4; i++) { - adjacentOffsets[i] = std.clamp( - std.add(coords, adjacentOffsets[i]), - d.vec2i(), - std.sub(bounds, d.vec2i(1)), - ); - } - return adjacentOffsets; - }, -); +const getNeighbors = (coords: d.v2i, bounds: d.v2i): d.v2i[] => { + 'use gpu'; + const adjacentOffsets = [ + d.vec2i(-1, 0), + d.vec2i(0, -1), + d.vec2i(1, 0), + d.vec2i(0, 1), + ]; + for (let i = 0; i < 4; i++) { + adjacentOffsets[i] = std.clamp( + coords + adjacentOffsets[i], + d.vec2i(), + bounds - d.vec2i(1), + ); + } + return adjacentOffsets; +}; export const brushLayout = tgpu.bindGroupLayout({ brushParams: { uniform: p.BrushParams }, @@ -30,6 +29,7 @@ export const brushFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const pixelPos = input.gid.xy; const brushSettings = brushLayout.$.brushParams; @@ -43,10 +43,7 @@ export const brushFn = tgpu.computeFn({ if (distSquared < radiusSquared) { const brushWeight = std.exp(-distSquared / radiusSquared); - forceVec = std.mul( - brushSettings.forceScale * brushWeight, - brushSettings.delta, - ); + forceVec = brushSettings.forceScale * brushWeight * brushSettings.delta; inkAmount = brushSettings.inkAmount * brushWeight; } @@ -73,11 +70,12 @@ export const addForcesFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const pixelPos = input.gid.xy; const currentVel = std.textureLoad(addForcesLayout.$.src, pixelPos, 0).xy; const forceVec = std.textureLoad(addForcesLayout.$.force, pixelPos, 0).xy; const timeStep = addForcesLayout.$.simParams.dt; - const newVel = std.add(currentVel, std.mul(timeStep, forceVec)); + const newVel = currentVel + timeStep * forceVec; std.textureStore(addForcesLayout.$.dst, pixelPos, d.vec4f(newVel, 0, 1)); }); @@ -92,6 +90,7 @@ export const advectFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const texSize = std.textureDimensions(advectLayout.$.src); const pixelPos = input.gid.xy; @@ -105,16 +104,13 @@ export const advectFn = tgpu.computeFn({ const velocity = std.textureLoad(advectLayout.$.src, pixelPos, 0); const timeStep = advectLayout.$.simParams.dt; - const prevPos = std.sub(d.vec2f(pixelPos), std.mul(timeStep, velocity.xy)); + const prevPos = d.vec2f(pixelPos) - timeStep * velocity.xy; const clampedPos = std.clamp( prevPos, d.vec2f(-0.5), - d.vec2f(texSize.xy).sub(0.5), - ); - const normalizedPos = std.div( - clampedPos.add(0.5), - d.vec2f(texSize.xy), + d.vec2f(texSize.xy) - 0.5, ); + const normalizedPos = (clampedPos + 0.5) / d.vec2f(texSize.xy); const prevVelocity = std.textureSampleLevel( advectLayout.$.src, @@ -136,6 +132,7 @@ export const diffusionFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const pixelPos = d.vec2i(input.gid.xy); const texSize = d.vec2i( std.textureDimensions(diffusionLayout.$.in), @@ -153,14 +150,9 @@ export const diffusionFn = tgpu.computeFn({ const viscosity = diffusionLayout.$.simParams.viscosity; const diffuseRate = viscosity * timeStep; - const blendFactor = 1.0 / (4.0 + diffuseRate); - const diffusedVal = std.mul( - d.vec4f(blendFactor), - std.add( - std.add(std.add(leftVal, rightVal), std.add(upVal, downVal)), - std.mul(d.f32(diffuseRate), centerVal), - ), - ); + const blendFactor = 1 / (4 + diffuseRate); + const diffusedVal = d.vec4f(blendFactor) * + (leftVal + rightVal + upVal + downVal + centerVal * diffuseRate); std.textureStore(diffusionLayout.$.out, pixelPos, diffusedVal); }); @@ -175,9 +167,7 @@ export const divergenceFn = tgpu.computeFn({ in: { gid: d.builtin.globalInvocationId }, })((input) => { const pixelPos = d.vec2i(input.gid.xy); - const texSize = d.vec2i( - std.textureDimensions(divergenceLayout.$.vel), - ); + const texSize = d.vec2i(std.textureDimensions(divergenceLayout.$.vel)); const neighbors = getNeighbors(pixelPos, texSize); @@ -236,6 +226,7 @@ export const projectFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const pixelPos = d.vec2i(input.gid.xy); const texSize = d.vec2i(std.textureDimensions(projectLayout.$.vel)); const velocity = std.textureLoad(projectLayout.$.vel, pixelPos, 0); @@ -251,7 +242,7 @@ export const projectFn = tgpu.computeFn({ 0.5 * (rightPressure.x - leftPressure.x), 0.5 * (downPressure.x - upPressure.x), ); - const projectedVel = std.sub(velocity.xy, pressureGrad); + const projectedVel = velocity.xy - pressureGrad; std.textureStore(projectLayout.$.out, pixelPos, d.vec4f(projectedVel, 0, 1)); }); @@ -267,21 +258,19 @@ export const advectInkFn = tgpu.computeFn({ workgroupSize: [p.WORKGROUP_SIZE_X, p.WORKGROUP_SIZE_Y], in: { gid: d.builtin.globalInvocationId }, })((input) => { + 'use gpu'; const texSize = std.textureDimensions(advectInkLayout.$.src); const pixelPos = input.gid.xy; const velocity = std.textureLoad(advectInkLayout.$.vel, pixelPos, 0).xy; const timeStep = advectInkLayout.$.simParams.dt; - const prevPos = std.sub(d.vec2f(pixelPos), std.mul(timeStep, velocity)); + const prevPos = d.vec2f(pixelPos) - timeStep * velocity; const clampedPos = std.clamp( prevPos, d.vec2f(-0.5), - std.sub(d.vec2f(texSize.xy), d.vec2f(0.5)), - ); - const normalizedPos = std.div( - std.add(clampedPos, d.vec2f(0.5)), - d.vec2f(texSize.xy), + d.vec2f(texSize.xy) - d.vec2f(0.5), ); + const normalizedPos = (clampedPos + 0.5) / d.vec2f(texSize.xy); const inkVal = std.textureSampleLevel( advectInkLayout.$.src, diff --git a/apps/typegpu-docs/src/examples/tests/uniformity/index.ts b/apps/typegpu-docs/src/examples/tests/uniformity/index.ts index 583d9b1d68..797e6465c4 100644 --- a/apps/typegpu-docs/src/examples/tests/uniformity/index.ts +++ b/apps/typegpu-docs/src/examples/tests/uniformity/index.ts @@ -21,12 +21,13 @@ const fragmentShader = tgpu.fragmentFn({ in: { uv: d.vec2f }, out: d.vec4f, })((input) => { - const uv = input.uv.add(1).div(2).mul(d.vec2f(canvasRatioUniform.$, 1)); - const gridedUV = std.floor(uv.mul(gridSizeUniform.$)); + 'use gpu'; + const uv = (input.uv + 1) / 2 * d.vec2f(canvasRatioUniform.$, 1); + const gridedUV = std.floor(uv * gridSizeUniform.$); randf.seed2(gridedUV); - return d.vec4f(d.vec3f(randf.sample()), 1.0); + return d.vec4f(d.vec3f(randf.sample()), 1); }); const pipelineCache = new Map>(); diff --git a/apps/typegpu-docs/src/examples/threejs/attractors/index.ts b/apps/typegpu-docs/src/examples/threejs/attractors/index.ts index bc061673af..24287e51bf 100644 --- a/apps/typegpu-docs/src/examples/threejs/attractors/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/attractors/index.ts @@ -172,14 +172,12 @@ const initCompute = t3.toTSL(() => { const instanceIndex = t3.instanceIndex.$; randf.seed(instanceIndex / count + seed); - const basePosition = randf.inUnitCube() - .sub(0.5) - .mul(d.vec3f(5, 0.2, 5)); + const basePosition = (randf.inUnitCube() - 0.5) * d.vec3f(5, 0.2, 5); positionBuffer.$[instanceIndex] = d.vec3f(basePosition); const phi = randf.sample() * 2 * Math.PI; const theta = randf.sample() * 2; - const baseVelocity = sphericalToVec3(phi, theta).mul(0.05); + const baseVelocity = sphericalToVec3(phi, theta) * 0.05; velocityBuffer.$[instanceIndex] = d.vec3f(baseVelocity); }); @@ -214,7 +212,7 @@ const update = t3.toTSL(() => { const attractorPosition = attractorsPositions.$[i].xyz; const attractorRotationAxis = attractorsRotationAxes.$[i].xyz; - const toAttractor = attractorPosition.sub(position); + const toAttractor = attractorPosition - position; const distance = std.length(toAttractor); const direction = std.normalize(toAttractor); @@ -223,33 +221,31 @@ const update = t3.toTSL(() => { getParticleMass() * gravityConstant / (distance ** 2); - const gravityForce = direction.mul(gravityStrength); - force = force.add(gravityForce); + const gravityForce = direction * gravityStrength; + force += gravityForce; // spinning - const spinningForce = attractorRotationAxis - .mul(gravityStrength) - .mul(spinningStrength.$); + const spinningForce = attractorRotationAxis * gravityStrength * + spinningStrength.$; const spinningVelocity = std.cross(spinningForce, toAttractor); - force = force.add(spinningVelocity); + force += spinningVelocity; } // velocity - velocity = velocity.add(force.mul(delta)); + velocity += force * delta; const speed = std.length(velocity); if (speed > maxSpeed.$) { - velocity = std.normalize(velocity).mul(maxSpeed.$); + velocity = std.normalize(velocity) * maxSpeed.$; } - velocity = velocity.mul(1 - velocityDamping.$); + velocity *= 1 - velocityDamping.$; // position - position = position.add(velocity.mul(delta)); + position += velocity * delta; // box loop const halfHalfExtent = boundHalfExtent.$ / 2; - position = std - .mod(position.add(halfHalfExtent), boundHalfExtent.$) - .sub(halfHalfExtent); + position = std.mod(position + halfHalfExtent, boundHalfExtent.$) - + halfHalfExtent; positionBuffer.$[t3.instanceIndex.$] = d.vec3f(position); velocityBuffer.$[t3.instanceIndex.$] = d.vec3f(velocity); diff --git a/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.ts b/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.ts index 2f9a1954cb..d939e52bb9 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-cloth/index.ts @@ -237,7 +237,7 @@ function setupClothMesh(): THREE.Mesh { clothMaterial.colorNode = t3.toTSL(() => { 'use gpu'; const uv = t3.uv().$; - const pattern = checkerBoard(uv.mul(5)); + const pattern = checkerBoard(uv * 5); return std.mix(patternUniforms.color1.$, patternUniforms.color2.$, pattern); }); diff --git a/apps/typegpu-docs/src/examples/threejs/compute-cloth/triNoise.ts b/apps/typegpu-docs/src/examples/threejs/compute-cloth/triNoise.ts index f217af49d6..fc95cc3fc5 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-cloth/triNoise.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-cloth/triNoise.ts @@ -27,14 +27,14 @@ export const triNoise3D = tgpu.fn([d.vec3f, d.f32, d.f32], d.f32)( let bp = d.vec3f(p); for (let i = d.f32(); i <= 3.0; i += 1) { - const dg = tri3(bp.mul(d.vec3f(2.0))); - p = p.add(dg.add(d.vec3f(time * 0.1 * speed))); - bp = bp.mul(d.vec3f(1.8)); + const dg = tri3(bp * 2); + p += dg + time * 0.1 * speed; + bp *= 1.8; z *= 1.5; - p = p.mul(1.2); + p *= 1.2; const t = tri(p.z + tri(p.x + tri(p.y))); rz = rz + t / z; - bp = bp.add(d.vec3f(0.14)); + bp += 0.14; } return rz; diff --git a/apps/typegpu-docs/src/examples/threejs/compute-cloth/verlet.ts b/apps/typegpu-docs/src/examples/threejs/compute-cloth/verlet.ts index dc63c20db4..798a7dd539 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-cloth/verlet.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-cloth/verlet.ts @@ -221,11 +221,10 @@ export class VerletSimulation { const vertex0Position = this.vertexPositionBuffer.$[vertexId.x]; const vertex1Position = this.vertexPositionBuffer.$[vertexId.y]; - const delta = vertex1Position.sub(vertex0Position); + const delta = vertex1Position - vertex0Position; const dist = std.max(std.length(delta), 0.000001); - const force = delta.mul( - ((dist - restLength) * this.stiffnessUniform.$ * 0.5) / dist, - ); + const force = delta * + ((dist - restLength) * this.stiffnessUniform.$ * 0.5) / dist; this.springForceBuffer.$[idx] = d.vec3f(force); }).compute(springCount); @@ -257,7 +256,7 @@ export class VerletSimulation { const position = this.vertexPositionBuffer.$[idx]; let force = d.vec3f(this.vertexForceBuffer.$[idx]); - force = force.mul(this.dampeningUniform.$); + force *= this.dampeningUniform.$; const ptrStart = springPointer; const ptrEnd = ptrStart + springCount; @@ -266,7 +265,7 @@ export class VerletSimulation { const springForce = this.springForceBuffer.$[springId]; const springVertexIds = this.springVertexIdBuffer.$[springId]; const factor = std.select(-1, 1, springVertexIds.x === idx); - force = force.add(springForce.mul(d.f32(factor))); + force += springForce * factor; } // gravity @@ -279,12 +278,10 @@ export class VerletSimulation { force.z -= windForce; // collision with sphere - const deltaSphere = position.add(force).sub(spherePositionUniform.$); + const deltaSphere = position + force - spherePositionUniform.$; const dist = std.length(deltaSphere); - const sphereForce = deltaSphere.mul( - (std.max(0, sphereRadius - dist) / dist) * sphereUniform.$, - ); - force = force.add(sphereForce); + force += deltaSphere * (std.max(0, sphereRadius - dist) / dist) * + sphereUniform.$; this.vertexForceBuffer.$[idx] = d.vec3f(force); this.vertexPositionBuffer.$[idx] = this.vertexPositionBuffer.$[idx].add( diff --git a/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.ts b/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.ts index b0d2005f23..76f1887d67 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-geometry/index.ts @@ -89,32 +89,18 @@ const jelly = TSL.Fn(({ renderer, geometry, object }) => { let position = positionAccessor.$[instanceIdx]; if (pointerPosition.$.w === 1) { - const worldPosition = modelMatrixAccessor.$.mul( - d.vec4f(position, 1), - ).xyz; + const worldPosition = (modelMatrixAccessor.$ * d.vec4f(position, 1)).xyz; const dist = std.distance(worldPosition, pointerPosition.$.xyz); - const direction = std.normalize( - pointerPosition.$.xyz.sub(worldPosition), - ); - const power = std.max(brushSize.$ - dist, 0) * - brushStrength.$; - - positionAccessor.$[instanceIdx] = position.add( - direction.mul(power), - ); + const direction = std.normalize(pointerPosition.$.xyz - worldPosition); + const power = std.max(brushSize.$ - dist, 0) * brushStrength.$; + + positionAccessor.$[instanceIdx] = position + direction * power; position = positionAccessor.$[instanceIdx]; } - const dist = std.distance( - basePosition, - position, - ); - const force = basePosition - .sub(position) - .mul(elasticity.$ * dist); - const speed = speedAccessor.$[instanceIdx] - .add(force) - .mul(damping.$); + const dist = std.distance(basePosition, position); + const force = (basePosition - position) * elasticity.$ * dist; + const speed = (speedAccessor.$[instanceIdx] + force) * damping.$; speedAccessor.$[instanceIdx] = d.vec3f(speed); positionAccessor.$[instanceIdx] = position.add(speed); diff --git a/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/entities.ts b/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/entities.ts index e8bf859caa..01613975db 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/entities.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/entities.ts @@ -47,9 +47,8 @@ export const floor = (() => { floor.position.y = 0; floor.material.opacityNode = t3.toTSL(() => { 'use gpu'; - return std.saturate( - std.length(t3.fromTSL(TSL.positionLocal.xz, d.vec2f).$.mul(0.05)), - ) - 1; + const localPos = t3.fromTSL(TSL.positionLocal, d.vec3f).$; + return std.saturate(std.length(localPos.xz * 0.05)) - 1; }); floor.layers.disableAll(); floor.layers.enable(1); diff --git a/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/index.ts b/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/index.ts index 742f313201..0ed810d8fa 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-particles-snow/index.ts @@ -61,10 +61,7 @@ collisionPosMaterial.outputNode = TSL.vec4( 1, ); -const positionBuffer = t3.instancedArray( - maxParticleCount, - d.vec3f, -); +const positionBuffer = t3.instancedArray(maxParticleCount, d.vec3f); const scaleBuffer = t3.instancedArray(maxParticleCount, d.vec3f); const staticPositionBuffer = t3.instancedArray(maxParticleCount, d.vec3f); const dataBuffer = t3.instancedArray(maxParticleCount, d.vec4f); @@ -75,8 +72,7 @@ const computeInit = t3.toTSL(() => { randf.seed(instanceIdx / maxParticleCount); const rand = d.vec3f(randf.sample(), randf.sample(), randf.sample()); - const randPos = rand.mul(d.vec3f(100, 500, 100)) - .add(d.vec3f(-50, 3, -50)); + const randPos = rand * d.vec3f(100, 500, 100) + d.vec3f(-50, 3, -50); positionBuffer.$[instanceIdx] = d.vec3f(randPos); scaleBuffer.$[instanceIdx] = d.vec3f(randf.sample() * 0.8 + 0.2); @@ -133,9 +129,9 @@ function particles(isStatic: boolean = false) { material.positionNode = t3.toTSL(() => { 'use gpu'; - return t3.fromTSL(TSL.positionLocal, d.vec3f).$ - .mul(scaleBuffer.$[t3.instanceIndex.$]) - .add(posBuffer.$[t3.instanceIndex.$]); + const iidx = t3.instanceIndex.$; + const localPos = t3.fromTSL(TSL.positionLocal, d.vec3f).$; + return localPos * scaleBuffer.$[iidx] + posBuffer.$[iidx]; }); const rainParticles = new THREE.Mesh(sphereGeometry, material); diff --git a/apps/typegpu-docs/src/examples/threejs/compute-particles/index.ts b/apps/typegpu-docs/src/examples/threejs/compute-particles/index.ts index 9515df44d0..c1cdda6a7d 100644 --- a/apps/typegpu-docs/src/examples/threejs/compute-particles/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/compute-particles/index.ts @@ -69,13 +69,13 @@ const computeAccessor = t3.toTSL(() => { let velocity = velocities.$[instanceIdx]; velocity.y += gravity.$; - position = position.add(velocity); - velocity = velocity.mul(friction.$); + position += velocity; + velocity *= friction.$; if (position.y < 0) { position.y = 0; velocity.y = -velocity.y * bounce.$; - velocity = velocity.mul(d.vec3f(0.9, 1, 0.9)); + velocity *= d.vec3f(0.9, 1, 0.9); } positions.$[instanceIdx] = d.vec3f(position); @@ -85,11 +85,8 @@ const computeAccessor = t3.toTSL(() => { const material = new THREE.SpriteNodeMaterial(); material.colorNode = t3.toTSL(() => { 'use gpu'; - return d.vec4f( - t3.uv().$.mul(colors.$[t3.instanceIndex.$].xy), - 0, - 1, - ); + const iidx = t3.instanceIndex.$; + return d.vec4f(t3.uv().$ * colors.$[iidx].xy, 0, 1); }); material.positionNode = positions.node.toAttribute(); material.scaleNode = size.node; @@ -124,14 +121,14 @@ const computeHit = t3.toTSL(() => { let velocity = velocities.$[instanceIdx]; const dist = std.distance(position, clickPosition.$); - const dir = std.normalize(position.sub(clickPosition.$)); + const dir = std.normalize(position - clickPosition.$); const distArea = std.max(0, 3 - dist); const power = distArea * 0.01; randf.seed(d.f32(instanceIdx / amount)); const relativePower = power * (1.5 * randf.sample() + 0.5); - velocity = velocity.add(dir.mul(relativePower)); + velocity += dir * relativePower; velocities.$[instanceIdx] = d.vec3f(velocity); }).compute(particleCount).setName('Hit Particles TypeGPU'); diff --git a/apps/typegpu-docs/src/examples/threejs/simple/index.ts b/apps/typegpu-docs/src/examples/threejs/simple/index.ts index 56fdc02884..19c6938ac3 100644 --- a/apps/typegpu-docs/src/examples/threejs/simple/index.ts +++ b/apps/typegpu-docs/src/examples/threejs/simple/index.ts @@ -23,7 +23,7 @@ const material = new THREE.MeshBasicNodeMaterial(); material.colorNode = t3.toTSL(() => { 'use gpu'; - const coords = t3.uv().$.mul(2); + const coords = t3.uv().$ * 2; const pattern = perlin3d.sample(d.vec3f(coords, t3.time.$ * 0.2)); return d.vec4f(std.tanh(pattern * 5), 0.2, 0.4, 1); }); @@ -34,12 +34,10 @@ material.positionNode = t3.toTSL(() => { 'use gpu'; const localPos = positionAttrib.$; const t = t3.time.$; - const patternX = perlin3d.sample(localPos.add(d.vec3f(t, 0, 0))); - const patternY = perlin3d.sample(localPos.add(d.vec3f(t, 0, 1))); - const patternZ = perlin3d.sample(localPos.add(d.vec3f(t, 0, 2))); - return localPos.add( - d.vec3f(patternX, patternY, patternZ).mul(0.5), - ); + const patternX = perlin3d.sample(localPos + d.vec3f(t, 0, 0)); + const patternY = perlin3d.sample(localPos + d.vec3f(t, 0, 1)); + const patternZ = perlin3d.sample(localPos + d.vec3f(t, 0, 2)); + return localPos + d.vec3f(patternX, patternY, patternZ) * 0.5; }); const mesh = new THREE.Mesh( diff --git a/apps/typegpu-docs/src/utils/examples/types.ts b/apps/typegpu-docs/src/utils/examples/types.ts index 70338722a0..4a4ac99a2e 100644 --- a/apps/typegpu-docs/src/utils/examples/types.ts +++ b/apps/typegpu-docs/src/utils/examples/types.ts @@ -26,6 +26,11 @@ export type ExampleSrcFile = { */ path: string; content: string; + /** + * Stripped down version of the content, without + * overloaded operators (if they were used) + */ + tsnotoverContent?: string | undefined; }; export type ExampleCommonFile = { @@ -35,6 +40,11 @@ export type ExampleCommonFile = { */ path: string; content: string; + /** + * Stripped down version of the content, without + * overloaded operators (if they were used) + */ + tsnotoverContent?: string | undefined; }; export interface ThumbnailPair { diff --git a/apps/typegpu-docs/tsconfig.json b/apps/typegpu-docs/tsconfig.json index 117bde7ac6..7a408f61bb 100644 --- a/apps/typegpu-docs/tsconfig.json +++ b/apps/typegpu-docs/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "jsx": "react-jsx", "jsxImportSource": "react", + "lib": ["ESNext", "DOM", "DOM.Iterable"], "types": ["@webgpu/types"] }, "include": [".astro/types.d.ts", "**/*"], diff --git a/package.json b/package.json index b9d297e45c..4f6d9ad641 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ ], "overrides": { "rollup": "4.34.8", + "typescript": "catalog:types", "three": "catalog:example" } } diff --git a/packages/typegpu-noise/src/generator.ts b/packages/typegpu-noise/src/generator.ts index 92502a8a42..9fce00d9c4 100644 --- a/packages/typegpu-noise/src/generator.ts +++ b/packages/typegpu-noise/src/generator.ts @@ -1,5 +1,5 @@ import tgpu, { d, type TgpuFnShell, type TgpuSlot } from 'typegpu'; -import { add, cos, dot, fract } from 'typegpu/std'; +import { cos, dot, fract } from 'typegpu/std'; export interface StatefulGenerator { seed?: (seed: number) => void; @@ -28,11 +28,13 @@ export const BPETER: StatefulGenerator = (() => { }), seed3: tgpu.fn([d.vec3f])((value) => { - seed.$ = add(value.xy, d.vec2f(value.z)); + 'use gpu'; + seed.$ = value.xy + d.vec2f(value.z); }), seed4: tgpu.fn([d.vec4f])((value) => { - seed.$ = add(value.xy, value.zw); + 'use gpu'; + seed.$ = value.xy + value.zw; }), sample: randomGeneratorShell(() => { diff --git a/packages/typegpu-noise/src/perlin-2d/algorithm.ts b/packages/typegpu-noise/src/perlin-2d/algorithm.ts index 0a6b42515d..c5e6ddabc4 100644 --- a/packages/typegpu-noise/src/perlin-2d/algorithm.ts +++ b/packages/typegpu-noise/src/perlin-2d/algorithm.ts @@ -1,10 +1,11 @@ import tgpu, { d } from 'typegpu'; -import { add, dot, floor, fract, mul, sub } from 'typegpu/std'; +import { dot, floor, fract } from 'typegpu/std'; import { randOnUnitCircle, randSeed2 } from '../random.ts'; -import { quinticDerivative2, quinticInterpolation2 } from '../utils.ts'; +import { quinticDerivative, quinticInterpolation } from '../utils.ts'; export const computeJunctionGradient = tgpu.fn([d.vec2i], d.vec2f)((pos) => { - randSeed2(mul(0.001, d.vec2f(pos))); + 'use gpu'; + randSeed2(0.001 * d.vec2f(pos)); return randOnUnitCircle(); }); @@ -14,22 +15,23 @@ export const getJunctionGradientSlot = tgpu.slot(computeJunctionGradient); * Returns value of Perlin Noise at point `pos` */ export const sample = tgpu.fn([d.vec2f], d.f32)((pos) => { + 'use gpu'; // Reference: https://iquilezles.org/articles/gradientnoise/ const i = d.vec2i(floor(pos)); const f = fract(pos); - const u = quinticInterpolation2(f); + const u = quinticInterpolation(f); const ga = getJunctionGradientSlot.$(i); - const gb = getJunctionGradientSlot.$(add(i, d.vec2i(1, 0))); - const gc = getJunctionGradientSlot.$(add(i, d.vec2i(0, 1))); - const gd = getJunctionGradientSlot.$(add(i, d.vec2i(1, 1))); + const gb = getJunctionGradientSlot.$(i + d.vec2i(1, 0)); + const gc = getJunctionGradientSlot.$(i + d.vec2i(0, 1)); + const gd = getJunctionGradientSlot.$(i + d.vec2i(1, 1)); - const va = dot(ga, sub(f, d.vec2f(0, 0))); - const vb = dot(gb, sub(f, d.vec2f(1, 0))); - const vc = dot(gc, sub(f, d.vec2f(0, 1))); - const vd = dot(gd, sub(f, d.vec2f(1, 1))); + const va = dot(ga, f - d.vec2f(0, 0)); + const vb = dot(gb, f - d.vec2f(1, 0)); + const vc = dot(gc, f - d.vec2f(0, 1)); + const vd = dot(gd, f - d.vec2f(1, 1)); const noise = va + u.x * (vb - va) + u.y * (vc - va) + u.x * u.y * (va - vb - vc + vd); @@ -42,38 +44,31 @@ export const sample = tgpu.fn([d.vec2f], d.f32)((pos) => { * the gradient of the function at that point as yz coordinates. */ export const sampleWithGradient = tgpu.fn([d.vec2f], d.vec3f)((pos) => { + 'use gpu'; // Reference: https://iquilezles.org/articles/gradientnoise/ const i = d.vec2i(floor(pos)); const f = fract(pos); - const u = quinticInterpolation2(f); - const du = quinticDerivative2(f); + const u = quinticInterpolation(f); + const du = quinticDerivative(f); const ga = getJunctionGradientSlot.$(i); - const gb = getJunctionGradientSlot.$(add(i, d.vec2i(1, 0))); - const gc = getJunctionGradientSlot.$(add(i, d.vec2i(0, 1))); - const gd = getJunctionGradientSlot.$(add(i, d.vec2i(1, 1))); + const gb = getJunctionGradientSlot.$(i + d.vec2i(1, 0)); + const gc = getJunctionGradientSlot.$(i + d.vec2i(0, 1)); + const gd = getJunctionGradientSlot.$(i + d.vec2i(1, 1)); - const va = dot(ga, sub(f, d.vec2f(0, 0))); - const vb = dot(gb, sub(f, d.vec2f(1, 0))); - const vc = dot(gc, sub(f, d.vec2f(0, 1))); - const vd = dot(gd, sub(f, d.vec2f(1, 1))); + const va = dot(ga, f - d.vec2f(0, 0)); + const vb = dot(gb, f - d.vec2f(1, 0)); + const vc = dot(gc, f - d.vec2f(0, 1)); + const vd = dot(gd, f - d.vec2f(1, 1)); const noise = va + u.x * (vb - va) + u.y * (vc - va) + u.x * u.y * (va - vb - vc + vd); - // ga + u.x*(gb-ga) + u.y*(gc-ga) + u.x*u.y*(ga-gb-gc+gd) + du * (u.yx*(va-vb-vc+vd) + vec2(vb,vc) - va)) - const grad = add( - ga, - add( - add( - add(mul(u.x, sub(gb, ga)), mul(u.y, sub(gc, ga))), - mul(u.x, mul(u.y, add(sub(sub(ga, gb), gc), gd))), - ), - mul(du, sub(add(mul(u.yx, va - vb - vc + vd), d.vec2f(vb, vc)), va)), - ), - ); + const grad = ga + u.x * (gb - ga) + u.y * (gc - ga) + + u.x * u.y * (ga - gb - gc + gd) + + du * (u.yx * (va - vb - vc + vd) + d.vec2f(vb, vc) - va); return d.vec3f(noise, grad); }); diff --git a/packages/typegpu-noise/src/perlin-3d/algorithm.ts b/packages/typegpu-noise/src/perlin-3d/algorithm.ts index bdf895b92c..11365f969a 100644 --- a/packages/typegpu-noise/src/perlin-3d/algorithm.ts +++ b/packages/typegpu-noise/src/perlin-3d/algorithm.ts @@ -1,36 +1,39 @@ import tgpu from 'typegpu'; import * as d from 'typegpu/data'; -import { add, dot, floor, mix, mul, sub } from 'typegpu/std'; +import { dot, floor, mix } from 'typegpu/std'; import { randOnUnitSphere, randSeed3 } from '../random.ts'; -import { quinticInterpolation3 } from '../utils.ts'; +import { quinticInterpolation } from '../utils.ts'; export const computeJunctionGradient = tgpu.fn([d.vec3i], d.vec3f)((pos) => { - randSeed3(mul(0.001, d.vec3f(pos))); + 'use gpu'; + randSeed3(0.001 * d.vec3f(pos)); return randOnUnitSphere(); }); export const getJunctionGradientSlot = tgpu.slot(computeJunctionGradient); -const dotProdGrid = tgpu.fn([d.vec3f, d.vec3f], d.f32)((pos, junction) => { - const relative = sub(pos, junction); - const gridVector = getJunctionGradientSlot.value(d.vec3i(junction)); +const dotProdGrid = (pos: d.v3f, junction: d.v3f): number => { + 'use gpu'; + const relative = pos - junction; + const gridVector = getJunctionGradientSlot.$(d.vec3i(junction)); return dot(relative, gridVector); -}); +}; export const sample = tgpu.fn([d.vec3f], d.f32)((pos) => { + 'use gpu'; const minJunction = floor(pos); const xyz = dotProdGrid(pos, minJunction); - const xyZ = dotProdGrid(pos, add(minJunction, d.vec3f(0, 0, 1))); - const xYz = dotProdGrid(pos, add(minJunction, d.vec3f(0, 1, 0))); - const xYZ = dotProdGrid(pos, add(minJunction, d.vec3f(0, 1, 1))); - const Xyz = dotProdGrid(pos, add(minJunction, d.vec3f(1, 0, 0))); - const XyZ = dotProdGrid(pos, add(minJunction, d.vec3f(1, 0, 1))); - const XYz = dotProdGrid(pos, add(minJunction, d.vec3f(1, 1, 0))); - const XYZ = dotProdGrid(pos, add(minJunction, d.vec3f(1, 1, 1))); - - const partial = sub(pos, minJunction); - const smoothPartial = quinticInterpolation3(partial); + const xyZ = dotProdGrid(pos, minJunction + d.vec3f(0, 0, 1)); + const xYz = dotProdGrid(pos, minJunction + d.vec3f(0, 1, 0)); + const xYZ = dotProdGrid(pos, minJunction + d.vec3f(0, 1, 1)); + const Xyz = dotProdGrid(pos, minJunction + d.vec3f(1, 0, 0)); + const XyZ = dotProdGrid(pos, minJunction + d.vec3f(1, 0, 1)); + const XYz = dotProdGrid(pos, minJunction + d.vec3f(1, 1, 0)); + const XYZ = dotProdGrid(pos, minJunction + d.vec3f(1, 1, 1)); + + const partial = pos - minJunction; + const smoothPartial = quinticInterpolation(partial); // Resolving the z-axis into a xy-slice const xy = mix(xyz, xyZ, smoothPartial.z); diff --git a/packages/typegpu-noise/src/random.ts b/packages/typegpu-noise/src/random.ts index 708e681b5a..6dfec0fd16 100644 --- a/packages/typegpu-noise/src/random.ts +++ b/packages/typegpu-noise/src/random.ts @@ -3,9 +3,7 @@ import { cos, dot, log, - mul, normalize, - pow, select, sign, sin, @@ -93,12 +91,13 @@ export const randOnUnitCircle: TgpuFn<() => d.Vec2f> = tgpu export const randInUnitSphere: TgpuFn<() => d.Vec3f> = tgpu .fn([], d.vec3f)(() => { + 'use gpu'; const u = randomGeneratorSlot.$.sample(); const v = d.vec3f(randNormal(0, 1), randNormal(0, 1), randNormal(0, 1)); const vNorm = normalize(v); - return vNorm.mul(pow(u, 0.33)); + return vNorm * (u ** 0.33); }); export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu @@ -114,18 +113,20 @@ export const randOnUnitSphere: TgpuFn<() => d.Vec3f> = tgpu export const randInUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = tgpu .fn([d.vec3f], d.vec3f)((normal) => { + 'use gpu'; const value = randInUnitSphere(); const alignment = dot(normal, value); - return mul(sign(alignment), value); + return sign(alignment) * value; }); export const randOnUnitHemisphere: TgpuFn<(normal: d.Vec3f) => d.Vec3f> = tgpu .fn([d.vec3f], d.vec3f)((normal) => { + 'use gpu'; const value = randOnUnitSphere(); const alignment = dot(normal, value); - return mul(sign(alignment), value); + return sign(alignment) * value; }); export const randUniformExclusive: TgpuFn<() => d.F32> = tgpu diff --git a/packages/typegpu-noise/src/utils.ts b/packages/typegpu-noise/src/utils.ts index f267ee9fb3..905fa09e33 100644 --- a/packages/typegpu-noise/src/utils.ts +++ b/packages/typegpu-noise/src/utils.ts @@ -1,5 +1,4 @@ -import tgpu, { d } from 'typegpu'; -import { add, mul, sub } from 'typegpu/std'; +import type { d } from 'typegpu'; export type Prettify = & { @@ -11,42 +10,23 @@ export type PrefixKeys = { [K in keyof T as K extends string ? `${Prefix}${K}` : K]: T[K]; }; -// t * t * t * (t * (6t - (15, 15)) + (10, 10)); -const quinticInterpolationImpl = (t: T): T => { - 'use gpu'; - return mul(mul(t, mul(t, t)), add(mul(t, sub(mul(t, 6), 15)), 10)); - // TODO: Write it using fluent APIs when it becomes available: - // return t.mul(t).mul(t).mul((t.mul(6).sub(15)).add(10)); -}; - -/** - * Works as a replacement for smoothstep, but with a continuous - * second derivative, which in e.g. smooth normals - */ -export const quinticInterpolation2 = tgpu - .fn([d.vec2f], d.vec2f)(quinticInterpolationImpl); - /** * Works as a replacement for smoothstep, but with a continuous * second derivative, which in e.g. smooth normals */ -export const quinticInterpolation3 = tgpu - .fn([d.vec3f], d.vec3f)(quinticInterpolationImpl); - -// 30 * t * t * (t * (t - (2, 2)) + (1, 1)) -const quinticDerivativeImpl = (t: T): T => { +export function quinticInterpolation(t: d.v2f): d.v2f; +export function quinticInterpolation(t: d.v3f): d.v3f; +export function quinticInterpolation(t: d.vecBase): d.vecBase { 'use gpu'; - return mul(mul(mul(30, t), t), add(mul(t, sub(t, 2)), 1)); -}; + return t * t * t * (t * (t * 6 - 15) + 10); +} /** - * Derivative of {@link quinticInterpolation2} + * Derivative of {@link quinticInterpolation} */ -export const quinticDerivative2 = tgpu - .fn([d.vec2f], d.vec2f)(quinticDerivativeImpl); - -/** - * Derivative of {@link quinticInterpolation3} - */ -export const quinticDerivative3 = tgpu - .fn([d.vec3f], d.vec3f)(quinticDerivativeImpl); +export function quinticDerivative(t: d.v2f): d.v2f; +export function quinticDerivative(t: d.v3f): d.v3f; +export function quinticDerivative(t: d.vecBase): d.vecBase { + 'use gpu'; + return 30 * t * t * (t * (t - 2)) + 1; +} diff --git a/packages/typegpu-sdf/src/2d.ts b/packages/typegpu-sdf/src/2d.ts index bf7bd4a14e..e71feb41ce 100644 --- a/packages/typegpu-sdf/src/2d.ts +++ b/packages/typegpu-sdf/src/2d.ts @@ -3,20 +3,18 @@ import { f32, type v2f, vec2f, vec3f } from 'typegpu/data'; import { abs, acos, - add, clamp, cos, + distance, dot, length, max, min, - mul, pow, saturate, sign, sin, sqrt, - sub, } from 'typegpu/std'; /** @@ -34,7 +32,8 @@ export const sdDisk = tgpu.fn([vec2f, f32], f32)((point, radius) => { * @param size Half-dimensions of the box */ export const sdBox2d = tgpu.fn([vec2f, vec2f], f32)((point, size) => { - const d = sub(abs(point), size); + 'use gpu'; + const d = abs(point) - size; return length(max(d, vec2f(0))) + min(max(d.x, d.y), 0); }); @@ -46,7 +45,8 @@ export const sdBox2d = tgpu.fn([vec2f, vec2f], f32)((point, size) => { */ export const sdRoundedBox2d = tgpu .fn([vec2f, vec2f, f32], f32)((point, size, cornerRadius) => { - const d = add(sub(abs(point), size), vec2f(cornerRadius)); + 'use gpu'; + const d = abs(point) - size + vec2f(cornerRadius); return length(max(d, vec2f(0))) + min(max(d.x, d.y), 0) - cornerRadius; }); @@ -57,10 +57,11 @@ export const sdRoundedBox2d = tgpu * @param B Second endpoint of the line */ export const sdLine = tgpu.fn([vec2f, vec2f, vec2f], f32)((point, A, B) => { - const pa = sub(point, A); - const ba = sub(B, A); + 'use gpu'; + const pa = point - A; + const ba = B - A; const h = max(0, min(1, dot(pa, ba) / dot(ba, ba))); - return length(sub(pa, ba.mul(h))); + return distance(pa, ba * h); }); const dot2 = (v: v2f) => { @@ -77,10 +78,11 @@ const dot2 = (v: v2f) => { */ export const sdBezier = tgpu.fn([vec2f, vec2f, vec2f, vec2f], f32)( (point, A, B, C) => { - const a = B.sub(A); - const b = A.sub(B.mul(2)).add(C); - const c = a.mul(f32(2)); - const d = A.sub(point); + 'use gpu'; + const a = B - A; + const b = A - B * 2 + C; + const c = a * 2; + const d = A - point; const dotB = max(dot(b, b), 0.0001); const kk = 1 / dotB; @@ -96,24 +98,20 @@ export const sdBezier = tgpu.fn([vec2f, vec2f, vec2f, vec2f], f32)( if (h >= 0.0) { h = sqrt(h); - const x = vec2f(h, -h).sub(q).mul(0.5); - const uv = sign(x).mul(pow(abs(x), vec2f(1 / 3))); - const t = clamp(uv.x + uv.y - kx, 0, 1); - res = dot2(d.add(c.add(b.mul(t)).mul(t))); + const x = (vec2f(h, -h) - q) * 0.5; + const uv = sign(x) * pow(abs(x), vec2f(1 / 3)); + const t = saturate(uv.x + uv.y - kx); + res = dot2(d + (c + b * t) * t); } else { const z = sqrt(-p); const v = acos(q / (p * z * 2)) / 3; const m = cos(v); - const n = mul(sin(v), 1.732050808); // sqrt(3) - const t = saturate( - vec3f(m + m, -n - m, n - m) - .mul(z) - .sub(kx), - ); + const n = sin(v) * 1.732050808; // sqrt(3) + const t = saturate(vec3f(m + m, -n - m, n - m) * z - kx); res = min( - dot2(d.add(c.add(b.mul(t.x)).mul(t.x))), - dot2(d.add(c.add(b.mul(t.y)).mul(t.y))), + dot2(d + (c + b * t.x) * t.x), + dot2(d + (c + b * t.y) * t.y), ); } @@ -137,25 +135,26 @@ export const sdBezierApprox = tgpu.fn( [vec2f, vec2f, vec2f, vec2f], f32, )((point, A, B, C) => { - const i = A.sub(C); - const j = C.sub(B); - const k = B.sub(A); - const w = j.sub(k); + 'use gpu'; + const i = A - C; + const j = C - B; + const k = B - A; + const w = j - k; - const v0 = A.sub(point); - const v1 = B.sub(point); - const v2 = C.sub(point); + const v0 = A - point; + const v1 = B - point; + const v2 = C - point; const x = cross(v0, v2); const y = cross(v1, v0); const z = cross(v2, v1); - const s = j.mul(y).add(k.mul(z)).mul(2).sub(i.mul(x)); + const s = (j * y + k * z) * 2 - (i * x); const r = (y * z - x * x * 0.25) / dot2(s); const t = saturate((0.5 * x + y + r * dot(s, w)) / (x + y + z)); - const d = v0.add(k.add(k).add(w.mul(t)).mul(t)); + const d = v0 + (k + k + (w * t)) * t; return length(d); }); @@ -167,9 +166,10 @@ export const sdBezierApprox = tgpu.fn( * @param radius - The radius of the pie */ export const sdPie = tgpu.fn([vec2f, vec2f, f32], f32)((point, sc, radius) => { + 'use gpu'; const p_w = vec2f(point); p_w.x = abs(point.x); const l = length(p_w) - radius; - const m = length(p_w.sub(sc.mul(clamp(dot(p_w, sc), 0, radius)))); + const m = distance(p_w, sc * (clamp(dot(p_w, sc), 0, radius))); return max(l, m * sign(sc.y * p_w.x - sc.x * p_w.y)); }); diff --git a/packages/typegpu-sdf/src/3d.ts b/packages/typegpu-sdf/src/3d.ts index 4b07d9ddeb..b7ed2b1d5e 100644 --- a/packages/typegpu-sdf/src/3d.ts +++ b/packages/typegpu-sdf/src/3d.ts @@ -1,6 +1,16 @@ import tgpu from 'typegpu'; import { f32, vec3f } from 'typegpu/data'; -import { abs, add, dot, length, max, min, saturate, sub } from 'typegpu/std'; +import { + abs, + add, + distance, + dot, + length, + max, + min, + saturate, + sub, +} from 'typegpu/std'; /** * Signed distance function for a sphere @@ -66,10 +76,11 @@ export const sdBoxFrame3d = tgpu * @param B Second endpoint of the line */ export const sdLine3d = tgpu.fn([vec3f, vec3f, vec3f], f32)((point, A, B) => { - const pa = sub(point, A); - const ba = sub(B, A); + 'use gpu'; + const pa = point - A; + const ba = B - A; const h = max(0, min(1, dot(pa, ba) / dot(ba, ba))); - return length(sub(pa, ba.mul(h))); + return distance(pa, ba * h); }); /** @@ -93,8 +104,9 @@ export const sdPlane = tgpu.fn([vec3f, vec3f, f32], f32)( */ export const sdCapsule = tgpu .fn([vec3f, vec3f, vec3f, f32], f32)((point, A, B, radius) => { - const pa = sub(point, A); - const ba = sub(B, A); + 'use gpu'; + const pa = point - A; + const ba = B - A; const h = saturate(dot(pa, ba) / dot(ba, ba)); - return length(sub(pa, ba.mul(h))) - radius; + return distance(pa, ba * h) - radius; }); diff --git a/packages/typegpu/package.json b/packages/typegpu/package.json index e474d7a733..ae9961c2cd 100644 --- a/packages/typegpu/package.json +++ b/packages/typegpu/package.json @@ -79,6 +79,7 @@ "packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67", "dependencies": { "tinyest": "workspace:~0.2.0", + "tsover-runtime": "^0.0.4", "typed-binary": "^4.3.1" } } diff --git a/packages/typegpu/src/data/index.ts b/packages/typegpu/src/data/index.ts index b60dc9b0c2..34c1745df6 100644 --- a/packages/typegpu/src/data/index.ts +++ b/packages/typegpu/src/data/index.ts @@ -4,6 +4,7 @@ // NOTE: This is a barrel file, internal files should not import things from this file +import { Operator } from 'tsover-runtime'; import { type InfixOperator, infixOperators } from '../tgsl/accessProp.ts'; import { MatBase } from './matrix.ts'; import { VecBase } from './vectorImpl.ts'; @@ -11,29 +12,31 @@ import { VecBase } from './vectorImpl.ts'; function assignInfixOperator( object: T, operator: InfixOperator, + operatorSymbol: symbol, ) { - type Instance = InstanceType; - - const proto = object.prototype as { - [K in InfixOperator]?: (this: Instance, other: unknown) => unknown; - }; + // oxlint-disable-next-line typescript/no-explicit-any anything is possible + const proto = object.prototype as any; const opImpl = infixOperators[operator] as ( - lhs: Instance, + lhs: unknown, rhs: unknown, ) => unknown; - proto[operator] = function (this: Instance, other: unknown): unknown { + proto[operator] = function (this: unknown, other: unknown): unknown { return opImpl(this, other); }; + + proto[operatorSymbol] = (lhs: unknown, rhs: unknown): unknown => { + return opImpl(lhs, rhs); + }; } -assignInfixOperator(VecBase, 'add'); -assignInfixOperator(VecBase, 'sub'); -assignInfixOperator(VecBase, 'mul'); -assignInfixOperator(VecBase, 'div'); -assignInfixOperator(MatBase, 'add'); -assignInfixOperator(MatBase, 'sub'); -assignInfixOperator(MatBase, 'mul'); +assignInfixOperator(VecBase, 'add', Operator.plus); +assignInfixOperator(VecBase, 'sub', Operator.minus); +assignInfixOperator(VecBase, 'mul', Operator.star); +assignInfixOperator(VecBase, 'div', Operator.slash); +assignInfixOperator(MatBase, 'add', Operator.plus); +assignInfixOperator(MatBase, 'sub', Operator.minus); +assignInfixOperator(MatBase, 'mul', Operator.star); export { bool, f16, f32, i32, u16, u32 } from './numeric.ts'; export { @@ -72,6 +75,7 @@ export type { Mat2x2f, Mat3x3f, Mat4x4f, + matBase, Ptr, Size, StorableData, @@ -104,6 +108,7 @@ export type { Vec4h, Vec4i, Vec4u, + vecBase, WgslArray, WgslStruct, } from './wgslTypes.ts'; diff --git a/packages/typegpu/src/data/wgslTypes.ts b/packages/typegpu/src/data/wgslTypes.ts index 721a3aa308..d1c65a2d2c 100644 --- a/packages/typegpu/src/data/wgslTypes.ts +++ b/packages/typegpu/src/data/wgslTypes.ts @@ -1,3 +1,4 @@ +import type { Operator } from 'tsover-runtime'; import type { TgpuNamable } from '../shared/meta.ts'; import type { ExtractInvalidSchemaError, @@ -56,19 +57,19 @@ export interface NumberArrayView { * These functions are not defined on vectors, * but are instead assigned to `VecBase` after both `data` and `std` are initialized. */ -export interface vecInfixNotation { - add(other: number): T; - add(other: T): T; - - sub(other: number): T; - sub(other: T): T; - - mul(other: number): T; - mul(other: T): T; - mul(other: mBaseForVec): T; +export interface vecInfixNotation { + add(other: T | number): T; + sub(other: T | number): T; + mul(other: mBaseForVec | T | number): T; + div(other: T | number): T; - div(other: number): T; - div(other: T): T; + [Operator.plus](lhs: T | number, rhs: T | number): T; + [Operator.minus](lhs: T | number, rhs: T | number): T; + [Operator.star]( + lhs: mBaseForVec | T | number, + rhs: mBaseForVec | T | number, + ): T; + [Operator.slash](lhs: T | number, rhs: T | number): T; } /** @@ -78,14 +79,17 @@ export interface vecInfixNotation { * These functions are not defined on matrices, * but are instead assigned to `MatBase` after both `data` and `std` are initialized. */ -export interface matInfixNotation { +export interface matInfixNotation { add(other: T): T; - sub(other: T): T; - - mul(other: number): T; + mul(other: T | number): T; mul(other: vBaseForMat): vBaseForMat; - mul(other: T): T; + + [Operator.plus](lhs: T, rhs: T): T; + [Operator.minus](lhs: T, rhs: T): T; + [Operator.star](lhs: T | number, rhs: T | number): T; + [Operator.star](lhs: T, rhs: vBaseForMat): vBaseForMat; + [Operator.star](lhs: vBaseForMat, rhs: T): vBaseForMat; } /** @@ -177,6 +181,10 @@ type Tuple2 = [S, S]; type Tuple3 = [S, S, S]; type Tuple4 = [S, S, S, S]; +export interface vecBase extends vecInfixNotation { + readonly [$internal]: true; +} + /** * Interface representing its WGSL vector type counterpart: vec2f or vec2. * A vector with 2 elements of type f32 @@ -472,6 +480,10 @@ export type AnyVecInstance = export type VecKind = AnyVecInstance['kind']; +export interface matBase extends matInfixNotation { + readonly [$internal]: true; +} + /** * Interface representing its WGSL matrix type counterpart: mat2x2 * A matrix with 2 rows and 2 columns, with elements of type `TColumn` @@ -539,14 +551,15 @@ export interface m4x4f extends mat4x4, matInfixNotation { export type AnyMatInstance = m2x2f | m3x3f | m4x4f; -export type vBaseForMat = T extends m2x2f ? v2f +export type vBaseForMat = T extends m2x2f ? v2f : T extends m3x3f ? v3f - : v4f; + : T extends m4x4f ? v4f + : vecBase; -export type mBaseForVec = T extends v2f ? m2x2f +export type mBaseForVec = T extends v2f ? m2x2f : T extends v3f ? m3x3f : T extends v4f ? m4x4f - : never; + : matBase; // #endregion diff --git a/packages/typegpu/src/tgsl/wgslGenerator.ts b/packages/typegpu/src/tgsl/wgslGenerator.ts index ec8ea50a30..49833fbea4 100644 --- a/packages/typegpu/src/tgsl/wgslGenerator.ts +++ b/packages/typegpu/src/tgsl/wgslGenerator.ts @@ -405,7 +405,8 @@ ${this.ctx.pre}}`; ); } - if (!isEphemeralSnippet(rhsExpr)) { + // Compound assignment operators are okay, e.g. +=, -=, *=, /=, ... + if (op === '=' && !isEphemeralSnippet(rhsExpr)) { throw new WgslTypeError( `'${lhsStr} = ${rhsStr}' is invalid, because references cannot be assigned.\n-----\nTry '${lhsStr} = ${ this.ctx.resolve(rhsExpr.dataType).value diff --git a/packages/typegpu/tests/examples/individual/3d-fish.test.ts b/packages/typegpu/tests/examples/individual/3d-fish.test.ts index 4c75b546b6..36e7c6060c 100644 --- a/packages/typegpu/tests/examples/individual/3d-fish.test.ts +++ b/packages/typegpu/tests/examples/individual/3d-fish.test.ts @@ -141,9 +141,9 @@ describe('3d fish example', () => { continue; } let other = (¤tFishData[i]); - let dist = length(((*fishData).position - (*other).position)); + let dist = distance((*fishData).position, (*other).position); if ((dist < fishBehavior.separationDist)) { - separation = (separation + ((*fishData).position - (*other).position)); + separation += ((*fishData).position - (*other).position); } if ((dist < fishBehavior.alignmentDist)) { alignment = (alignment + (*other).direction); @@ -155,10 +155,10 @@ describe('3d fish example', () => { } } if ((alignmentCount > 0i)) { - alignment = (alignment * (1f / f32(alignmentCount))); + alignment = (alignment / f32(alignmentCount)); } if ((cohesionCount > 0i)) { - cohesion = (((1f / f32(cohesionCount)) * cohesion) - (*fishData).position); + cohesion = ((cohesion / f32(cohesionCount)) - (*fishData).position); } for (var i = 0; (i < 3i); i += 1i) { var repulsion = vec3f(); @@ -181,11 +181,11 @@ describe('3d fish example', () => { let str = (pow(2f, clamp((limit - length(diff)), 0f, limit)) - 1f); rayRepulsion = (normalize(diff) * str); var direction = (*fishData).direction; - direction = (direction + (separation * fishBehavior.separationStr)); - direction = (direction + (alignment * fishBehavior.alignmentStr)); - direction = (direction + (cohesion * fishBehavior.cohesionStr)); - direction = (direction + (wallRepulsion * 1e-4f)); - direction = (direction + (rayRepulsion * 0.0015f)); + direction += (separation * fishBehavior.separationStr); + direction += (alignment * fishBehavior.alignmentStr); + direction += (cohesion * fishBehavior.cohesionStr); + direction += (wallRepulsion * 1e-4f); + direction += (rayRepulsion * 0.0015f); direction = (normalize(direction) * clamp(length((*fishData).direction), 0f, 0.01f)); var translation = (direction * (min(999f, timePassed) / 8f)); let nextFishData_1 = (&nextFishData[fishIndex]); @@ -277,10 +277,10 @@ describe('3d fish example', () => { var pitchMatrix = mat4x4f(cos(pitch), sin(pitch), 0, 0, -sin(pitch), cos(pitch), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); var yawMatrix = mat4x4f(cos(yaw), 0, -sin(yaw), 0, 0, 1, 0, 0, sin(yaw), 0, cos(yaw), 0, 0, 0, 0, 1); var translationMatrix = mat4x4f(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, (*currentModelData).position.x, (*currentModelData).position.y, (*currentModelData).position.z, 1); - var worldPosition = (translationMatrix * (yawMatrix * (pitchMatrix * (scaleMatrix * vec4f(wavedVertex.position, 1f))))); - var worldNormal = normalize((yawMatrix * (pitchMatrix * vec4f(wavedVertex.normal, 1f))).xyz); + var worldPosition = ((((translationMatrix * yawMatrix) * pitchMatrix) * scaleMatrix) * vec4f(wavedVertex.position, 1f)); + var worldNormal = normalize(((yawMatrix * pitchMatrix) * vec4f(wavedVertex.normal, 1f)).xyz); let worldPositionUniform = (&worldPosition); - var canvasPosition = (camera.projection * (camera.view * (*worldPositionUniform))); + var canvasPosition = ((camera.projection * camera.view) * (*worldPositionUniform)); return vertexShader_Output(worldPosition.xyz, worldNormal, canvasPosition, (*currentModelData).variant, input.textureUV, (*currentModelData).applySeaFog, (*currentModelData).applySeaDesaturation); } @@ -401,14 +401,14 @@ describe('3d fish example', () => { @fragment fn fragmentShader(input: fragmentShader_Input) -> @location(0) vec4f { var textureColorWithAlpha = textureSample(modelTexture, sampler_1, input.textureUV); var textureColor = textureColorWithAlpha.rgb; - var ambient = (0.5f * (textureColor * vec3f(0.800000011920929, 0.800000011920929, 1))); + var ambient = ((0.5f * textureColor) * vec3f(0.800000011920929, 0.800000011920929, 1)); let cosTheta = dot(input.worldNormal, vec3f(-0.2357022613286972, 0.9428090453147888, -0.2357022613286972)); - var diffuse = (max(0f, cosTheta) * (textureColor * vec3f(0.800000011920929, 0.800000011920929, 1))); + var diffuse = ((max(0f, cosTheta) * textureColor) * vec3f(0.800000011920929, 0.800000011920929, 1)); var viewSource = normalize((camera.position.xyz - input.worldPosition)); var reflectSource = normalize(reflect(vec3f(0.2357022613286972, -0.9428090453147888, 0.2357022613286972), input.worldNormal)); let specularStrength = pow(max(0f, dot(viewSource, reflectSource)), 16f); var specular = (specularStrength * vec3f(0.800000011920929, 0.800000011920929, 1)); - var lightedColor = (ambient + (diffuse + specular)); + var lightedColor = ((ambient + diffuse) + specular); let distanceFromCamera = length((camera.position.xyz - input.worldPosition)); var desaturatedColor = lightedColor; if ((input.applySeaDesaturation == 1u)) { diff --git a/packages/typegpu/tests/examples/individual/ascii-filter.test.ts b/packages/typegpu/tests/examples/individual/ascii-filter.test.ts index 204b3082aa..445d771b97 100644 --- a/packages/typegpu/tests/examples/individual/ascii-filter.test.ts +++ b/packages/typegpu/tests/examples/individual/ascii-filter.test.ts @@ -226,7 +226,7 @@ describe('ascii filter example', () => { let charValue = characterFn(n, p); var resultColor = vec3f(1); if ((displayMode == 0u)) { - resultColor = (color * charValue).rgb; + resultColor = (color.rgb * charValue); } if ((displayMode == 1u)) { resultColor = vec3f((gray * charValue)); diff --git a/packages/typegpu/tests/examples/individual/boids.test.ts b/packages/typegpu/tests/examples/individual/boids.test.ts index 86b054217d..8c6725fb65 100644 --- a/packages/typegpu/tests/examples/individual/boids.test.ts +++ b/packages/typegpu/tests/examples/individual/boids.test.ts @@ -40,7 +40,7 @@ describe('boids example', () => { @group(1) @binding(1) var nextTrianglePos: array; fn simulate(index: u32, _arg_1: u32, _arg_2: u32) { - var instanceInfo = currentTrianglePos[index]; + var self_1 = currentTrianglePos[index]; var separation = vec2f(); var alignment = vec2f(); var cohesion = vec2f(); @@ -49,46 +49,34 @@ describe('boids example', () => { for (var i = 0u; i < arrayLength((¤tTrianglePos)); i++) { let other = (¤tTrianglePos[i]); { - let dist = distance(instanceInfo.position, (*other).position); + let dist = distance(self_1.position, (*other).position); if ((dist < paramsBuffer.separationDistance)) { - separation = (separation + (instanceInfo.position - (*other).position)); + separation += (self_1.position - (*other).position); } if ((dist < paramsBuffer.alignmentDistance)) { - alignment = (alignment + (*other).velocity); + alignment += (*other).velocity; alignmentCount++; } if ((dist < paramsBuffer.cohesionDistance)) { - cohesion = (cohesion + (*other).position); + cohesion += (*other).position; cohesionCount++; } } } if ((alignmentCount > 0i)) { - alignment = ((1f / f32(alignmentCount)) * alignment); + alignment /= f32(alignmentCount); } if ((cohesionCount > 0i)) { - cohesion = ((1f / f32(cohesionCount)) * cohesion); - cohesion = (cohesion - instanceInfo.position); + cohesion /= f32(cohesionCount); + cohesion -= self_1.position; } - var velocity = (paramsBuffer.separationStrength * separation); - velocity = (velocity + (paramsBuffer.alignmentStrength * alignment)); - velocity = (velocity + (paramsBuffer.cohesionStrength * cohesion)); - instanceInfo.velocity = (instanceInfo.velocity + velocity); - instanceInfo.velocity = (clamp(length(instanceInfo.velocity), 0f, 0.01f) * normalize(instanceInfo.velocity)); - if ((instanceInfo.position.x > 1.03f)) { - instanceInfo.position.x = -1.03f; - } - if ((instanceInfo.position.y > 1.03f)) { - instanceInfo.position.y = -1.03f; - } - if ((instanceInfo.position.x < -1.03f)) { - instanceInfo.position.x = 1.03f; - } - if ((instanceInfo.position.y < -1.03f)) { - instanceInfo.position.y = 1.03f; - } - instanceInfo.position = (instanceInfo.position + instanceInfo.velocity); - nextTrianglePos[index] = instanceInfo; + var velocity = (((paramsBuffer.separationStrength * separation) + (paramsBuffer.alignmentStrength * alignment)) + (paramsBuffer.cohesionStrength * cohesion)); + self_1.velocity += velocity; + self_1.velocity = (clamp(length(self_1.velocity), 0f, 0.01f) * normalize(self_1.velocity)); + self_1.position += self_1.velocity; + const domain = 2.06; + self_1.position = ((fract(((self_1.position / domain) + 0.5f)) - 0.5f) * domain); + nextTrianglePos[index] = self_1; } struct mainCompute_Input { diff --git a/packages/typegpu/tests/examples/individual/caustics.test.ts b/packages/typegpu/tests/examples/individual/caustics.test.ts index cac8c27b83..e9a3f328b6 100644 --- a/packages/typegpu/tests/examples/individual/caustics.test.ts +++ b/packages/typegpu/tests/examples/individual/caustics.test.ts @@ -82,7 +82,7 @@ describe('caustics example', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { @@ -134,9 +134,9 @@ describe('caustics example', () => { var noFogColor = (albedo * mix(vec3f(0.20000000298023224, 0.5, 1), (c1 + c2), blend)); let fog = min((pow(_arg_0.uv.y, 0.5f) * 1.2f), 1f); var godRayUv = ((rotateXY(-0.3f) * _arg_0.uv) * vec2f(15, 3)); - let godRayFactor = pow(_arg_0.uv.y, 1f); - var godRay1 = ((sample(vec3f(godRayUv, (time * 0.5f))) + 1f) * (vec3f(0.18000000715255737, 0.30000001192092896, 0.5) * godRayFactor)); - var godRay2 = ((sample(vec3f((godRayUv * 2f), (time * 0.3f))) + 1f) * (vec3f(0.18000000715255737, 0.30000001192092896, 0.5) * (godRayFactor * 0.4f))); + let godRayFactor = _arg_0.uv.y; + var godRay1 = (((sample(vec3f(godRayUv, (time * 0.5f))) + 1f) * vec3f(0.18000000715255737, 0.30000001192092896, 0.5)) * godRayFactor); + var godRay2 = ((((sample(vec3f((godRayUv * 2f), (time * 0.3f))) + 1f) * vec3f(0.18000000715255737, 0.30000001192092896, 0.5)) * godRayFactor) * 0.4f); var godRays = (godRay1 + godRay2); return vec4f((mix(noFogColor, vec3f(0.05000000074505806, 0.20000000298023224, 0.699999988079071), fog) + godRays), 1f); }" diff --git a/packages/typegpu/tests/examples/individual/clouds.test.ts b/packages/typegpu/tests/examples/individual/clouds.test.ts index df70cf0435..ad184b45b8 100644 --- a/packages/typegpu/tests/examples/individual/clouds.test.ts +++ b/packages/typegpu/tests/examples/individual/clouds.test.ts @@ -101,7 +101,7 @@ describe('clouds example', () => { fn sampleDensityCheap(pos: vec3f) -> f32 { let noise = (noise3d((pos * 1.4f)) * 1f); - return clamp(((noise + 0.7f) - 0.5f), 0f, 1f); + return saturate(((noise + 0.7f) - 0.5f)); } fn raymarch(rayOrigin: vec3f, rayDir: vec3f, sunDir: vec3f) -> vec4f { @@ -112,18 +112,18 @@ describe('clouds example', () => { let stepSize = (1f / f32(maxSteps)); var dist = (randFloat01() * stepSize); for (var i = 0; (i < maxSteps); i++) { - var samplePos = (rayOrigin + (rayDir * (dist * maxDepth))); + var samplePos = (rayOrigin + ((rayDir * dist) * maxDepth)); let cloudDensity = sampleDensity(samplePos); if ((cloudDensity > 0f)) { var shadowPos = (samplePos + sunDir); let shadowDensity = sampleDensityCheap(shadowPos); - let shadow = clamp((cloudDensity - shadowDensity), 0f, 1f); + let shadow = saturate((cloudDensity - shadowDensity)); let lightVal = mix(0.3f, 1f, shadow); - var light = (vec3f(0.6600000262260437, 0.4949999749660492, 0.824999988079071) + (vec3f(1, 0.699999988079071, 0.30000001192092896) * (lightVal * 0.9f))); + var light = (vec3f(0.6600000262260437, 0.4949999749660492, 0.824999988079071) + ((vec3f(1, 0.699999988079071, 0.30000001192092896) * lightVal) * 0.9f)); var color = mix(vec3f(1), vec3f(0.20000000298023224), cloudDensity); var lit = (color * light); - var contrib = (vec4f(lit, 1f) * (cloudDensity * (0.88f - accum.a))); - accum = (accum + contrib); + var contrib = ((vec4f(lit, 1f) * cloudDensity) * (0.88f - accum.a)); + accum += contrib; if ((accum.a >= 0.879f)) { break; } @@ -133,11 +133,11 @@ describe('clouds example', () => { return accum; } - struct mainFragment_Input { + struct FragmentIn { @location(0) uv: vec2f, } - @fragment fn mainFragment(_arg_0: mainFragment_Input) -> @location(0) vec4f { + @fragment fn fragment(_arg_0: FragmentIn) -> @location(0) vec4f { randSeed2((_arg_0.uv * params.time)); let screenRes = (&resolutionUniform); let aspect = ((*screenRes).x / (*screenRes).y); @@ -147,10 +147,10 @@ describe('clouds example', () => { let time = params.time; var rayOrigin = vec3f((sin((time * 0.6f)) * 0.5f), ((cos((time * 0.8f)) * 0.5f) - 1f), (time * 1f)); var rayDir = normalize(vec3f(screenPos.x, screenPos.y, 1f)); - let sunDot = clamp(dot(rayDir, sunDir), 0f, 1f); + let sunDot = saturate(dot(rayDir, sunDir)); let sunGlow = pow(sunDot, 1.371742112482853f); - var skyCol = (vec3f(0.75, 0.6600000262260437, 0.8999999761581421) - (vec3f(1, 0.699999988079071, 0.4300000071525574) * (rayDir.y * 0.35f))); - skyCol = (skyCol + (vec3f(1, 0.3700000047683716, 0.17000000178813934) * sunGlow)); + var skyCol = (vec3f(0.75, 0.6600000262260437, 0.8999999761581421) - ((vec3f(1, 0.699999988079071, 0.4300000071525574) * rayDir.y) * 0.35f)); + skyCol += (vec3f(1, 0.3700000047683716, 0.17000000178813934) * sunGlow); var cloudCol = raymarch(rayOrigin, rayDir, sunDir); var finalCol = ((skyCol * (1.1f - cloudCol.a)) + cloudCol.rgb); return vec4f(finalCol, 1f); diff --git a/packages/typegpu/tests/examples/individual/gravity.test.ts b/packages/typegpu/tests/examples/individual/gravity.test.ts index 0d57799581..bfe238a0db 100644 --- a/packages/typegpu/tests/examples/individual/gravity.test.ts +++ b/packages/typegpu/tests/examples/individual/gravity.test.ts @@ -169,9 +169,9 @@ describe('gravity example', () => { let dist = max((radiusOf(current) + radiusOf((*other))), distance(current.position, (*other).position)); let gravityForce = (((current.mass * (*other).mass) / dist) / dist); var direction = normalize(((*other).position - current.position)); - current.velocity = (current.velocity + (direction * ((gravityForce / current.mass) * dt))); + current.velocity += ((direction * (gravityForce / current.mass)) * dt); } - current.position = (current.position + (current.velocity * dt)); + current.position += (current.velocity * dt); } outState[currentId] = current; } @@ -294,8 +294,7 @@ describe('gravity example', () => { var lightDirection = normalize((lightSource - input.worldPosition)); let cosTheta = dot(normal, lightDirection); var diffuse = ((textureColor * lightColor) * max(0f, cosTheta)); - var litColor = (ambient + diffuse); - return vec4f(litColor, 1f); + return vec4f((ambient + diffuse), 1f); }" `); }); diff --git a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts index 544d5b5ae7..725544252f 100644 --- a/packages/typegpu/tests/examples/individual/jelly-slider.test.ts +++ b/packages/typegpu/tests/examples/individual/jelly-slider.test.ts @@ -383,7 +383,7 @@ describe('jelly-slider example', () => { var p_w = point; p_w.x = abs(point.x); let l = (length(p_w) - radius); - let m = length((p_w - (sc * clamp(dot(p_w, sc), 0f, radius)))); + let m = distance(p_w, (sc * clamp(dot(p_w, sc), 0f, radius))); return max(l, (m * sign(((sc.y * p_w.x) - (sc.x * p_w.y))))); } @@ -693,7 +693,7 @@ describe('jelly-slider example', () => { h = sqrt(h); var x = ((vec2f(h, -(h)) - q) * 0.5f); var uv = (sign(x) * pow(abs(x), vec2f(0.3333333432674408))); - let t = clamp(((uv.x + uv.y) - kx), 0f, 1f); + let t = saturate(((uv.x + uv.y) - kx)); res = dot2((d + ((c + (b * t)) * t))); } else { diff --git a/packages/typegpu/tests/examples/individual/perlin-noise.test.ts b/packages/typegpu/tests/examples/individual/perlin-noise.test.ts index cc90153c0c..7ca44875bf 100644 --- a/packages/typegpu/tests/examples/individual/perlin-noise.test.ts +++ b/packages/typegpu/tests/examples/individual/perlin-noise.test.ts @@ -111,7 +111,7 @@ describe('perlin noise example', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { diff --git a/packages/typegpu/tests/examples/individual/ripple-cube.test.ts b/packages/typegpu/tests/examples/individual/ripple-cube.test.ts index dbe13f72fd..b6bfd252c5 100644 --- a/packages/typegpu/tests/examples/individual/ripple-cube.test.ts +++ b/packages/typegpu/tests/examples/individual/ripple-cube.test.ts @@ -150,7 +150,7 @@ describe('ripple-cube example', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { @@ -450,7 +450,7 @@ describe('ripple-cube example', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { diff --git a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts index 0f1b46e97e..9ccdd15d4e 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold-3d.test.ts @@ -190,7 +190,7 @@ describe('slime mold 3d example', () => { var coneOffset = ((perp1 * cos(theta)) + (perp2 * sin(theta))); var sensorDir = normalize((direction + (coneOffset * sin(params.sensorAngle)))); var sensorPos = (pos + (sensorDir * params.sensorDistance)); - var sensorPosInt = vec3u(clamp(sensorPos, vec3f(), (dimsf - vec3f(1)))); + var sensorPosInt = vec3u(clamp(sensorPos, vec3f(), (dimsf - 1f))); let weight = textureLoad(oldState, sensorPosInt).x; weightedDir = (weightedDir + (sensorDir * weight)); totalWeight = (totalWeight + weight); @@ -263,8 +263,8 @@ describe('slime mold 3d example', () => { var direction = normalize((*agent).direction); var senseResult = sense3D((*agent).position, direction); var targetDirection = select(randOnUnitHemisphere(direction), normalize(senseResult.weightedDir), (senseResult.totalWeight > 0.01f)); - direction = normalize((direction + (targetDirection * (params.turnSpeed * params.deltaTime)))); - var newPos = ((*agent).position + (direction * (params.moveSpeed * params.deltaTime))); + direction = normalize((direction + ((targetDirection * params.turnSpeed) * params.deltaTime))); + var newPos = ((*agent).position + ((direction * params.moveSpeed) * params.deltaTime)); var center = (dimsf / 2f); if (((newPos.x < 0f) || (newPos.x >= dimsf.x))) { newPos.x = clamp(newPos.x, 0f, (dimsf.x - 1f)); @@ -343,7 +343,7 @@ describe('slime mold 3d example', () => { } fn rayBoxIntersection(rayOrigin: vec3f, rayDir: vec3f, boxMin: vec3f, boxMax: vec3f) -> RayBoxResult { - var invDir = (vec3f(1) / rayDir); + var invDir = (1f / rayDir); var t0 = ((boxMin - rayOrigin) * invDir); var t1 = ((boxMax - rayOrigin) * invDir); var tmin = min(t0, t1); @@ -418,7 +418,7 @@ describe('slime mold 3d example', () => { let density = pow(d0, gamma); let alphaSrc = (1f - exp(((-(sigmaT) * density) * stepSize))); var contrib = (albedo * alphaSrc); - accum = (accum + (contrib * transmittance)); + accum += (contrib * transmittance); transmittance = (transmittance * (1f - alphaSrc)); i += 1i; } diff --git a/packages/typegpu/tests/examples/individual/slime-mold.test.ts b/packages/typegpu/tests/examples/individual/slime-mold.test.ts index 9cdf638876..4a20a0cd7b 100644 --- a/packages/typegpu/tests/examples/individual/slime-mold.test.ts +++ b/packages/typegpu/tests/examples/individual/slime-mold.test.ts @@ -99,13 +99,13 @@ describe('slime mold example', () => { var dimsi = vec2i(dims); if (((((samplePos.x >= 0i) && (samplePos.x < dimsi.x)) && (samplePos.y >= 0i)) && (samplePos.y < dimsi.y))) { var color = textureLoad(oldState, vec2u(samplePos)).rgb; - sum = (sum + color); - count = (count + 1f); + sum += color; + count += 1f; } } } var blurred = (sum / count); - var newColor = clamp((blurred - params.evaporationRate), vec3f(), vec3f(1)); + var newColor = saturate((blurred - params.evaporationRate)); textureStore(newState, _arg_0.gid.xy, vec4f(newColor, 1f)); } @@ -156,7 +156,7 @@ describe('slime mold example', () => { var sensorPos = (pos + (sensorDir * params.sensorDistance)); var dims = textureDimensions(oldState); var dimsf = vec2f(dims); - var sensorPosInt = vec2u(clamp(sensorPos, vec2f(), (dimsf - vec2f(1)))); + var sensorPosInt = vec2u(clamp(sensorPos, vec2f(), (dimsf - 1f))); var color = textureLoad(oldState, sensorPosInt).rgb; return ((color.x + color.y) + color.z); } @@ -200,10 +200,10 @@ describe('slime mold example', () => { } } var dir = vec2f(cos(angle), sin(angle)); - var newPos = ((*agent).position + (dir * (params.moveSpeed * deltaTime))); + var newPos = ((*agent).position + ((dir * params.moveSpeed) * deltaTime)); var dimsf = vec2f(dims); if (((((newPos.x < 0f) || (newPos.x > dimsf.x)) || (newPos.y < 0f)) || (newPos.y > dimsf.y))) { - newPos = clamp(newPos, vec2f(), (dimsf - vec2f(1))); + newPos = clamp(newPos, vec2f(), (dimsf - 1f)); if (((newPos.x <= 0f) || (newPos.x >= (dimsf.x - 1f)))) { angle = (3.141592653589793f - angle); } @@ -214,7 +214,7 @@ describe('slime mold example', () => { } agentsData[_arg_0.gid.x] = Agent(newPos, angle); var oldState_1 = textureLoad(oldState, vec2u(newPos)).rgb; - var newState = (oldState_1 + vec3f(1)); + var newState = (oldState_1 + 1f); textureStore(newState_1, vec2u(newPos), vec4f(newState, 1f)); } diff --git a/packages/typegpu/tests/examples/individual/smoky-triangle.test.ts b/packages/typegpu/tests/examples/individual/smoky-triangle.test.ts index c5251d3c79..e1e77f7aca 100644 --- a/packages/typegpu/tests/examples/individual/smoky-triangle.test.ts +++ b/packages/typegpu/tests/examples/individual/smoky-triangle.test.ts @@ -116,7 +116,7 @@ describe('smoky triangle', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { diff --git a/packages/typegpu/tests/examples/individual/stable-fluid.test.ts b/packages/typegpu/tests/examples/individual/stable-fluid.test.ts index 040a569566..275555054d 100644 --- a/packages/typegpu/tests/examples/individual/stable-fluid.test.ts +++ b/packages/typegpu/tests/examples/individual/stable-fluid.test.ts @@ -94,7 +94,7 @@ describe('stable-fluid example', () => { let viscosity = simParams.viscosity; let diffuseRate = (viscosity * timeStep); let blendFactor = (1f / (4f + diffuseRate)); - var diffusedVal = (vec4f(blendFactor) * (((leftVal + rightVal) + (upVal + downVal)) + (diffuseRate * centerVal))); + var diffusedVal = (vec4f(blendFactor) * ((((leftVal + rightVal) + upVal) + downVal) + (centerVal * diffuseRate))); textureStore(out, pixelPos, diffusedVal); } @@ -215,7 +215,7 @@ describe('stable-fluid example', () => { let timeStep = simParams.dt; var prevPos = (vec2f(pixelPos) - (timeStep * velocity)); var clampedPos = clamp(prevPos, vec2f(-0.5), (vec2f(texSize.xy) - vec2f(0.5))); - var normalizedPos = ((clampedPos + vec2f(0.5)) / vec2f(texSize.xy)); + var normalizedPos = ((clampedPos + 0.5f) / vec2f(texSize.xy)); var inkVal = textureSampleLevel(src, linSampler, normalizedPos, 0); textureStore(dst, pixelPos, inkVal); } diff --git a/packages/typegpu/tests/examples/individual/vaporrave.test.ts b/packages/typegpu/tests/examples/individual/vaporrave.test.ts index c39d076d81..467d77903b 100644 --- a/packages/typegpu/tests/examples/individual/vaporrave.test.ts +++ b/packages/typegpu/tests/examples/individual/vaporrave.test.ts @@ -138,7 +138,7 @@ describe('vaporrave example', () => { } fn quinticInterpolationImpl(t: vec3f) -> vec3f { - return ((t * (t * t)) * ((t * ((t * 6f) - 15f)) + 10f)); + return (((t * t) * t) * ((t * ((t * 6f) - 15f)) + 10f)); } fn sample(pos: vec3f) -> f32 { @@ -203,7 +203,7 @@ describe('vaporrave example', () => { var p = ((rd * distOrigin) + ro); var scene = getSceneRay(p); var sphereDist = getSphere(p, sphereColorUniform, vec3f(0, 6, 12), sphereAngleUniform); - glow = ((sphereColorUniform * exp(-(sphereDist.dist))) + glow); + glow += (sphereColorUniform * exp(-(sphereDist.dist))); distOrigin += scene.dist; if ((distOrigin > 19f)) { result.dist = 19f; diff --git a/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts b/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts index e83c208e69..feac263073 100644 --- a/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts +++ b/packages/typegpu/tests/examples/individual/xor-dev-centrifuge-2.test.ts @@ -31,21 +31,18 @@ describe('xor dev centrifuge example', () => { return vertexMain_Output(vec4f(pos[input.vertexIndex], 0f, 1f), pos[input.vertexIndex]); } - @group(0) @binding(0) var aspectRatio: f32; - - @group(0) @binding(1) var tunnelDepth: i32; - - @group(0) @binding(2) var cameraPos: vec2f; - - @group(0) @binding(3) var bigStrips: f32; - - @group(0) @binding(4) var time: f32; - - @group(0) @binding(5) var dollyZoom: f32; - - @group(0) @binding(6) var smallStrips: f32; + struct Params { + time: f32, + aspectRatio: f32, + cameraPos: vec2f, + tunnelDepth: i32, + bigStrips: f32, + smallStrips: f32, + dollyZoom: f32, + color: vec3f, + } - @group(0) @binding(7) var color: vec3f; + @group(0) @binding(0) var paramsUniform: Params; fn safeTanh(v: vec3f) -> vec3f { return select(tanh(v), sign(v), (abs(v) > vec3f(10))); @@ -56,18 +53,19 @@ describe('xor dev centrifuge example', () => { } @fragment fn fragmentMain(_arg_0: fragmentMain_Input) -> @location(0) vec4f { - var ratio = vec2f(aspectRatio, 1f); + let params = (¶msUniform); + var ratio = vec2f((*params).aspectRatio, 1f); var dir = normalize(vec3f((_arg_0.uv * ratio), -1f)); var z = 0f; var acc = vec3f(); - for (var i = 0; (i < tunnelDepth); i++) { + for (var i = 0; (i < (*params).tunnelDepth); i++) { var p = (dir * z); - p.x += cameraPos.x; - p.y += cameraPos.y; - var coords = vec3f(((atan2(p.y, p.x) * bigStrips) + time), ((p.z * dollyZoom) - (5f * time)), (length(p.xy) - 11f)); - var coords2 = (cos((coords + cos((coords * smallStrips)))) - 1f); + p.x += (*params).cameraPos.x; + p.y += (*params).cameraPos.y; + var coords = vec3f(((atan2(p.y, p.x) * (*params).bigStrips) + (*params).time), ((p.z * (*params).dollyZoom) - (5f * (*params).time)), (length(p.xy) - 11f)); + var coords2 = (cos((coords + cos((coords * (*params).smallStrips)))) - 1f); let dd = ((length(vec4f(coords.z, coords2)) * 0.5f) - 0.1f); - acc = (acc + ((1.2f - cos((color * p.z))) / dd)); + acc += ((1.2f - cos(((*params).color * p.z))) / dd); z += dd; } acc = safeTanh((acc * 5e-3f)); diff --git a/packages/typegpu/tests/tgsl/operatorOverloads.test.ts b/packages/typegpu/tests/tgsl/operatorOverloads.test.ts new file mode 100644 index 0000000000..af7666388a --- /dev/null +++ b/packages/typegpu/tests/tgsl/operatorOverloads.test.ts @@ -0,0 +1,89 @@ +import { expect } from 'vitest'; +import { d, tgpu } from '../../src/index.ts'; +import { test } from '../utils/extendedIt.ts'; + +test('vec3f() +', () => { + const main = () => { + 'use gpu'; + let result = d.vec3f(); + result += d.vec3f(1, 2, 3) + 1 /* comptime */; + result += 1; + return result; + }; + + expect(tgpu.resolve([main])).toMatchInlineSnapshot(` + "fn main() -> vec3f { + var result = vec3f(); + result += vec3f(2, 3, 4); + result += 1; + return result; + }" + `); + + expect(String(main())).toMatchInlineSnapshot(`"vec3f(3, 4, 5)"`); +}); + +test('vec3f() -', () => { + const main = () => { + 'use gpu'; + let result = d.vec3f(); + result -= d.vec3f(1, 2, 3) + 1 /* comptime */; + result -= 1; + return result; + }; + + expect(tgpu.resolve([main])).toMatchInlineSnapshot(` + "fn main() -> vec3f { + var result = vec3f(); + result -= vec3f(2, 3, 4); + result -= 1; + return result; + }" + `); + + expect(String(main())).toMatchInlineSnapshot(`"vec3f(-3, -4, -5)"`); +}); + +test('vec3f() *', () => { + const main = () => { + 'use gpu'; + let result = d.vec3f(1, 2, 3); + result *= d.vec3f(5, 4, 3); + result *= 2; + return result; + }; + + expect(tgpu.resolve([main])).toMatchInlineSnapshot(` + "fn main() -> vec3f { + var result = vec3f(1, 2, 3); + result *= vec3f(5, 4, 3); + result *= 2; + return result; + }" + `); + + expect(String(main())).toMatchInlineSnapshot(`"vec3f(10, 16, 18)"`); +}); + +test('vec3f() /', () => { + const main = () => { + 'use gpu'; + let result = d.vec3f(1, 2, 3); + result /= d.vec3f(2, 2, 3); + result /= 2; + return result; + }; + + expect(tgpu.resolve([main])).toMatchInlineSnapshot(` + "fn main() -> vec3f { + var result = vec3f(1, 2, 3); + result /= vec3f(2, 2, 3); + result /= 2; + return result; + }" + `); + + expect(String(main())).toMatchInlineSnapshot( + `"vec3f(0.25, 0.5, 0.5)"`, + ); +}); diff --git a/packages/unplugin-typegpu/src/babel.ts b/packages/unplugin-typegpu/src/babel.ts index f4dc5e7558..44c7968729 100644 --- a/packages/unplugin-typegpu/src/babel.ts +++ b/packages/unplugin-typegpu/src/babel.ts @@ -12,6 +12,7 @@ import { gatherTgpuAliases, getFunctionName, isShellImplementationCall, + operators, type Options, performExpressionNaming, useGpuDirective, @@ -43,11 +44,22 @@ function i(identifier: string): babel.Identifier { return types.identifier(identifier); } +const fnNodeToOriginalMap = new WeakMap< + | babel.FunctionDeclaration + | babel.FunctionExpression + | babel.ArrowFunctionExpression, + | babel.FunctionDeclaration + | babel.FunctionExpression + | babel.ArrowFunctionExpression +>(); + function functionToTranspiled( node: babel.ArrowFunctionExpression | babel.FunctionExpression, parent: babel.Node | null, ): babel.CallExpression { - const { params, body, externalNames } = transpileFn(node); + const { params, body, externalNames } = transpileFn( + fnNodeToOriginalMap.get(node) ?? node, + ); const maybeName = getFunctionName(node, parent); const metadata = types.objectExpression([ @@ -128,6 +140,8 @@ function wrapInAutoName( } function functionVisitor(ctx: Context): TraverseOptions { + let useGpuDepth = 0; + return { VariableDeclarator(path) { performExpressionNaming(ctx, path.node, (node, name) => { @@ -136,6 +150,24 @@ function functionVisitor(ctx: Context): TraverseOptions { }, AssignmentExpression(path) { + if (useGpuDepth > 0) { + const runtimeFn = + operators[path.node.operator as keyof typeof operators]; + + if (runtimeFn) { + path.replaceWith( + types.assignmentExpression( + '=', + path.node.left, + types.callExpression(types.identifier(runtimeFn), [ + path.node.left as babel.Expression, + path.node.right as babel.Expression, + ]), + ), + ); + } + } + performExpressionNaming(ctx, path.node, (node, name) => { path.get('right').replaceWith(wrapInAutoName(node, name)); }); @@ -157,71 +189,126 @@ function functionVisitor(ctx: Context): TraverseOptions { gatherTgpuAliases(path.node, ctx); }, - ArrowFunctionExpression(path) { - const node = path.node; - const parent = path.parentPath.node; - if (containsUseGpuDirective(node)) { - path.replaceWith(functionToTranspiled(node, parent)); - path.skip(); - } + BinaryExpression: { + exit(path) { + if (useGpuDepth <= 0) { + return; + } + + const runtimeFn = + operators[path.node.operator as keyof typeof operators]; + + if (runtimeFn) { + path.replaceWith( + types.callExpression(types.identifier(runtimeFn), [ + path.node.left as babel.Expression, + path.node.right as babel.Expression, + ]), + ); + } + }, }, - FunctionExpression(path) { - const node = path.node; - const parent = path.parentPath.node; - if (containsUseGpuDirective(node)) { - path.replaceWith(functionToTranspiled(node, parent)); - path.skip(); - } + ArrowFunctionExpression: { + enter(path) { + if (containsUseGpuDirective(path.node)) { + fnNodeToOriginalMap.set(path.node, types.cloneNode(path.node, true)); + useGpuDepth++; + } + }, + exit(path) { + const node = path.node; + if (containsUseGpuDirective(node)) { + useGpuDepth--; + const parent = path.parentPath.node; + path.replaceWith(functionToTranspiled(node, parent)); + path.skip(); + } + }, }, - FunctionDeclaration(path) { - const node = path.node; - const parent = path.parentPath.node; - const expression = types.functionExpression( - node.id, - node.params, - node.body, - ); - - if (containsUseGpuDirective(path.node) && node.id) { - path.replaceWith( - types.variableDeclaration('const', [ - types.variableDeclarator( - node.id, - functionToTranspiled(expression, parent), - ), - ]), - ); - path.skip(); - } + FunctionExpression: { + enter(path) { + if (containsUseGpuDirective(path.node)) { + fnNodeToOriginalMap.set(path.node, types.cloneNode(path.node, true)); + useGpuDepth++; + } + }, + exit(path) { + const node = path.node; + if (containsUseGpuDirective(node)) { + useGpuDepth--; + const parent = path.parentPath.node; + path.replaceWith(functionToTranspiled(node, parent)); + path.skip(); + } + }, }, - CallExpression(path) { - const node = path.node; + FunctionDeclaration: { + enter(path) { + if (containsUseGpuDirective(path.node)) { + fnNodeToOriginalMap.set(path.node, types.cloneNode(path.node, true)); + useGpuDepth++; + } + }, + exit(path) { + const node = (fnNodeToOriginalMap.get(path.node) ?? + path.node) as babel.FunctionDeclaration; + if (containsUseGpuDirective(node)) { + useGpuDepth--; - if (isShellImplementationCall(node, ctx)) { - const implementation = node.arguments[0]; + if (!node.id) { + return; + } - if ( - implementation && - (implementation.type === 'FunctionExpression' || - implementation.type === 'ArrowFunctionExpression') - ) { - const transpiled = functionToTranspiled( - implementation, - null, + const parent = path.parentPath.node; + const expression = types.functionExpression( + node.id, + node.params, + node.body, ); path.replaceWith( - types.callExpression(node.callee, [ - transpiled, + types.variableDeclaration('const', [ + types.variableDeclarator( + node.id, + functionToTranspiled(expression, parent), + ), ]), ); - path.skip(); } - } + }, + }, + + CallExpression: { + exit(path) { + const node = path.node; + + if (isShellImplementationCall(node, ctx)) { + const implementation = node.arguments[0]; + + if ( + implementation && + (implementation.type === 'FunctionExpression' || + implementation.type === 'ArrowFunctionExpression') + ) { + const transpiled = functionToTranspiled( + implementation, + null, + ) as babel.CallExpression; + + path.replaceWith( + types.callExpression(node.callee, [ + transpiled, + ]), + ); + + path.skip(); + } + } + }, }, }; } diff --git a/packages/unplugin-typegpu/src/common.ts b/packages/unplugin-typegpu/src/common.ts index f7fb832d31..ccbc52c766 100644 --- a/packages/unplugin-typegpu/src/common.ts +++ b/packages/unplugin-typegpu/src/common.ts @@ -323,3 +323,14 @@ export const useGpuDirective = 'use gpu'; /** Regular expressions used for early pruning (to avoid unnecessary parsing, which is expensive) */ export const earlyPruneRegex = [/["']use gpu["']/, /t(ype)?gpu/]; + +export const operators = { + '+': '__tsover_add', + '-': '__tsover_sub', + '*': '__tsover_mul', + '/': '__tsover_div', + '+=': '__tsover_add', + '-=': '__tsover_sub', + '*=': '__tsover_mul', + '/=': '__tsover_div', +}; diff --git a/packages/unplugin-typegpu/src/rollup-impl.ts b/packages/unplugin-typegpu/src/rollup-impl.ts index 7469ade2c6..2bdebb7957 100644 --- a/packages/unplugin-typegpu/src/rollup-impl.ts +++ b/packages/unplugin-typegpu/src/rollup-impl.ts @@ -9,6 +9,7 @@ import { gatherTgpuAliases, getFunctionName, isShellImplementationCall, + operators, type Options, performExpressionNaming, useGpuDirective, @@ -183,6 +184,44 @@ export const rollUpImpl = (rawOptions: Options) => { const { params, body, externalNames } = transpileFn(def); const isFunctionStatement = def.type === 'FunctionDeclaration'; + walk(def as Node, { + leave(_node) { + const node = _node as acorn.AnyNode; + + if (node.type === 'AssignmentExpression') { + const runtimeFn = + operators[node.operator as keyof typeof operators]; + + if (runtimeFn) { + const left = node.left; + const right = node.right; + + const lhs = magicString.sliceNode(left); + const rhs = magicString.sliceNode(right); + magicString.overwriteNode( + node, + `${lhs} = ${runtimeFn}(${lhs}, ${rhs})`, + ); + } + } else if (node.type === 'BinaryExpression') { + const runtimeFn = + operators[node.operator as keyof typeof operators]; + + if (runtimeFn) { + const left = node.left; + const right = node.right; + + const lhs = magicString.sliceNode(left); + const rhs = magicString.sliceNode(right); + magicString.overwriteNode( + node, + `${runtimeFn}(${lhs}, ${rhs})`, + ); + } + } + }, + }); + if ( isFunctionStatement && name && diff --git a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts index 73e6f5933e..6c77485c9f 100644 --- a/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts +++ b/packages/unplugin-typegpu/test/tgsl-transpiling.test.ts @@ -256,8 +256,8 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { .computeFn({ in: { num: d.builtin.numWorkgroups }, workgroupSize: [1] })((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((input) => { const tmp = counter.$.x; counter.$.x = counter.$.y; - counter.$.y += tmp; - counter.$.z += d.f32(input.num.x); + counter.$.y = __tsover_add(counter.$.y, tmp); + counter.$.z = __tsover_add(counter.$.z, d.f32(input.num.x)); }), { v: 1, name: undefined, @@ -298,6 +298,7 @@ describe('[ROLLUP] plugin for transpiling tgsl functions to tinyest', () => { }) && $.f)({}))); tgpu.fn([])((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (() => { + __tsover_add(2, 2); }), { v: 1, name: undefined, diff --git a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts index c632b44e98..2ea4f848fe 100644 --- a/packages/unplugin-typegpu/test/use-gpu-directive.test.ts +++ b/packages/unplugin-typegpu/test/use-gpu-directive.test.ts @@ -21,7 +21,7 @@ describe('[BABEL] "use gpu" directive', () => { const addGPU = ($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { 'use gpu'; - return a + b; + return __tsover_add(a, b); }, { v: 1, name: "addGPU", @@ -58,7 +58,7 @@ describe('[BABEL] "use gpu" directive', () => { shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { 'use gpu'; - return a + b; + return __tsover_add(a, b); }, { v: 1, name: void 0, @@ -95,7 +95,7 @@ describe('[BABEL] "use gpu" directive', () => { shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function (a, b) { 'use gpu'; - return a + b; + return __tsover_add(a, b); }, { v: 1, name: void 0, @@ -132,7 +132,7 @@ describe('[BABEL] "use gpu" directive', () => { shell(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = function addGPU(a, b) { 'use gpu'; - return a + b; + return __tsover_add(a, b); }, { v: 1, name: "addGPU", @@ -284,6 +284,35 @@ describe('[BABEL] "use gpu" directive', () => { ;" `); }); + + it('transforms numeric operations', async () => { + const code = `\ + import tgpu from 'typegpu'; + + const main = (a, b) => { + 'use gpu'; + let c = a + b + 2; + c += 2 * b; + }; + `; + + expect(babelTransform(code)).toMatchInlineSnapshot(` + "import tgpu from 'typegpu'; + const main = ($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (a, b) => { + 'use gpu'; + + let c = __tsover_add(__tsover_add(a, b), 2); + c = __tsover_add(c, __tsover_mul(2, b)); + }, { + v: 1, + name: "main", + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]]]],"externalNames":[]}, + externals: () => { + return {}; + } + }) && $.f)({});" + `); + }); }); describe('[ROLLUP] "use gpu" directive', () => { @@ -320,7 +349,7 @@ describe('[ROLLUP] "use gpu" directive', () => { const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { 'use gpu'; - return a + b; + return __tsover_add(a, b); }), { v: 1, name: "addGPU", @@ -362,7 +391,7 @@ describe('[ROLLUP] "use gpu" directive', () => { shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { 'use gpu'; - return a + b; + return __tsover_add(a, b); }), { v: 1, name: undefined, @@ -399,7 +428,7 @@ describe('[ROLLUP] "use gpu" directive', () => { shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function(a, b){ 'use gpu'; - return a + b; + return __tsover_add(a, b); }), { v: 1, name: undefined, @@ -437,7 +466,7 @@ describe('[ROLLUP] "use gpu" directive', () => { shell((($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function addGPU(a, b){ 'use gpu'; - return a + b; + return __tsover_add(a, b); }), { v: 1, name: "addGPU", @@ -458,7 +487,7 @@ describe('[ROLLUP] "use gpu" directive', () => { function addGPU(a, b) { 'use gpu'; - return a + b; + return a + b * 3; } console.log(addGPU); @@ -475,11 +504,11 @@ describe('[ROLLUP] "use gpu" directive', () => { const addGPU = (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function addGPU(a, b) { 'use gpu'; - return a + b; + return __tsover_add(a, __tsover_mul(b, 3)); }), { v: 1, name: "addGPU", - ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+","b"]]]],"externalNames":[]}, + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[10,[1,"a","+",[1,"b","*",[5,"3"]]]]]],"externalNames":[]}, externals: () => ({}), }) && $.f)({})); @@ -579,7 +608,7 @@ describe('[ROLLUP] "use gpu" directive', () => { expect(await rollupTransform(code)).toMatchInlineSnapshot(` "(($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = (function add(a, b) { 'use gpu'; - return a + b; + return __tsover_add(a, b); }), { v: 1, name: "add", @@ -607,4 +636,32 @@ describe('[ROLLUP] "use gpu" directive', () => { " `); }); + + it('transforms numeric operations', async () => { + const code = `\ + import tgpu from 'typegpu'; + + const main = (a, b) => { + 'use gpu'; + let c = a + b + 2; + c += 2 * b; + }; + `; + + expect(await rollupTransform(code)).toMatchInlineSnapshot(` + "import 'typegpu'; + + (($ => (globalThis.__TYPEGPU_META__ ??= new WeakMap()).set($.f = ((a, b) => { + 'use gpu'; + let c = __tsover_add(__tsover_add(a, b), 2); + c = __tsover_add(c, __tsover_mul(2, b)); + }), { + v: 1, + name: "main", + ast: {"params":[{"type":"i","name":"a"},{"type":"i","name":"b"}],"body":[0,[[12,"c",[1,[1,"a","+","b"],"+",[5,"2"]]],[2,"c","+=",[1,[5,"2"],"*","b"]]]],"externalNames":[]}, + externals: () => ({}), + }) && $.f)({})); + " + `); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4c9040acb..bdedf07102 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,12 +41,10 @@ catalogs: '@webgpu/types': specifier: ^0.1.66 version: 0.1.66 - typescript: - specifier: ^5.9.3 - version: 5.9.3 overrides: rollup: 4.34.8 + typescript: npm:tsover@^5.9.10 three: ^0.181.0 importers: @@ -58,7 +56,7 @@ importers: version: link:packages/tgpu-dev-cli '@vitest/browser': specifier: ^3.2.4 - version: 3.2.4(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4) + version: 3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4) '@vitest/coverage-v8': specifier: 3.1.2 version: 3.1.2(@vitest/browser@3.2.4)(vitest@3.2.4) @@ -81,8 +79,8 @@ importers: specifier: ^0.0.62 version: 0.0.62 typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unplugin-typegpu: specifier: workspace:* version: link:packages/unplugin-typegpu @@ -91,7 +89,7 @@ importers: version: 9.0.0(rollup@4.34.8) vitest: specifier: catalog:test - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) apps/bun-example: dependencies: @@ -102,8 +100,8 @@ importers: specifier: 'workspace:' version: link:../../packages/typegpu typescript: - specifier: ^5 - version: 5.8.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unplugin-typegpu: specifier: 'workspace:' version: link:../../packages/unplugin-typegpu @@ -138,16 +136,16 @@ importers: version: 0.25.10 ts-loader: specifier: ^9.5.4 - version: 9.5.4(typescript@5.9.3)(webpack@5.104.1) + version: 9.5.4(tsover@5.9.10)(webpack@5.104.1) tsdown: specifier: ^0.15.6 - version: 0.15.12(typescript@5.9.3)(unrun@0.2.27) + version: 0.15.12(tsover@5.9.10)(unrun@0.2.27) tsx: specifier: ^4.19.2 version: 4.20.6 typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 webpack: specifier: ^5.102.0 version: 5.104.1(esbuild@0.25.10)(webpack-cli@6.0.1) @@ -159,7 +157,7 @@ importers: dependencies: '@astrojs/check': specifier: ^0.9.4 - version: 0.9.4(prettier@3.8.0)(typescript@5.9.3) + version: 0.9.4(prettier@3.8.0)(tsover@5.9.10) '@astrojs/react': specifier: ^4.3.1 version: 4.3.1(@types/node@24.10.0)(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) @@ -168,13 +166,13 @@ importers: version: 3.7.0 '@astrojs/starlight': specifier: ^0.36.1 - version: 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + version: 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) '@astrojs/starlight-tailwind': specifier: ^4.0.1 - version: 4.0.1(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(tailwindcss@4.1.11) + version: 4.0.1(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(tailwindcss@4.1.11) '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@4.1.11) + version: 6.0.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1))(tailwindcss@4.1.11) '@babel/standalone': specifier: ^7.28.6 version: 7.28.6 @@ -231,13 +229,13 @@ importers: version: 2.1.28 astro: specifier: ^5.15.9 - version: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + version: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1) classnames: specifier: ^2.5.1 version: 2.5.1 expressive-code-twoslash: specifier: ^0.5.3 - version: 0.5.3(@expressive-code/core@0.41.2)(expressive-code@0.41.2)(typescript@5.9.3) + version: 0.5.3(@expressive-code/core@0.41.2)(expressive-code@0.41.2)(tsover@5.9.10) fuse.js: specifier: catalog:frontend version: 7.1.0 @@ -291,10 +289,10 @@ importers: version: 0.34.2 starlight-blog: specifier: ^0.23.2 - version: 0.23.2(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + version: 0.23.2(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) starlight-typedoc: specifier: ^0.19.0 - version: 0.19.0(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(typescript@5.9.3)))(typedoc@0.27.9(typescript@5.9.3)) + version: 0.19.0(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(tsover@5.9.10)))(typedoc@0.27.9(tsover@5.9.10)) three: specifier: ^0.181.0 version: 0.181.2 @@ -303,16 +301,16 @@ importers: version: 3.1.1 typedoc: specifier: ^0.27.9 - version: 0.27.9(typescript@5.9.3) + version: 0.27.9(tsover@5.9.10) typedoc-plugin-markdown: specifier: 4.3.0 - version: 4.3.0(typedoc@0.27.9(typescript@5.9.3)) + version: 4.3.0(typedoc@0.27.9(tsover@5.9.10)) typegpu: specifier: workspace:* version: link:../../packages/typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unplugin-typegpu: specifier: workspace:* version: link:../../packages/unplugin-typegpu @@ -349,10 +347,13 @@ importers: version: 0.1.66 astro-vtbot: specifier: ^2.1.10 - version: 2.1.10(prettier@3.8.0)(typescript@5.9.3) + version: 2.1.10(prettier@3.8.0)(tsover@5.9.10) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) + magic-string: + specifier: ^0.30.21 + version: 0.30.21 tailwindcss: specifier: ^4.1.11 version: 4.1.11 @@ -373,23 +374,23 @@ importers: dependencies: '@typescript-eslint/utils': specifier: ^8.53.0 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10) devDependencies: '@types/node': specifier: ^25.0.10 version: 25.1.0 '@typescript-eslint/rule-tester': specifier: ^8.53.1 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10) eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 vitest: specifier: ^4.0.17 - version: 4.0.18(@types/node@25.1.0)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + version: 4.0.18(@types/node@25.1.0)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) packages/tgpu-dev-cli: dependencies: @@ -447,10 +448,10 @@ importers: version: link:../tgpu-dev-cli tsdown: specifier: catalog:build - version: 0.20.3(typescript@5.9.3) + version: 0.20.3(tsover@5.9.10) typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 publishDirectory: dist packages/tinyest-for-wgsl: @@ -473,10 +474,10 @@ importers: version: 8.14.1 tsdown: specifier: catalog:build - version: 0.20.3(typescript@5.9.3) + version: 0.20.3(tsover@5.9.10) typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 publishDirectory: dist packages/typegpu: @@ -484,13 +485,16 @@ importers: tinyest: specifier: workspace:~0.2.0 version: link:../tinyest + tsover-runtime: + specifier: ^0.0.4 + version: 0.0.4 typed-binary: specifier: ^4.3.1 version: 4.3.2 devDependencies: '@ark/attest': specifier: ^0.56.0 - version: 0.56.0(typescript@5.9.3) + version: 0.56.0(tsover@5.9.10) '@typegpu/tgpu-dev-cli': specifier: workspace:* version: link:../tgpu-dev-cli @@ -508,10 +512,10 @@ importers: version: 27.0.0(canvas@3.2.0)(postcss@8.5.6) tsdown: specifier: catalog:build - version: 0.20.3(typescript@5.9.3) + version: 0.20.3(tsover@5.9.10) typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -532,11 +536,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -554,11 +558,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -576,11 +580,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -598,11 +602,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -620,11 +624,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -649,11 +653,11 @@ importers: specifier: workspace:* version: link:../typegpu typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 unbuild: specifier: catalog:build - version: 3.5.0(typescript@5.9.3) + version: 3.5.0(tsover@5.9.10) unplugin-typegpu: specifier: workspace:* version: link:../unplugin-typegpu @@ -730,10 +734,10 @@ importers: version: 4.34.8 tsdown: specifier: catalog:build - version: 0.20.3(typescript@5.9.3) + version: 0.20.3(tsover@5.9.10) typescript: - specifier: catalog:types - version: 5.9.3 + specifier: npm:tsover@^5.9.10 + version: tsover@5.9.10 publishDirectory: dist packages: @@ -1897,9 +1901,6 @@ packages: '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -5252,9 +5253,6 @@ packages: resolution: {integrity: sha512-8rbuNizut2gW94kv7pqgt0dvk+AHLPVIm0iJtpSgQJ9dx21eWx5SBel8z3jp1xtC0j6/iyK3AWGhAR1H61s7LA==} engines: {node: '>=20.18.0'} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -6904,6 +6902,13 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsover-runtime@0.0.4: + resolution: {integrity: sha512-vvv0kU8Yh2RXkdB0yE6mNzGims3ZBIhvyvXJpyawM6G3xtkw4Tzpj2RWtRO0bv0hbrpU5u83oG5/ugBuQKu1FQ==} + + tsover@5.9.10: + resolution: {integrity: sha512-15EJyR8tgHh1ExCreWVb1Y2b5tYfLmYuNfD+H1D+LcXuvXHQH+YCkw4TQwTN05Bq1V0RfyY7NWDHCwQLi9fmnQ==} + hasBin: true + tsx@4.20.6: resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} engines: {node: '>=18.0.0'} @@ -6965,11 +6970,6 @@ packages: typescript-auto-import-cache@0.3.6: resolution: {integrity: sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==} - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -7818,16 +7818,16 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@ark/attest@0.56.0(typescript@5.9.3)': + '@ark/attest@0.56.0(tsover@5.9.10)': dependencies: '@ark/fs': 0.56.0 '@ark/util': 0.56.0 '@prettier/sync': 0.6.1(prettier@3.6.2) '@typescript/analyze-trace': 0.10.1 - '@typescript/vfs': 1.6.1(typescript@5.9.3) + '@typescript/vfs': 1.6.1(tsover@5.9.10) arktype: 2.1.28 prettier: 3.6.2 - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color @@ -7857,23 +7857,23 @@ snapshots: '@asamuzakjp/nwsapi@2.3.9': {} - '@astrojs/check@0.9.4(prettier@3.8.0)(typescript@5.9.3)': + '@astrojs/check@0.9.4(prettier@3.8.0)(tsover@5.9.10)': dependencies: - '@astrojs/language-server': 2.15.4(prettier@3.8.0)(typescript@5.9.3) + '@astrojs/language-server': 2.15.4(prettier@3.8.0)(tsover@5.9.10) chokidar: 4.0.3 kleur: 4.1.5 - typescript: 5.9.3 + typescript: tsover@5.9.10 yargs: 17.7.2 transitivePeerDependencies: - prettier - prettier-plugin-astro - '@astrojs/check@0.9.6(prettier@3.8.0)(typescript@5.9.3)': + '@astrojs/check@0.9.6(prettier@3.8.0)(tsover@5.9.10)': dependencies: - '@astrojs/language-server': 2.16.3(prettier@3.8.0)(typescript@5.9.3) + '@astrojs/language-server': 2.16.3(prettier@3.8.0)(tsover@5.9.10) chokidar: 4.0.3 kleur: 4.1.5 - typescript: 5.9.3 + typescript: tsover@5.9.10 yargs: 17.7.2 transitivePeerDependencies: - prettier @@ -7887,12 +7887,12 @@ snapshots: '@astrojs/internal-helpers@0.7.5': {} - '@astrojs/language-server@2.15.4(prettier@3.8.0)(typescript@5.9.3)': + '@astrojs/language-server@2.15.4(prettier@3.8.0)(tsover@5.9.10)': dependencies: '@astrojs/compiler': 2.13.0 '@astrojs/yaml2ts': 0.2.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@volar/kit': 2.4.11(typescript@5.9.3) + '@volar/kit': 2.4.11(tsover@5.9.10) '@volar/language-core': 2.4.11 '@volar/language-server': 2.4.11 '@volar/language-service': 2.4.11 @@ -7912,12 +7912,12 @@ snapshots: transitivePeerDependencies: - typescript - '@astrojs/language-server@2.16.3(prettier@3.8.0)(typescript@5.9.3)': + '@astrojs/language-server@2.16.3(prettier@3.8.0)(tsover@5.9.10)': dependencies: '@astrojs/compiler': 2.13.0 '@astrojs/yaml2ts': 0.2.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@volar/kit': 2.4.27(typescript@5.9.3) + '@volar/kit': 2.4.27(tsover@5.9.10) '@volar/language-core': 2.4.27 '@volar/language-server': 2.4.27 '@volar/language-service': 2.4.27 @@ -8015,12 +8015,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))': + '@astrojs/mdx@4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@astrojs/markdown-remark': 6.3.1 '@mdx-js/mdx': 3.1.0(acorn@8.15.0) acorn: 8.15.0 - astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1) es-module-lexer: 1.7.0 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -8076,22 +8076,22 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.25.76 - '@astrojs/starlight-tailwind@4.0.1(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(tailwindcss@4.1.11)': + '@astrojs/starlight-tailwind@4.0.1(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(tailwindcss@4.1.11)': dependencies: - '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) tailwindcss: 4.1.11 - '@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))': + '@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@astrojs/markdown-remark': 6.3.6 - '@astrojs/mdx': 4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/mdx': 4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) '@astrojs/sitemap': 3.7.0 '@pagefind/default-ui': 1.3.0 '@types/hast': 3.0.4 '@types/js-yaml': 4.0.9 '@types/mdast': 4.0.4 - astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) - astro-expressive-code: 0.41.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1) + astro-expressive-code: 0.41.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) bcp-47: 2.1.0 hast-util-from-html: 2.0.3 hast-util-select: 6.0.4 @@ -8114,9 +8114,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/tailwind@6.0.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(tailwindcss@4.1.11)': + '@astrojs/tailwind@6.0.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1))(tailwindcss@4.1.11)': dependencies: - astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1) autoprefixer: 10.4.21(postcss@8.5.3) postcss: 8.5.3 postcss-load-config: 4.0.2(postcss@8.5.3) @@ -8929,8 +8929,6 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.0': {} - '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.25': @@ -10061,32 +10059,32 @@ snapshots: '@types/webxr@0.5.22': {} - '@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10)': dependencies: '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.54.0(tsover@5.9.10) '@typescript-eslint/visitor-keys': 8.54.0 debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.54.0(tsover@5.9.10)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.54.0(tsover@5.9.10) '@typescript-eslint/types': 8.54.0 debug: 4.4.3 - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color - '@typescript-eslint/rule-tester@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/rule-tester@8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10)': dependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10) + '@typescript-eslint/typescript-estree': 8.54.0(tsover@5.9.10) + '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10) ajv: 6.12.6 eslint: 9.39.2(jiti@2.6.1) json-stable-stringify-without-jsonify: 1.0.1 @@ -10101,35 +10099,35 @@ snapshots: '@typescript-eslint/types': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0 - '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.54.0(tsover@5.9.10)': dependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 '@typescript-eslint/types@8.54.0': {} - '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.54.0(tsover@5.9.10)': dependencies: - '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) + '@typescript-eslint/project-service': 8.54.0(tsover@5.9.10) + '@typescript-eslint/tsconfig-utils': 8.54.0(tsover@5.9.10) '@typescript-eslint/types': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.4.0(tsover@5.9.10) + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(tsover@5.9.10)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.54.0(tsover@5.9.10) eslint: 9.39.2(jiti@2.6.1) - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color @@ -10149,10 +10147,10 @@ snapshots: treeify: 1.1.0 yargs: 16.2.0 - '@typescript/vfs@1.6.1(typescript@5.9.3)': + '@typescript/vfs@1.6.1(tsover@5.9.10)': dependencies: debug: 4.4.3 - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color @@ -10174,13 +10172,30 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitest/browser@3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)': + dependencies: + '@testing-library/dom': 10.4.0 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) + '@vitest/mocker': 3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/utils': 3.2.4 + magic-string: 0.30.21 + sirv: 3.0.1 + tinyrainbow: 2.0.0 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + '@vitest/browser@3.2.4(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) '@vitest/mocker': 3.2.4(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) '@vitest/utils': 3.2.4 - magic-string: 0.30.17 + magic-string: 0.30.21 sirv: 3.0.1 tinyrainbow: 2.0.0 vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) @@ -10190,6 +10205,7 @@ snapshots: - msw - utf-8-validate - vite + optional: true '@vitest/coverage-v8@3.1.2(@vitest/browser@3.2.4)(vitest@3.2.4)': dependencies: @@ -10200,14 +10216,14 @@ snapshots: istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.17 + magic-string: 0.30.21 magicast: 0.3.5 std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) optionalDependencies: - '@vitest/browser': 3.2.4(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4) + '@vitest/browser': 3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4) transitivePeerDependencies: - supports-color @@ -10228,6 +10244,15 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 + '@vitest/mocker@3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.10.2(@types/node@25.1.0)(tsover@5.9.10) + vite: 6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + '@vitest/mocker@3.2.4(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 @@ -10237,13 +10262,13 @@ snapshots: msw: 2.10.2(@types/node@25.1.0)(typescript@5.9.3) vite: 6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/mocker@4.0.18(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.0.18(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.10.2(@types/node@25.1.0)(typescript@5.9.3) + msw: 2.10.2(@types/node@25.1.0)(tsover@5.9.10) vite: 6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': @@ -10294,21 +10319,21 @@ snapshots: '@vitest/pretty-format': 4.0.18 tinyrainbow: 3.0.3 - '@volar/kit@2.4.11(typescript@5.9.3)': + '@volar/kit@2.4.11(tsover@5.9.10)': dependencies: '@volar/language-service': 2.4.11 '@volar/typescript': 2.4.11 typesafe-path: 0.2.2 - typescript: 5.9.3 + typescript: tsover@5.9.10 vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.1.0 - '@volar/kit@2.4.27(typescript@5.9.3)': + '@volar/kit@2.4.27(tsover@5.9.10)': dependencies: '@volar/language-service': 2.4.27 '@volar/typescript': 2.4.27 typesafe-path: 0.2.2 - typescript: 5.9.3 + typescript: tsover@5.9.10 vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.1.0 @@ -10388,9 +10413,9 @@ snapshots: '@vtbag/element-crossing@1.1.0': {} - '@vtbag/inspection-chamber@1.0.23(prettier@3.8.0)(typescript@5.9.3)': + '@vtbag/inspection-chamber@1.0.23(prettier@3.8.0)(tsover@5.9.10)': dependencies: - '@astrojs/check': 0.9.6(prettier@3.8.0)(typescript@5.9.3) + '@astrojs/check': 0.9.6(prettier@3.8.0)(tsover@5.9.10) transitivePeerDependencies: - prettier - prettier-plugin-astro @@ -10606,9 +10631,9 @@ snapshots: astring@1.9.0: {} - astro-expressive-code@0.41.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)): + astro-expressive-code@0.41.2(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)): dependencies: - astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + astro: 5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1) rehype-expressive-code: 0.41.2 astro-remote@0.3.3: @@ -10619,11 +10644,11 @@ snapshots: marked-smartypants: 1.1.9(marked@12.0.2) ultrahtml: 1.6.0 - astro-vtbot@2.1.10(prettier@3.8.0)(typescript@5.9.3): + astro-vtbot@2.1.10(prettier@3.8.0)(tsover@5.9.10): dependencies: '@vtbag/cam-shaft': 1.0.6 '@vtbag/element-crossing': 1.1.0 - '@vtbag/inspection-chamber': 1.0.23(prettier@3.8.0)(typescript@5.9.3) + '@vtbag/inspection-chamber': 1.0.23(prettier@3.8.0)(tsover@5.9.10) '@vtbag/turn-signal': 1.3.1 '@vtbag/utensil-drawer': 1.2.14 transitivePeerDependencies: @@ -10631,7 +10656,7 @@ snapshots: - prettier-plugin-astro - typescript - astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): + astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@astrojs/compiler': 2.13.0 '@astrojs/internal-helpers': 0.7.5 @@ -10681,7 +10706,7 @@ snapshots: smol-toml: 1.6.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 - tsconfck: 3.1.6(typescript@5.9.3) + tsconfck: 3.1.6(tsover@5.9.10) ultrahtml: 1.6.0 unifont: 0.6.0 unist-util-visit: 5.0.0 @@ -10694,7 +10719,7 @@ snapshots: yocto-spinner: 0.2.3 zod: 3.25.76 zod-to-json-schema: 3.25.1(zod@3.25.76) - zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + zod-to-ts: 1.2.0(tsover@5.9.10)(zod@3.25.76) optionalDependencies: sharp: 0.34.2 transitivePeerDependencies: @@ -11385,7 +11410,7 @@ snapshots: glob: 10.5.0 ora: 5.4.1 tslib: 2.8.1 - typescript: 5.9.3 + typescript: tsover@5.9.10 yargs: 17.7.2 dset@3.1.4: {} @@ -11682,15 +11707,15 @@ snapshots: expect-type@1.3.0: {} - expressive-code-twoslash@0.5.3(@expressive-code/core@0.41.2)(expressive-code@0.41.2)(typescript@5.9.3): + expressive-code-twoslash@0.5.3(@expressive-code/core@0.41.2)(expressive-code@0.41.2)(tsover@5.9.10): dependencies: '@expressive-code/core': 0.41.2 expressive-code: 0.41.2 mdast-util-from-markdown: 2.0.2 mdast-util-gfm: 3.1.0 mdast-util-to-hast: 13.2.0 - twoslash: 0.2.12(typescript@5.9.3) - typescript: 5.9.3 + twoslash: 0.2.12(tsover@5.9.10) + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color @@ -12507,11 +12532,7 @@ snapshots: magic-string-ast@1.0.0: dependencies: - magic-string: 0.30.17 - - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + magic-string: 0.30.21 magic-string@0.30.21: dependencies: @@ -13119,7 +13140,7 @@ snapshots: mkdirp-classic@0.5.3: optional: true - mkdist@2.2.0(typescript@5.9.3): + mkdist@2.2.0(tsover@5.9.10): dependencies: autoprefixer: 10.4.21(postcss@8.5.6) citty: 0.1.6 @@ -13135,7 +13156,7 @@ snapshots: semver: 7.7.3 tinyglobby: 0.2.15 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 mlly@1.8.0: dependencies: @@ -13171,6 +13192,32 @@ snapshots: ms@2.1.3: {} + msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.21(@types/node@25.1.0) + '@mswjs/interceptors': 0.39.8 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.6 + graphql: 16.12.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.41.0 + yargs: 17.7.2 + optionalDependencies: + typescript: tsover@5.9.10 + transitivePeerDependencies: + - '@types/node' + optional: true + msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3): dependencies: '@bundled-es-modules/cookie': 2.0.1 @@ -14052,7 +14099,7 @@ snapshots: robust-predicates@3.0.2: {} - rolldown-plugin-dts@0.17.8(rolldown@1.0.0-beta.45)(typescript@5.9.3): + rolldown-plugin-dts@0.17.8(rolldown@1.0.0-beta.45)(tsover@5.9.10): dependencies: '@babel/generator': 7.28.5 '@babel/parser': 7.28.6 @@ -14065,11 +14112,11 @@ snapshots: obug: 2.1.1 rolldown: 1.0.0-beta.45 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - oxc-resolver - rolldown-plugin-dts@0.22.1(rolldown@1.0.0-rc.3)(typescript@5.9.3): + rolldown-plugin-dts@0.22.1(rolldown@1.0.0-rc.3)(tsover@5.9.10): dependencies: '@babel/generator': 8.0.0-rc.1 '@babel/helper-validator-identifier': 8.0.0-rc.1 @@ -14082,7 +14129,7 @@ snapshots: obug: 2.1.1 rolldown: 1.0.0-rc.3 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - oxc-resolver @@ -14147,11 +14194,11 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.3 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.3 - rollup-plugin-dts@6.1.1(rollup@4.34.8)(typescript@5.9.3): + rollup-plugin-dts@6.1.1(rollup@4.34.8)(tsover@5.9.10): dependencies: magic-string: 0.30.21 rollup: 4.34.8 - typescript: 5.9.3 + typescript: tsover@5.9.10 optionalDependencies: '@babel/code-frame': 7.27.1 @@ -14334,12 +14381,12 @@ snapshots: stackback@0.0.2: {} - starlight-blog@0.23.2(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)): + starlight-blog@0.23.2(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)): dependencies: '@astrojs/markdown-remark': 6.3.1 - '@astrojs/mdx': 4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/mdx': 4.2.6(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) '@astrojs/rss': 4.0.11 - '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) astro-remote: 0.3.3 github-slugger: 2.0.0 marked: 15.0.7 @@ -14351,12 +14398,12 @@ snapshots: - astro - supports-color - starlight-typedoc@0.19.0(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)))(typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(typescript@5.9.3)))(typedoc@0.27.9(typescript@5.9.3)): + starlight-typedoc@0.19.0(@astrojs/starlight@0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)))(typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(tsover@5.9.10)))(typedoc@0.27.9(tsover@5.9.10)): dependencies: - '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/starlight': 0.36.1(astro@5.15.9(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.34.8)(terser@5.44.1)(tsover@5.9.10)(tsx@4.20.6)(yaml@2.8.1)) github-slugger: 2.0.0 - typedoc: 0.27.9(typescript@5.9.3) - typedoc-plugin-markdown: 4.3.0(typedoc@0.27.9(typescript@5.9.3)) + typedoc: 0.27.9(tsover@5.9.10) + typedoc-plugin-markdown: 4.3.0(typedoc@0.27.9(tsover@5.9.10)) state-local@1.0.7: {} @@ -14582,25 +14629,25 @@ snapshots: trough@2.2.0: {} - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.4.0(tsover@5.9.10): dependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1): + ts-loader@9.5.4(tsover@5.9.10)(webpack@5.104.1): dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 micromatch: 4.0.8 semver: 7.7.3 source-map: 0.7.4 - typescript: 5.9.3 + typescript: tsover@5.9.10 webpack: 5.104.1(esbuild@0.25.10)(webpack-cli@6.0.1) - tsconfck@3.1.6(typescript@5.9.3): + tsconfck@3.1.6(tsover@5.9.10): optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 - tsdown@0.15.12(typescript@5.9.3)(unrun@0.2.27): + tsdown@0.15.12(tsover@5.9.10)(unrun@0.2.27): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -14610,14 +14657,14 @@ snapshots: empathic: 2.0.0 hookable: 5.5.3 rolldown: 1.0.0-beta.45 - rolldown-plugin-dts: 0.17.8(rolldown@1.0.0-beta.45)(typescript@5.9.3) + rolldown-plugin-dts: 0.17.8(rolldown@1.0.0-beta.45)(tsover@5.9.10) semver: 7.7.3 tinyexec: 1.0.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 unconfig: 7.4.2 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 unrun: 0.2.27 transitivePeerDependencies: - '@ts-macro/tsc' @@ -14626,7 +14673,7 @@ snapshots: - supports-color - vue-tsc - tsdown@0.20.3(typescript@5.9.3): + tsdown@0.20.3(tsover@5.9.10): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -14637,7 +14684,7 @@ snapshots: obug: 2.1.1 picomatch: 4.0.3 rolldown: 1.0.0-rc.3 - rolldown-plugin-dts: 0.22.1(rolldown@1.0.0-rc.3)(typescript@5.9.3) + rolldown-plugin-dts: 0.22.1(rolldown@1.0.0-rc.3)(tsover@5.9.10) semver: 7.7.3 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -14645,7 +14692,7 @@ snapshots: unconfig-core: 7.4.2 unrun: 0.2.27 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - '@ts-macro/tsc' - '@typescript/native-preview' @@ -14655,6 +14702,16 @@ snapshots: tslib@2.8.1: {} + tsover-runtime@0.0.4: + dependencies: + minimatch: 9.0.5 + unplugin: 2.3.5 + + tsover@5.9.10: + dependencies: + minimatch: 9.0.5 + unplugin: 2.3.5 + tsx@4.20.6: dependencies: esbuild: 0.25.10 @@ -14671,11 +14728,11 @@ snapshots: twoslash-protocol@0.2.12: {} - twoslash@0.2.12(typescript@5.9.3): + twoslash@0.2.12(tsover@5.9.10): dependencies: - '@typescript/vfs': 1.6.1(typescript@5.9.3) + '@typescript/vfs': 1.6.1(tsover@5.9.10) twoslash-protocol: 0.2.12 - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - supports-color @@ -14691,17 +14748,17 @@ snapshots: typed-binary@4.3.2: {} - typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(typescript@5.9.3)): + typedoc-plugin-markdown@4.3.0(typedoc@0.27.9(tsover@5.9.10)): dependencies: - typedoc: 0.27.9(typescript@5.9.3) + typedoc: 0.27.9(tsover@5.9.10) - typedoc@0.27.9(typescript@5.9.3): + typedoc@0.27.9(tsover@5.9.10): dependencies: '@gerrit0/mini-shiki': 1.27.2 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 - typescript: 5.9.3 + typescript: tsover@5.9.10 yaml: 2.8.1 typesafe-path@0.2.2: {} @@ -14714,9 +14771,8 @@ snapshots: dependencies: semver: 7.7.3 - typescript@5.8.3: {} - - typescript@5.9.3: {} + typescript@5.9.3: + optional: true uc.micro@2.1.0: {} @@ -14724,7 +14780,7 @@ snapshots: ultrahtml@1.6.0: {} - unbuild@3.5.0(typescript@5.9.3): + unbuild@3.5.0(tsover@5.9.10): dependencies: '@rollup/plugin-alias': 5.1.1(rollup@4.34.8) '@rollup/plugin-commonjs': 28.0.3(rollup@4.34.8) @@ -14740,18 +14796,18 @@ snapshots: hookable: 5.5.3 jiti: 2.6.1 magic-string: 0.30.21 - mkdist: 2.2.0(typescript@5.9.3) + mkdist: 2.2.0(tsover@5.9.10) mlly: 1.8.0 pathe: 2.0.3 pkg-types: 2.1.0 pretty-bytes: 6.1.1 rollup: 4.34.8 - rollup-plugin-dts: 6.1.1(rollup@4.34.8)(typescript@5.9.3) + rollup-plugin-dts: 6.1.1(rollup@4.34.8)(tsover@5.9.10) scule: 1.3.0 tinyglobby: 0.2.15 untyped: 2.0.0 optionalDependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 transitivePeerDependencies: - sass - vue @@ -15071,6 +15127,50 @@ snapshots: optionalDependencies: vite: 6.4.1(@types/node@24.10.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 25.1.0 + '@vitest/browser': 3.2.4(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vitest@3.2.4) + jsdom: 27.0.0(canvas@3.2.0)(postcss@8.5.6) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.1.0)(@vitest/browser@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 @@ -15115,10 +15215,10 @@ snapshots: - tsx - yaml - vitest@4.0.18(@types/node@25.1.0)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): + vitest@4.0.18(@types/node@25.1.0)(jiti@2.6.1)(jsdom@27.0.0(canvas@3.2.0)(postcss@8.5.6))(lightningcss@1.30.2)(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(msw@2.10.2(@types/node@25.1.0)(typescript@5.9.3))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/mocker': 4.0.18(msw@2.10.2(@types/node@25.1.0)(tsover@5.9.10))(vite@6.4.1(@types/node@25.1.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -15571,9 +15671,9 @@ snapshots: dependencies: zod: 3.25.76 - zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76): + zod-to-ts@1.2.0(tsover@5.9.10)(zod@3.25.76): dependencies: - typescript: 5.9.3 + typescript: tsover@5.9.10 zod: 3.25.76 zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 19499a81a8..44c6c5797b 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,7 +11,7 @@ catalogs: unbuild: ^3.5.0 jiti: ^2.6.0 types: - typescript: ^5.9.3 + typescript: npm:tsover@^5.9.10 '@webgpu/types': ^0.1.66 '@types/three': '^0.181.0' test: