Skip to content

fix: Use a more compatible texture format for stable-fluids example and add touch controls #1251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ import {
renderLayout,
} from './render.ts';
import * as c from './simulation.ts';
import type { BrushState, DisplayMode, FieldFormat } from './types.ts';
import type { BrushState, DisplayMode } from './types.ts';

// Initialize
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
throw new Error('No GPU adapter found');
}
const device = await adapter.requestDevice({
requiredFeatures: ['float32-filterable'],
});
const root = tgpu.initFromDevice({ device });
const root = await tgpu.init();
const device = root.device;

// Setup canvas
const canvas = document.querySelector('canvas') as HTMLCanvasElement;
Expand All @@ -37,9 +35,9 @@ context.configure({
});

// Helpers
function createField(format: FieldFormat, name: string) {
function createField(name: string) {
return root['~unstable']
.createTexture({ size: [p.SIM_N, p.SIM_N], format })
.createTexture({ size: [p.SIM_N, p.SIM_N], format: 'rgba16float' })
.$usage('storage', 'sampled')
.$name(name);
}
Expand Down Expand Up @@ -120,22 +118,13 @@ device.queue.copyExternalImageToTexture(
);

// Create simulation textures
const velTex = [
createField('rg32float', 'velocity0'),
createField('rg32float', 'velocity1'),
];
const inkTex = [
createField('r32float', 'density0'),
createField('r32float', 'density1'),
];
const pressureTex = [
createField('r32float', 'pressure0'),
createField('r32float', 'pressure1'),
];

const newInkTex = createField('r32float', 'addedInk');
const forceTex = createField('rg32float', 'force');
const divergenceTex = createField('r32float', 'divergence');
const velTex = [createField('velocity0'), createField('velocity1')];
const inkTex = [createField('density0'), createField('density1')];
const pressureTex = [createField('pressure0'), createField('pressure1')];

const newInkTex = createField('addedInk');
const forceTex = createField('force');
const divergenceTex = createField('divergence');

const linSampler = tgpu['~unstable'].sampler({
magFilter: 'linear',
Expand Down Expand Up @@ -419,17 +408,43 @@ canvas.addEventListener('mousedown', (e) => {
isDown: true,
};
});
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const x = (touch.clientX - rect.left) * devicePixelRatio;
const y = (touch.clientY - rect.top) * devicePixelRatio;
brushState = {
pos: toGrid(x, y),
delta: [0, 0],
isDown: true,
};
}, { passive: false });

window.addEventListener('mouseup', () => {
brushState.isDown = false;
});
window.addEventListener('touchend', () => {
brushState.isDown = false;
});

canvas.addEventListener('mousemove', (e) => {
const x = e.offsetX * devicePixelRatio;
const y = e.offsetY * devicePixelRatio;

const [newX, newY] = toGrid(x, y);
brushState.delta = [newX - brushState.pos[0], newY - brushState.pos[1]];
brushState.pos = [newX, newY];
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const x = (touch.clientX - rect.left) * devicePixelRatio;
const y = (touch.clientY - rect.top) * devicePixelRatio;
const [newX, newY] = toGrid(x, y);
brushState.delta = [newX - brushState.pos[0], newY - brushState.pos[1]];
brushState.pos = [newX, newY];
}, { passive: false });

