first commit
This commit is contained in:
387
main.js
Normal file
387
main.js
Normal file
@@ -0,0 +1,387 @@
|
||||
import { Engine } from "/framework/Engine.js";
|
||||
|
||||
import { Scene } from "/framework/Scene.js";
|
||||
|
||||
import { Mesh } from "/framework/Mesh.js";
|
||||
|
||||
import { OceanPipeline } from "./pipelines/OceanPipeline.js";
|
||||
|
||||
import { OceanTests } from "./tests/OceanTests.js";
|
||||
|
||||
import { events } from "./events.js";
|
||||
|
||||
|
||||
class OceanApp {
|
||||
|
||||
static bitReverse( x, bits ) {
|
||||
|
||||
let n = x;
|
||||
let r = 0;
|
||||
|
||||
for ( let i = 0; i < bits; i++ ) {
|
||||
|
||||
r = ( r << 1 ) | ( n & 1 );
|
||||
n = n >> 1;
|
||||
|
||||
}
|
||||
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
|
||||
async run( ) {
|
||||
|
||||
const eventsHandler = new events( );
|
||||
|
||||
const domElements = eventsHandler.getDomElements( );
|
||||
|
||||
const statusElement = domElements.statusElement;
|
||||
|
||||
const canvas = domElements.canvas;
|
||||
|
||||
const waveHeightSlider = domElements.waveHeightSlider;
|
||||
|
||||
const waveHeightValue = domElements.waveHeightValue;
|
||||
|
||||
const wavelengthSlider = domElements.wavelengthSlider;
|
||||
|
||||
const wavelengthValue = domElements.wavelengthValue;
|
||||
|
||||
const resolutionSlider = domElements.resolutionSlider;
|
||||
|
||||
const resolutionValue = domElements.resolutionValue;
|
||||
|
||||
const tilingSlider = domElements.tilingSlider;
|
||||
|
||||
const tilingValue = domElements.tilingValue;
|
||||
|
||||
const wireframeToggle = domElements.wireframeToggle;
|
||||
|
||||
const pauseToggle = domElements.pauseToggle;
|
||||
|
||||
const shadingModeSelect = domElements.shadingModeSelect;
|
||||
|
||||
const stepButton = domElements.stepButton;
|
||||
|
||||
const dumpHeightButton = domElements.dumpHeightButton;
|
||||
|
||||
|
||||
function resizeCanvasToDisplay( ) {
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
||||
if ( canvas.width !== width || canvas.height !== height ) {
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resizeCanvasToDisplay( );
|
||||
|
||||
if ( !navigator.gpu ) {
|
||||
statusElement.textContent = "WebGPU not supported in this browser.";
|
||||
return;
|
||||
}
|
||||
|
||||
statusElement.textContent = "Requesting WebGPU device…";
|
||||
|
||||
const adapter = await navigator.gpu.requestAdapter( );
|
||||
|
||||
const device = await adapter.requestDevice( );
|
||||
|
||||
const engine = new Engine( device );
|
||||
|
||||
const renderSystem = engine.createRenderSystem( canvas );
|
||||
|
||||
let scene = new Scene( );
|
||||
|
||||
let pipelines = [];
|
||||
|
||||
let currentMeshResolution = 1024;
|
||||
|
||||
let currentWavelengthScale = 1.0;
|
||||
|
||||
let currentTiling = 1;
|
||||
|
||||
|
||||
function getPrimaryPipeline( ) {
|
||||
|
||||
return pipelines.length > 0 ? pipelines[ 0 ] : null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function rebuildScene( ) {
|
||||
|
||||
const previousPrimary = getPrimaryPipeline( );
|
||||
|
||||
statusElement.textContent = "Rebuilding pipelines…";
|
||||
|
||||
const newScene = new Scene( );
|
||||
|
||||
const meshRes = currentMeshResolution;
|
||||
|
||||
const tiling = currentTiling;
|
||||
|
||||
const pipeline = new OceanPipeline( engine, canvas );
|
||||
|
||||
pipeline.gridSize = 64;
|
||||
pipeline.meshResolution = meshRes;
|
||||
|
||||
pipeline.memory.set( "canvasRef", canvas );
|
||||
|
||||
await pipeline.create( );
|
||||
|
||||
// restore camera from previous primary pipeline if available
|
||||
if ( previousPrimary && previousPrimary.camera && pipeline.camera ) {
|
||||
|
||||
const prevCam = previousPrimary.camera;
|
||||
const newCam = pipeline.camera;
|
||||
|
||||
newCam.yaw = prevCam.yaw;
|
||||
newCam.pitch = prevCam.pitch;
|
||||
newCam.distance = prevCam.distance;
|
||||
newCam.fovRadians = prevCam.fovRadians;
|
||||
newCam.near = prevCam.near;
|
||||
newCam.far = prevCam.far;
|
||||
|
||||
if ( prevCam.target && newCam.target ) {
|
||||
|
||||
newCam.target.x = prevCam.target.x;
|
||||
newCam.target.y = prevCam.target.y;
|
||||
newCam.target.z = prevCam.target.z;
|
||||
|
||||
}
|
||||
|
||||
if ( prevCam.up && newCam.up ) {
|
||||
|
||||
newCam.up.x = prevCam.up.x;
|
||||
newCam.up.y = prevCam.up.y;
|
||||
newCam.up.z = prevCam.up.z;
|
||||
|
||||
}
|
||||
|
||||
if ( typeof newCam.update === "function" ) {
|
||||
|
||||
newCam.update( );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// global settings
|
||||
if ( waveHeightSlider && waveHeightValue ) {
|
||||
|
||||
const hValue = parseFloat( waveHeightSlider.value );
|
||||
|
||||
if ( !isNaN( hValue ) ) {
|
||||
|
||||
pipeline.setHeightScale( hValue );
|
||||
|
||||
waveHeightValue.textContent = hValue.toFixed( 0 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( wavelengthSlider && wavelengthValue && typeof pipeline.setWavelengthScale === "function" ) {
|
||||
|
||||
const wValue = parseFloat( wavelengthSlider.value );
|
||||
|
||||
if ( !isNaN( wValue ) && wValue > 0 ) {
|
||||
|
||||
pipeline.setWavelengthScale( wValue );
|
||||
|
||||
wavelengthValue.textContent = wValue.toFixed( 2 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( wireframeToggle ) {
|
||||
|
||||
if ( wireframeToggle.checked ) {
|
||||
|
||||
pipeline.setRenderMode( "wireframe" );
|
||||
|
||||
} else {
|
||||
|
||||
pipeline.setRenderMode( "solid" );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( shadingModeSelect ) {
|
||||
|
||||
pipeline.setShadingMode( shadingModeSelect.value || "lighting" );
|
||||
|
||||
}
|
||||
|
||||
if ( pauseToggle ) {
|
||||
|
||||
pipeline.setPaused( pauseToggle.checked );
|
||||
|
||||
}
|
||||
|
||||
// tiling (number of tiles around center)
|
||||
pipeline.setTiling( tiling );
|
||||
|
||||
const tileRange = Math.max( 0, tiling - 1 );
|
||||
const tilesPerRow = tileRange * 2 + 1;
|
||||
const tileCount = tilesPerRow * tilesPerRow;
|
||||
|
||||
const block = pipeline.getBlockByName( "ocean" );
|
||||
|
||||
if ( block ) {
|
||||
|
||||
const skyPass = block.getPass( "SkySphere" );
|
||||
|
||||
if ( skyPass && skyPass.shader ) {
|
||||
|
||||
const skyMesh = new Mesh( );
|
||||
|
||||
skyMesh.addShader( skyPass.shader );
|
||||
|
||||
newScene.addMesh( skyMesh );
|
||||
|
||||
}
|
||||
|
||||
|
||||
const renderPassName = wireframeToggle && wireframeToggle.checked ? "RenderWire" : "RenderSolid";
|
||||
|
||||
const renderPass = block.getPass( renderPassName );
|
||||
|
||||
if ( renderPass && renderPass.shader ) {
|
||||
|
||||
const mesh = new Mesh( );
|
||||
|
||||
mesh.addShader( renderPass.shader );
|
||||
|
||||
mesh.setInstanceCount( tileCount );
|
||||
|
||||
newScene.addMesh( mesh );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pipelines = [ pipeline ];
|
||||
scene = newScene;
|
||||
|
||||
statusElement.textContent = "Simulating ocean (drag to orbit, scroll to zoom)";
|
||||
|
||||
}
|
||||
|
||||
|
||||
await rebuildScene( );
|
||||
|
||||
eventsHandler.resizeCanvasToDisplay = resizeCanvasToDisplay;
|
||||
eventsHandler.getPrimaryPipeline = getPrimaryPipeline;
|
||||
eventsHandler.getPipelines = function( ) { return pipelines; };
|
||||
eventsHandler.rebuildScene = rebuildScene;
|
||||
eventsHandler.getScene = function( ) { return scene; };
|
||||
eventsHandler.getRenderSystem = function( ) { return renderSystem; };
|
||||
eventsHandler.setCurrentMeshResolution = function( value ) { currentMeshResolution = value; };
|
||||
eventsHandler.setCurrentWavelengthScale = function( value ) { currentWavelengthScale = value; };
|
||||
eventsHandler.setCurrentTiling = function( value ) { currentTiling = value; };
|
||||
eventsHandler.setAutoRotate = function( value ) { autoRotate = value; };
|
||||
|
||||
eventsHandler.setup( );
|
||||
|
||||
|
||||
// expose FFT test helper
|
||||
const oceanTests = new OceanTests( getPrimaryPipeline );
|
||||
|
||||
window.testFft = async function( ) {
|
||||
|
||||
return oceanTests.testFft( );
|
||||
|
||||
};
|
||||
|
||||
|
||||
window.testSpectrum = async function( ) {
|
||||
|
||||
return oceanTests.testSpectrum( );
|
||||
|
||||
};
|
||||
|
||||
let autoRotate = true;
|
||||
|
||||
let lastAutoRotateTime = performance.now();
|
||||
|
||||
|
||||
async function frame( ) {
|
||||
|
||||
const now = performance.now();
|
||||
|
||||
if ( autoRotate ) {
|
||||
|
||||
const dt = ( now - lastAutoRotateTime ) / 1000;
|
||||
|
||||
const primary = getPrimaryPipeline();
|
||||
|
||||
if ( primary && primary.camera ) {
|
||||
|
||||
const rotationSpeed = 0.15;
|
||||
|
||||
primary.camera.yaw += rotationSpeed * dt;
|
||||
|
||||
primary.camera.update();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lastAutoRotateTime = now;
|
||||
|
||||
|
||||
const list = pipelines.slice( );
|
||||
|
||||
const run = async function( ) {
|
||||
|
||||
for ( const p of list ) {
|
||||
|
||||
await p.bindBuffers( );
|
||||
await p.execute( );
|
||||
|
||||
}
|
||||
|
||||
if ( scene && renderSystem ) {
|
||||
|
||||
renderSystem.render( scene );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
await run( );
|
||||
|
||||
requestAnimationFrame( frame );
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
console.error( error );
|
||||
statusElement.textContent = "Error while drawing ocean.";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
requestAnimationFrame( frame );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const app = new OceanApp( );
|
||||
|
||||
app.run( );
|
||||
Reference in New Issue
Block a user