function hideHelp() {
const helpElem = document.getElementById('help');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const getNeighbors = tgpu['~unstable'].fn(
for (let i = 0; i < 4; i++) {
adjacentOffsets[i] = std.clamp(
std.add(coords, adjacentOffsets[i]),
d.vec2i(0),
d.vec2i(),
std.sub(bounds, d.vec2i(1)),
);
}
Expand All @@ -25,8 +25,8 @@ const getNeighbors = tgpu['~unstable'].fn(

export const brushLayout = tgpu.bindGroupLayout({
brushParams: { uniform: p.BrushParams },
forceDst: { storageTexture: 'rg32float', access: 'writeonly' },
inkDst: { storageTexture: 'r32float', access: 'writeonly' },
forceDst: { storageTexture: 'rgba16float', access: 'writeonly' },
inkDst: { storageTexture: 'rgba16float', access: 'writeonly' },
});

export const brushFn = tgpu['~unstable'].computeFn({
Expand Down Expand Up @@ -67,7 +67,7 @@ export const brushFn = tgpu['~unstable'].computeFn({

export const addForcesLayout = tgpu.bindGroupLayout({
src: { texture: 'float' },
dst: { storageTexture: 'rg32float', access: 'writeonly' },
dst: { storageTexture: 'rgba16float', access: 'writeonly' },
force: { texture: 'float' },
simParams: { uniform: p.ShaderParams },
});
Expand All @@ -86,7 +86,7 @@ export const addForcesFn = tgpu['~unstable'].computeFn({

export const advectLayout = tgpu.bindGroupLayout({
src: { texture: 'float' },
dst: { storageTexture: 'rg32float', access: 'writeonly' },
dst: { storageTexture: 'rgba16float', access: 'writeonly' },
simParams: { uniform: p.ShaderParams },
linSampler: { sampler: 'filtering' },
});
Expand Down Expand Up @@ -131,7 +131,7 @@ export const advectFn = tgpu['~unstable'].computeFn({

export const diffusionLayout = tgpu.bindGroupLayout({
in: { texture: 'float' },
out: { storageTexture: 'rg32float', access: 'writeonly' },
out: { storageTexture: 'rgba16float', access: 'writeonly' },
simParams: { uniform: p.ShaderParams },
});

Expand Down Expand Up @@ -170,7 +170,7 @@ export const diffusionFn = tgpu['~unstable'].computeFn({

export const divergenceLayout = tgpu.bindGroupLayout({
vel: { texture: 'float' },
div: { storageTexture: 'r32float', access: 'writeonly' },
div: { storageTexture: 'rgba16float', access: 'writeonly' },
});

export const divergenceFn = tgpu['~unstable'].computeFn({
Expand All @@ -189,7 +189,7 @@ export const divergenceFn = tgpu['~unstable'].computeFn({
const rightVel = std.textureLoad(divergenceLayout.$.vel, neighbors[2], 0);
const downVel = std.textureLoad(divergenceLayout.$.vel, neighbors[3], 0);

const divergence = d.f32(0.5) *
const divergence = 0.5 *
(rightVel.x - leftVel.x + (downVel.y - upVel.y));
std.textureStore(
divergenceLayout.$.div,
Expand All @@ -201,7 +201,7 @@ export const divergenceFn = tgpu['~unstable'].computeFn({
export const pressureLayout = tgpu.bindGroupLayout({
x: { texture: 'float' },
b: { texture: 'float' },
out: { storageTexture: 'r32float', access: 'writeonly' },
out: { storageTexture: 'rgba16float', access: 'writeonly' },
});

export const pressureFn = tgpu['~unstable'].computeFn({
Expand Down Expand Up @@ -232,7 +232,7 @@ export const pressureFn = tgpu['~unstable'].computeFn({
export const projectLayout = tgpu.bindGroupLayout({
vel: { texture: 'float' },
p: { texture: 'float' },
out: { storageTexture: 'rg32float', access: 'writeonly' },
out: { storageTexture: 'rgba16float', access: 'writeonly' },
});

export const projectFn = tgpu['~unstable'].computeFn({
Expand Down Expand Up @@ -261,7 +261,7 @@ export const projectFn = tgpu['~unstable'].computeFn({
export const advectInkLayout = tgpu.bindGroupLayout({
vel: { texture: 'float' },
src: { texture: 'float' },
dst: { storageTexture: 'r32float', access: 'writeonly' },
dst: { storageTexture: 'rgba16float', access: 'writeonly' },
simParams: { uniform: p.ShaderParams },
linSampler: { sampler: 'filtering' },
});
Expand Down Expand Up @@ -297,7 +297,7 @@ export const advectInkFn = tgpu['~unstable'].computeFn({

export const addInkLayout = tgpu.bindGroupLayout({
src: { texture: 'float' },
dst: { storageTexture: 'r32float', access: 'writeonly' },
dst: { storageTexture: 'rgba16float', access: 'writeonly' },
add: { texture: 'float' },
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export type FieldFormat = 'rg32float' | 'r32float';
export type DisplayMode = 'ink' | 'velocity' | 'image';

export type SimulationParams = {
Expand Down