From 743d3fc0ed8f32bcb820ffe55f665d3da2c38ec3 Mon Sep 17 00:00:00 2001 From: darzu Date: Mon, 15 Aug 2022 13:56:33 -0700 Subject: [PATCH 01/16] ocean experiments; including gradient --- public/shaders/std-ocean.wgsl | 50 ++++++++++++++++------------- src/game/game-hyperspace.ts | 23 ++++++++++--- src/game/ocean.ts | 8 +++-- src/render/gpu-helper.ts | 6 ++-- src/render/gpu-struct.ts | 1 + src/render/pipelines/std-compose.ts | 21 ++++++++---- src/render/pipelines/std-ocean.ts | 5 +++ 7 files changed, 76 insertions(+), 38 deletions(-) diff --git a/public/shaders/std-ocean.wgsl b/public/shaders/std-ocean.wgsl index 0a48c480..08dbede1 100644 --- a/public/shaders/std-ocean.wgsl +++ b/public/shaders/std-ocean.wgsl @@ -1,6 +1,7 @@ struct VertexOutput { // TODO(@darzu): change @location(0) normal : vec3, + // @location(0) @interpolate(flat) normal : vec3, @location(1) color : vec3, @location(2) worldPos: vec4, @location(3) uv: vec2, @@ -120,35 +121,39 @@ fn vert_main(input: VertexInput) -> VertexOutput { struct FragOut { @location(0) color: vec4, - @location(1) surface: vec2, + @location(1) normal: vec4, + @location(2) surface: vec2, } @fragment fn frag_main(input: VertexOutput) -> FragOut { let normal = normalize(input.normal); - var lightingColor: vec3 = vec3(0.0, 0.0, 0.0); - let unlit = 0u; - for (var i: u32 = 0u; i < scene.numPointLights; i++) { - let light = pointLights.ms[i]; - let toLight = light.position - input.worldPos.xyz; - let distance = length(toLight); - let attenuation = 1.0 / (light.constant + light.linear * distance + - light.quadratic * distance * distance); - let angle = clamp(dot(normalize(toLight), input.normal), 0.0, 1.0); - // XY is in (-1, 1) space, Z is in (0, 1) space - let posFromLight = (pointLights.ms[i].viewProj * input.worldPos).xyz; + // var lightingColor: vec3 = vec3(0.0, 0.0, 0.0); + // let unlit = 1u; + // for (var i: u32 = 0u; i < scene.numPointLights; i++) { + // let light = pointLights.ms[i]; + // let toLight = light.position - input.worldPos.xyz; + // let distance = length(toLight); + // let attenuation = 1.0 / (light.constant + light.linear * distance + + // light.quadratic * distance * distance); + // let angle = clamp(dot(normalize(toLight), input.normal), 0.0, 1.0); + // // XY is in (-1, 1) space, Z is in (0, 1) space + // let posFromLight = (pointLights.ms[i].viewProj * input.worldPos).xyz; - // Convert XY to (0, 1), Y is flipped because texture coords are Y-down. - let shadowPos = vec3(posFromLight.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5), - posFromLight.z - ); - let shadowVis = getShadowVis(shadowPos, input.normal, normalize(toLight), i); - //lightingColor = lightingColor + clamp(abs((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)), vec3(0.0), vec3(1.0)); - //lightingColor += light.ambient; - lightingColor = lightingColor + f32(1u - unlit) * ((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)); - } - let litColor = input.color * (lightingColor + vec3(f32(unlit))); + // // Convert XY to (0, 1), Y is flipped because texture coords are Y-down. + // let shadowPos = vec3(posFromLight.xy * vec2(0.5, -0.5) + vec2(0.5, 0.5), + // posFromLight.z + // ); + // let shadowVis = getShadowVis(shadowPos, input.normal, normalize(toLight), i); + // //lightingColor = lightingColor + clamp(abs((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)), vec3(0.0), vec3(1.0)); + // //lightingColor += light.ambient; + // lightingColor = lightingColor + f32(1u - unlit) * ((light.ambient * attenuation) + (light.diffuse * angle * attenuation * shadowVis)); + // } + // let litColor = input.color * (lightingColor + vec3(f32(unlit))); + + // let litColor = mix(vec3(0.1, 0.3, 0.8), vec3(0.015), input.uv.x); + let litColor = mix(vec3(0.1, 0.3, 0.8), vec3(0.1, 0.05, 0.1), input.uv.x); var out: FragOut; @@ -156,6 +161,7 @@ fn frag_main(input: VertexOutput) -> FragOut { out.surface.r = input.surface; out.surface.g = input.id; + out.normal = vec4(normal, 0.0); return out; } diff --git a/src/game/game-hyperspace.ts b/src/game/game-hyperspace.ts index 362763b1..bd8be1b4 100644 --- a/src/game/game-hyperspace.ts +++ b/src/game/game-hyperspace.ts @@ -11,7 +11,11 @@ import { shadowDepthTextures, shadowPipelines, } from "../render/pipelines/std-shadow.js"; -import { initStars, renderStars } from "../render/pipelines/std-stars.js"; +import { + emissionTexturePtr, + initStars, + renderStars, +} from "../render/pipelines/std-stars.js"; import { AssetsDef } from "./assets.js"; import { AuthorityDef, MeDef } from "../net/components.js"; import { createPlayer } from "./player.js"; @@ -28,6 +32,11 @@ import { createSpawner, SpawnerDef } from "./spawner.js"; import { tempVec3 } from "../temp-pool.js"; import { createDarkStarNow, STAR1_COLOR, STAR2_COLOR } from "./darkstar.js"; import { renderOceanPipe } from "../render/pipelines/std-ocean.js"; +import { + normalsTexturePtr, + positionsTexturePtr, + surfacesTexturePtr, +} from "../render/pipelines/std-scene.js"; // export let jfaMaxStep = VISUALIZE_JFA ? 0 : 999; @@ -81,12 +90,16 @@ export async function initHyperspaceGame(em: EntityManager) { "debugLoop" ); - const grid = [[...shadowDepthTextures]]; + // const grid = [[...shadowDepthTextures]]; // // // [oceanJfa._inputMaskTex, oceanJfa._uvMaskTex], // // // [oceanJfa.voronoiTex, shadowDepthTexture], // ]; + const grid = [ + [normalsTexturePtr, surfacesTexturePtr], + [positionsTexturePtr, emissionTexturePtr], + ]; // let grid = noiseGridFrame; // const grid = [[oceanJfa._voronoiTexs[0]], [oceanJfa._voronoiTexs[1]]]; @@ -101,11 +114,11 @@ export async function initHyperspaceGame(em: EntityManager) { stdRenderPipeline, renderOceanPipe, outlineRender, - //renderStars, - //...blurPipelines, + // renderStars, + // ...blurPipelines, postProcess, - //...(res.dev.showConsole ? gridCompose : []), + // ...(res.dev.showConsole ? gridCompose : []), ]; }, "hyperspaceGame" diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 3a207b00..7f4e7031 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -190,7 +190,7 @@ export async function initOcean() { 0.5 / 4.0, 1 ), - //createGerstnerWave(1, 0.5, randNormalVec2(vec2.create()), 0.5 / 1.0, 3), + // createGerstnerWave(0.7, 0.5, randNormalVec2(vec2.create()), 0.5 / 1.0, 1), ]; const uvToPos = (out: vec3, uv: vec2) => { @@ -257,8 +257,9 @@ export async function initOcean() { vec3.copy(outNorm, gNorm); // HACK: smooth out norm? - vec3.add(outNorm, outNorm, vec3.scale(tempVec3(), norm, 2.0)); - vec3.normalize(outNorm, outNorm); + vec3.copy(outNorm, norm); + // vec3.add(outNorm, outNorm, vec3.scale(tempVec3(), norm, 2.0)); + // vec3.normalize(outNorm, outNorm); }; // TODO(@darzu): hacky hacky way to do this @@ -369,6 +370,7 @@ EM.registerSystem( const forwardish = vec3.sub(tempVec3(), aheadPos, e.position); const newNorm = tempVec3(); res.ocean.uvToGerstnerDispAndNorm(tempVec3(), newNorm, e.uvPos); + // const newNorm = res.ocean.uvToNorm(tempVec3(), e.uvPos); quatFromUpForward(e.rotation, newNorm, forwardish); // console.log( // `UVDir ${[e.uvDir[0], e.uvDir[1]]} -> ${quatDbg(e.rotation)}` diff --git a/src/render/gpu-helper.ts b/src/render/gpu-helper.ts index eb930ecd..a29d3c4e 100644 --- a/src/render/gpu-helper.ts +++ b/src/render/gpu-helper.ts @@ -99,7 +99,7 @@ export function createRenderTextureToQuad( let fSnip = `return ${returnWgslType}(inPx);`; if (inputArity === 2 && outArity === 4) - fSnip = `return ${returnWgslType}(inPx.xy, 0.0, 0.0);`; + fSnip = `return ${returnWgslType}(vec2(inPx.xy), 0.0, 0.0);`; if (fragSnippet) { if (isFunction(fragSnippet)) @@ -125,7 +125,7 @@ export function createRenderTextureToQuad( ? `.xyzw` : never(inputArity); - return ` + const shader = ` ${libs ? libs.map((l) => shaders[l].code).join("\n") : ""} ${shaders["std-screen-quad-vert"].code} @@ -144,6 +144,8 @@ export function createRenderTextureToQuad( ${fSnip} } `; + // console.log(shader); + return shader; }; const pipeline = CY.createRenderPipeline(name, { globals: [ diff --git a/src/render/gpu-struct.ts b/src/render/gpu-struct.ts index 94f72e64..b5df1b98 100644 --- a/src/render/gpu-struct.ts +++ b/src/render/gpu-struct.ts @@ -62,6 +62,7 @@ export const TexTypeToElementArity: Partial< r32float: 1, rgba16float: 4, rg16float: 2, + rg16uint: 2, r16float: 1, r8unorm: 1, r8snorm: 1, diff --git a/src/render/pipelines/std-compose.ts b/src/render/pipelines/std-compose.ts index 9805ec64..2e3f90f5 100644 --- a/src/render/pipelines/std-compose.ts +++ b/src/render/pipelines/std-compose.ts @@ -21,13 +21,22 @@ export function createGridComposePipelines( let pipes: CyRenderPipelinePtr[] = []; - for (let ri = 0; ri < grid.length; ri++) { - for (let ci = 0; ci < grid[ri].length; ci++) { + const rCount = grid.length; + for (let ri = 0; ri < rCount; ri++) { + const cCount = grid[ri].length; + for (let ci = 0; ci < cCount; ci++) { const tex = grid[ri][ci]; - const xMin = uvStartX + ci * (uvWidth + padding); - const xMax = xMin + uvWidth; - const yMax = uvStartY - ri * (uvHeight + padding); - const yMin = yMax - uvHeight; + let xMin = uvStartX + ci * (uvWidth + padding); + let xMax = xMin + uvWidth; + let yMax = uvStartY - ri * (uvHeight + padding); + let yMin = yMax - uvHeight; + // HACK: when we're working with 2x2, we shrink the images for easier nav + // TODO(@darzu): this is the common case; we should support this in a more + // principled way + if (ci === 0 && cCount === 2) xMax -= 0.25; + if (ci === 1 && cCount === 2) xMin += 0.25; + if (ri === 0 && rCount === 2) yMin += 0.25; + if (ri === 1 && rCount === 2) yMax -= 0.25; pipes.push( createRenderTextureToQuad( `composeViews_${ci}x${ri}`, diff --git a/src/render/pipelines/std-ocean.ts b/src/render/pipelines/std-ocean.ts index 81298a88..2766f9c2 100644 --- a/src/render/pipelines/std-ocean.ts +++ b/src/render/pipelines/std-ocean.ts @@ -11,6 +11,7 @@ import { litTexturePtr, mainDepthTex, surfacesTexturePtr, + normalsTexturePtr, } from "./std-scene.js"; import { shadowDepthTextures } from "./std-shadow.js"; @@ -189,6 +190,10 @@ export const renderOceanPipe = CY.createRenderPipeline("oceanRender", { ptr: litTexturePtr, clear: "never", }, + { + ptr: normalsTexturePtr, + clear: "never", + }, { ptr: surfacesTexturePtr, clear: "never", From 398d29579d2e48c2f6141bbfd0c881bb408c8809 Mon Sep 17 00:00:00 2001 From: darzu Date: Wed, 17 Aug 2022 10:13:49 -0700 Subject: [PATCH 02/16] render notes --- notes/perf.txt | 10 +++++++++- notes/renderer.txt | 5 ++++- notes/rust_wasm.txt | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/notes/perf.txt b/notes/perf.txt index ade7750b..e212a710 100644 --- a/notes/perf.txt +++ b/notes/perf.txt @@ -161,4 +161,12 @@ Pipeline GPU stats from Doug: triRender, 1.012784ms, outlineRender, 2.091969ms, renderStars, 0.120638ms, - postProcess, 2.823797ms, \ No newline at end of file + postProcess, 2.823797ms, + +Animometer: + WebGL: + https://github.com/kenrussell/webgl-animometer + http://kenrussell.github.io/webgl-animometer/Animometer/tests/3d/webgl.html + http://sprig.land/webgl-animometer/Animometer/tests/3d/webgl.html + https://sprig.land/webgl-animometer/Animometer/tests/3d/webgl.html?webgl_version=2&use_ubos=1&use_multi_draw=1 + ?webgl_version=2&use_ubos=1&use_multi_draw=1 \ No newline at end of file diff --git a/notes/renderer.txt b/notes/renderer.txt index 9326aefc..c7ac6553 100644 --- a/notes/renderer.txt +++ b/notes/renderer.txt @@ -958,4 +958,7 @@ Anti-aliasing: "Sokpop: I built a 3D engine in 2D": https://www.youtube.com/watch?v=chtRPC1ISyA - example project: https://sokpop.itch.io/sokpop-fake-3d-demo \ No newline at end of file + example project: https://sokpop.itch.io/sokpop-fake-3d-demo + +Decals: + https://tuket.github.io/posts/2022-08-05-explosion-decals/ \ No newline at end of file diff --git a/notes/rust_wasm.txt b/notes/rust_wasm.txt index dad95555..d6947584 100644 --- a/notes/rust_wasm.txt +++ b/notes/rust_wasm.txt @@ -53,4 +53,7 @@ Rustcraft uses wgpu: https://github.com/dskart/rustcraft Mandelbrot perf with wasm: - https://blog.feather.systems/jekyll/update/2021/06/21/WasmPerformance.html \ No newline at end of file + https://blog.feather.systems/jekyll/update/2021/06/21/WasmPerformance.html + +Minimal example of animating the HTML5 canvas from C++ using OpenGL through WebAssembly + https://github.com/timhutton/opengl-canvas-wasm \ No newline at end of file From cfc028c66add959e5ee39f96c9dca8679b404668 Mon Sep 17 00:00:00 2001 From: darzu Date: Wed, 17 Aug 2022 10:15:17 -0700 Subject: [PATCH 03/16] std-gerstner.wgsl --- public/shaders/std-gerstner.wgsl | 19 +++++++++++++++++++ public/shaders/std-ocean.wgsl | 26 +++++--------------------- src/render/pipelines/std-ocean.ts | 1 + src/render/shader-loader.ts | 1 + 4 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 public/shaders/std-gerstner.wgsl diff --git a/public/shaders/std-gerstner.wgsl b/public/shaders/std-gerstner.wgsl new file mode 100644 index 00000000..4529dea2 --- /dev/null +++ b/public/shaders/std-gerstner.wgsl @@ -0,0 +1,19 @@ +fn gerstner(uv: vec2, t: f32) -> mat2x3 { + var displacement = vec3(0.0, 0.0, 0.0); + var normal = vec3(0.0, 0.0, 0.0); + for (var i = 0u; i < scene.numGerstnerWaves; i++) { + let wave = gerstnerWaves.ms[i]; + let dot_w_d_uv_phi_t = dot(wave.w * wave.D, uv) + wave.phi * t; + let _cos = cos(dot_w_d_uv_phi_t); + let _sin = sin(dot_w_d_uv_phi_t); + displacement.x += wave.Q * wave.A + wave.D.x * _cos; + displacement.z += wave.Q * wave.A + wave.D.y * _cos; + displacement.y += wave.A * _sin; + normal.x += -1.0 * wave.D.x * wave.w * wave.A * _cos; + normal.z += -1.0 * wave.D.y * wave.w * wave.A * _cos; + normal.y += wave.Q * wave.w * wave.A * _sin; + } + normal.y = 1.0 - normal.y; + normalize(normal); + return mat2x3(displacement, normal); +} \ No newline at end of file diff --git a/public/shaders/std-ocean.wgsl b/public/shaders/std-ocean.wgsl index 08dbede1..6851cccc 100644 --- a/public/shaders/std-ocean.wgsl +++ b/public/shaders/std-ocean.wgsl @@ -57,25 +57,6 @@ fn getShadowVis(shadowPos: vec3, normal: vec3, lightDir: vec3, in return visibility; } -fn gerstner(uv: vec2, t: f32) -> mat2x3 { - var displacement = vec3(0.0, 0.0, 0.0); - var normal = vec3(0.0, 0.0, 0.0); - for (var i = 0u; i < scene.numGerstnerWaves; i++) { - let wave = gerstnerWaves.ms[i]; - displacement = displacement + - vec3(wave.Q * wave.A + wave.D.x * cos(dot(wave.w * wave.D, uv) + wave.phi * t), - wave.A * sin(dot(wave.w * wave.D, uv) + wave.phi * t), - wave.Q * wave.A + wave.D.y * cos(dot(wave.w * wave.D, uv) + wave.phi * t)); - normal = normal + - vec3(-1.0 * wave.D.x * wave.w * wave.A * cos(wave.w * dot(wave.D, uv) + wave.phi * t), - wave.Q * wave.w * wave.A * sin(wave.w * dot(wave.D, uv) + wave.phi * t), - -1.0 * wave.D.y * wave.w * wave.A * cos(wave.w * dot(wave.D, uv) + wave.phi * t)); - } - normal.y = 1.0 - normal.y; - normalize(normal); - return mat2x3(displacement, normal); -} - @vertex fn vert_main(input: VertexInput) -> VertexOutput { let position = input.position; @@ -89,10 +70,13 @@ fn vert_main(input: VertexInput) -> VertexOutput { // TODO(@darzu): we're not totally sure about x,y,z vs normal,tangent,perp let surfBasis = mat3x3(perp, normal, tangent); let gerst = gerstner(input.uv * 1000, scene.time * .001); + let displacedPos = position + surfBasis * gerst[0]; - //let displacedPos = flattenedPos + gerst[0]; let gerstNormal = surfBasis * gerst[1]; - //let gerstNormal = gerst[1]; + // FLATTENED: + // let displacedPos = flattenedPos + gerst[0]; + // let gerstNormal = gerst[1]; + // let displacedPos = flattenedPos + wave1; // let displacedPos = position + wave0; // let displacedPos = position + wave1; diff --git a/src/render/pipelines/std-ocean.ts b/src/render/pipelines/std-ocean.ts index 2766f9c2..8cc20305 100644 --- a/src/render/pipelines/std-ocean.ts +++ b/src/render/pipelines/std-ocean.ts @@ -202,6 +202,7 @@ export const renderOceanPipe = CY.createRenderPipeline("oceanRender", { depthStencil: mainDepthTex, shader: (shaderSet) => ` ${shaderSet["std-rand"].code} + ${shaderSet["std-gerstner"].code} ${shaderSet["std-ocean"].code} `, }); diff --git a/src/render/shader-loader.ts b/src/render/shader-loader.ts index 16e1d972..fb0aae76 100644 --- a/src/render/shader-loader.ts +++ b/src/render/shader-loader.ts @@ -18,6 +18,7 @@ export const ShaderPaths = [ "std-screen-quad-vert", "std-rand", "std-stars", + "std-gerstner", ] as const; export type ShaderName = typeof ShaderPaths[number]; From 17ae817cbcb489da3ec0cbafb966e3106ac14087 Mon Sep 17 00:00:00 2001 From: darzu Date: Wed, 17 Aug 2022 15:35:07 -0700 Subject: [PATCH 04/16] subdividing quad meshes --- notes/water.txt | 8 +- public/shaders/std-ocean.wgsl | 10 +- public/shaders/std-outline.wgsl | 2 +- src/game/assets.ts | 182 +++++++++++++++++++++++------- src/game/ocean.ts | 41 ++++--- src/render/pipelines/std-ocean.ts | 2 +- 6 files changed, 175 insertions(+), 70 deletions(-) diff --git a/notes/water.txt b/notes/water.txt index 32c9141b..d3207634 100644 --- a/notes/water.txt +++ b/notes/water.txt @@ -71,4 +71,10 @@ Falconeer uses "gerstner waves", 7 iterations: https://www.youtube.com/watch?v=BJSMVvZMQ1w Cool interactive waterfall: - https://twitter.com/Cyanilux/status/1190253359703482369 \ No newline at end of file + https://twitter.com/Cyanilux/status/1190253359703482369 + +Tons of water links: + http://vterrain.org/Water/index.html + +GPU gems water, gerstner waves: + https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1-effective-water-simulation-physical-models \ No newline at end of file diff --git a/public/shaders/std-ocean.wgsl b/public/shaders/std-ocean.wgsl index 6851cccc..f22124df 100644 --- a/public/shaders/std-ocean.wgsl +++ b/public/shaders/std-ocean.wgsl @@ -2,7 +2,7 @@ struct VertexOutput { // TODO(@darzu): change @location(0) normal : vec3, // @location(0) @interpolate(flat) normal : vec3, - @location(1) color : vec3, + @location(1) @interpolate(flat) color : vec3, @location(2) worldPos: vec4, @location(3) uv: vec2, @location(4) @interpolate(flat) surface: u32, @@ -71,11 +71,11 @@ fn vert_main(input: VertexInput) -> VertexOutput { let surfBasis = mat3x3(perp, normal, tangent); let gerst = gerstner(input.uv * 1000, scene.time * .001); - let displacedPos = position + surfBasis * gerst[0]; - let gerstNormal = surfBasis * gerst[1]; + // let displacedPos = position + surfBasis * gerst[0]; + // let gerstNormal = surfBasis * gerst[1]; // FLATTENED: - // let displacedPos = flattenedPos + gerst[0]; - // let gerstNormal = gerst[1]; + let displacedPos = flattenedPos + gerst[0]; + let gerstNormal = gerst[1]; // let displacedPos = flattenedPos + wave1; // let displacedPos = position + wave0; diff --git a/public/shaders/std-outline.wgsl b/public/shaders/std-outline.wgsl index cb935613..7e79e142 100644 --- a/public/shaders/std-outline.wgsl +++ b/public/shaders/std-outline.wgsl @@ -95,7 +95,7 @@ fn frag_main(@location(0) uv : vec2) -> @location(0) vec4 { color *= 1.0 + edgeLum; // TODO(@darzu): DEBUG WIREFRAME // color *= 2.0; - // color = vec3(0.8); + color = vec3(0.8); } // DEBUG WIREFRAME // else { diff --git a/src/game/assets.ts b/src/game/assets.ts index dfe0e7ac..a61d072f 100644 --- a/src/game/assets.ts +++ b/src/game/assets.ts @@ -168,8 +168,28 @@ const MeshModify: Partial<{ // console.log("getMeshAsGrid failed!"); // console.error(e); // } + const xLen = grid.length; const yLen = grid[0].length; + + // redo quad indices based on the grid (optional?) + for (let xi = 0; xi < xLen - 1; xi++) { + for (let yi = 0; yi < yLen - 1; yi++) { + const qi = gridXYtoQuad(xi, yi); + vec4.copy(m.quad[qi], [ + grid[xi][yi], + grid[xi + 1][yi], + grid[xi + 1][yi + 1], + grid[xi][yi + 1], + ]); + } + } + function gridXYtoQuad(xi: number, yi: number): number { + const qi = yi + xi * (yLen - 1); + assert(qi < m.quad.length, "quads and grid mismatch!"); + return qi; + } + // console.log(`xLen:${xLen},yLen:${yLen}`); const uvs = m.pos.map((_, vi) => vec2.create()); m.uvs = uvs; @@ -182,45 +202,135 @@ const MeshModify: Partial<{ // instead of per quad, for vertex displacement (e.g. waves) // purposes? - //set tangents + // set tangents and normals m.tangents = m.pos.map(() => vec3.create()); m.normals = m.pos.map(() => vec3.create()); - for (let xIndex = 0; xIndex < grid.length; xIndex++) { - for (let yIndex = 0; yIndex < grid[0].length; yIndex++) { + for (let xi = 0; xi < grid.length; xi++) { + for (let yi = 0; yi < grid[0].length; yi++) { let normal: vec3; let tangent: vec3; - if (xIndex + 1 < grid.length && yIndex + 1 < grid[0].length) { - const pos = m.pos[grid[xIndex][yIndex]]; - const posNX = m.pos[grid[xIndex + 1][yIndex]]; - const posNY = m.pos[grid[xIndex][yIndex + 1]]; + if (xi + 1 < grid.length && yi + 1 < grid[0].length) { + const pos = m.pos[grid[xi][yi]]; + const posNX = m.pos[grid[xi + 1][yi]]; + const posNY = m.pos[grid[xi][yi + 1]]; normal = computeTriangleNormal(pos, posNX, posNY); - tangent = vec3.sub(m.tangents[grid[xIndex][yIndex]], posNX, pos); + tangent = vec3.sub(m.tangents[grid[xi][yi]], posNX, pos); vec3.normalize(tangent, tangent); - } else if (xIndex + 1 >= grid.length) { - normal = m.normals[grid[xIndex - 1][yIndex]]; - tangent = m.tangents[grid[xIndex - 1][yIndex]]; - } else if (yIndex + 1 >= grid[0].length) { - normal = m.normals[grid[xIndex][yIndex - 1]]; - tangent = m.tangents[grid[xIndex][yIndex - 1]]; + } else if (xi + 1 >= grid.length) { + normal = m.normals[grid[xi - 1][yi]]; + tangent = m.tangents[grid[xi - 1][yi]]; + } else if (yi + 1 >= grid[0].length) { + normal = m.normals[grid[xi][yi - 1]]; + tangent = m.tangents[grid[xi][yi - 1]]; } else { assert(false); } - vec3.copy(m.normals[grid[xIndex][yIndex]], normal); - vec3.copy(m.tangents[grid[xIndex][yIndex]], tangent); + vec3.copy(m.normals[grid[xi][yi]], normal); + vec3.copy(m.tangents[grid[xi][yi]], tangent); + } + } + + // TODO(@darzu): testing subdivide + // for (let i = 0; i < 100; i++) { + // subdivideQuad(i); + // } + let startXi = Math.floor(grid.length * 0.3); + let endXi = Math.floor(grid.length * 0.6); + let startYi = Math.floor(grid[0].length * 0.2); + let endYi = Math.floor(grid[0].length * 0.8); + for (let xi = startXi; xi < endXi; xi++) { + for (let yi = startYi; yi < endYi; yi++) { + let recurse = 0; + if (grid.length * 0.35 < xi && xi < grid.length * 0.5) recurse = 1; + // else if (grid.length * 0.4 < xi && xi < grid.length * 0.45) recurse = 2; + subdivideQuad(gridXYtoQuad(xi, yi), recurse); } } - // console.dir(uvs); - // console.log(` - // X: - // ${max(uvs.map((uv) => uv[0]))} - // ${min(uvs.map((uv) => uv[0]))} - // Y: - // ${max(uvs.map((uv) => uv[1]))} - // ${min(uvs.map((uv) => uv[1]))} - // `); + // console.dir(m); + + // console.dir({ + // uvMin: [min(m.uvs.map((a) => a[0])), min(m.uvs.map((a) => a[1]))], + // uvMax: [max(m.uvs.map((a) => a[0])), max(m.uvs.map((a) => a[1]))], + // }); + + // console.dir(m.uvs); + // console.dir({ minX, maxX, minZ, maxZ }); + return m; + + function subdivideQuad(quadIdx: number, recurse = 0) { + // maintain: colors, normals, pos, quad, surfaceIds, tangents, uvs + let [tl, tr, br, bl] = m.quad[quadIdx]; + let tm = m.pos.push(midPos(tl, tr)) - 1; + let rm = m.pos.push(midPos(tr, br)) - 1; + let bm = m.pos.push(midPos(br, bl)) - 1; + let lm = m.pos.push(midPos(bl, tl)) - 1; + let mm = m.pos.push(midPos(tm, bm)) - 1; + + // all quads are: top-left, top-right, bottom-right, bottom-left + let q_tl = quadIdx; // re-use + m.quad[q_tl] = [tl, tm, mm, lm]; + let q_tr = m.quad.push([tm, tr, rm, mm]) - 1; + let q_br = m.quad.push([mm, rm, br, bm]) - 1; + let q_bl = m.quad.push([lm, mm, bm, bl]) - 1; + + // init per-vertex (set later) + for (let p of [tm, rm, bm, lm, mm]) { + m.uvs![p] = vec2.create(); + m.tangents![p] = vec3.create(); + m.normals![p] = vec3.create(); + } + + // update quad properties + for (let q of [q_tl, q_tr, q_br, q_bl]) { + // per quad: + m.colors[q] = vec3.clone(m.colors[quadIdx]); + m.surfaceIds![q] = q; + // per provoking vert: + let vs = m.quad[q]; + m.normals![vs[0]] = computeTriangleNormal( + m.pos[vs[0]], + m.pos[vs[1]], + m.pos[vs[2]] + ); + m.tangents![vs[0]] = vec3.sub( + vec3.create(), + m.pos[vs[1]], + m.pos[vs[0]] + ); + vec3.normalize(m.tangents![vs[0]], m.tangents![vs[0]]); + } + // per vertex: + m.uvs![tm] = midUV(tl, tr); + m.uvs![rm] = midUV(tr, br); + m.uvs![bm] = midUV(br, bl); + m.uvs![lm] = midUV(bl, tl); + m.uvs![mm] = midUV(tm, bm); + + // recurse? + if (recurse > 0) { + subdivideQuad(q_tl, recurse - 1); + subdivideQuad(q_tr, recurse - 1); + subdivideQuad(q_br, recurse - 1); + subdivideQuad(q_bl, recurse - 1); + } + + function midPos(p0: number, p1: number): vec3 { + return vec3.fromValues( + (m.pos[p0][0] + m.pos[p1][0]) * 0.5, + (m.pos[p0][1] + m.pos[p1][1]) * 0.5, + (m.pos[p0][2] + m.pos[p1][2]) * 0.5 + ); + } + function midUV(p0: number, p1: number): vec2 { + return vec2.fromValues( + (m.uvs![p0][0] + m.uvs![p1][0]) * 0.5, + (m.uvs![p0][1] + m.uvs![p1][1]) * 0.5 + ); + } + } function setUV( x: number, @@ -252,14 +362,6 @@ const MeshModify: Partial<{ ]; setUV(nX, nY, dir, newDist, branch); } - // console.dir({ - // uvMin: [min(m.uvs.map((a) => a[0])), min(m.uvs.map((a) => a[1]))], - // uvMax: [max(m.uvs.map((a) => a[0])), max(m.uvs.map((a) => a[1]))], - // }); - - // console.dir(m.uvs); - // console.dir({ minX, maxX, minZ, maxZ }); - return m; }, }; @@ -714,13 +816,13 @@ onInit(async (em) => { const assetsPromise = loadAssets(renderer.renderer); assetLoader.promise = assetsPromise; - try { - const result = await assetsPromise; - em.addSingletonComponent(AssetsDef, result); - } catch (failureReason) { - // TODO(@darzu): fail more gracefully - throw `Failed to load assets: ${failureReason}`; - } + // try { + const result = await assetsPromise; + em.addSingletonComponent(AssetsDef, result); + // } catch (failureReason) { + // // TODO(@darzu): fail more gracefully + // throw `Failed to load assets: ${failureReason}`; + // } }); async function loadTxtInternal(relPath: string): Promise { diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 7f4e7031..05fa5d53 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -167,30 +167,25 @@ export async function initOcean() { ); // console.log("adding OceanDef"); + const dirs = [ + // randNormalVec2(vec2.create()), + // randNormalVec2(vec2.create()), + // randNormalVec2(vec2.create()), + // randNormalVec2(vec2.create()), + vec2.normalize(vec2.create(), [0.5, 0.5]), + vec2.normalize(vec2.create(), [0.5, 0.5]), + vec2.normalize(vec2.create(), [0.5, 0.5]), + vec2.normalize(vec2.create(), [0.5, 0.5]), + ]; const gerstnerWaves = [ - createGerstnerWave( - 1.08 * 2.0, - 10 * 0.5, - randNormalVec2(vec2.create()), - 0.5 / 20.0, - 0.5 - ), - createGerstnerWave( - 1.08 * 2.0, - 10 * 0.5, - randNormalVec2(vec2.create()), - 0.5 / 20.0, - 0.5 - ), - createGerstnerWave( - 1.08 * 2.0, - 2 * 0.5, - randNormalVec2(vec2.create()), - 0.5 / 4.0, - 1 - ), - // createGerstnerWave(0.7, 0.5, randNormalVec2(vec2.create()), 0.5 / 1.0, 1), + // BAD: + // createGerstnerWave(2, 5, dirs[0], 0.01, 0.5), + // createGerstnerWave(1.08 * 2.0, 10 * 0.5, dirs[1], 0.5 / 20.0, 0.5), + // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), + // + // GOOD: + createGerstnerWave(2, 1, dirs[2], 0.5, 1), ]; const uvToPos = (out: vec3, uv: vec2) => { @@ -244,6 +239,8 @@ export async function initOcean() { // outDisp[1] = pos[1] + disp[1]; // outDisp[2] = pos[2] + disp[2] * 0.5; vec3.add(outDisp, pos, disp); + // TODO(@darzu): HACK + vec3.copy(outDisp, pos); const gNorm = vec3.add( tempVec3(), diff --git a/src/render/pipelines/std-ocean.ts b/src/render/pipelines/std-ocean.ts index 8cc20305..30340232 100644 --- a/src/render/pipelines/std-ocean.ts +++ b/src/render/pipelines/std-ocean.ts @@ -15,7 +15,7 @@ import { } from "./std-scene.js"; import { shadowDepthTextures } from "./std-shadow.js"; -const MAX_OCEAN_VERTS = 10000; +const MAX_OCEAN_VERTS = 65000; // capped by uint16 max const MAX_OCEAN_MESHES = 1; // TODO(@darzu): change From a07fa7c26a8f1e794b4eed34661a6da293d845e0 Mon Sep 17 00:00:00 2001 From: darzu Date: Sun, 21 Aug 2022 09:57:32 -0600 Subject: [PATCH 05/16] reasonable-ish waves --- notes/water.txt | 5 ++++- src/game/ocean.ts | 53 ++++++++++++++++++++++++++++++++++++++++------- src/math.ts | 3 +++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/notes/water.txt b/notes/water.txt index d3207634..b371dde6 100644 --- a/notes/water.txt +++ b/notes/water.txt @@ -77,4 +77,7 @@ Tons of water links: http://vterrain.org/Water/index.html GPU gems water, gerstner waves: - https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1-effective-water-simulation-physical-models \ No newline at end of file + https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1-effective-water-simulation-physical-models + +Sea of Thieves (not very useful): + https://www.youtube.com/watch?v=9nxlmCq4220 \ No newline at end of file diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 05fa5d53..e7121473 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -4,7 +4,7 @@ import { createRef, Ref } from "../em_helpers.js"; import { EM, EntityManager } from "../entity-manager.js"; import { vec3, vec2, mat3, mat4 } from "../gl-matrix.js"; import { InputsDef } from "../inputs.js"; -import { clamp } from "../math.js"; +import { clamp, mathMix } from "../math.js"; import { PhysicsParentDef, PositionDef, @@ -167,16 +167,31 @@ export async function initOcean() { ); // console.log("adding OceanDef"); - const dirs = [ + let dirs: vec2[] = [ // randNormalVec2(vec2.create()), // randNormalVec2(vec2.create()), // randNormalVec2(vec2.create()), // randNormalVec2(vec2.create()), - vec2.normalize(vec2.create(), [0.5, 0.5]), - vec2.normalize(vec2.create(), [0.5, 0.5]), - vec2.normalize(vec2.create(), [0.5, 0.5]), - vec2.normalize(vec2.create(), [0.5, 0.5]), + // vec2.normalize(vec2.create(), [0.5, 0.5]), + // vec2.normalize(vec2.create(), [0.5, 0.5]), + // vec2.normalize(vec2.create(), [0.5, 0.5]), + // vec2.normalize(vec2.create(), [0.5, 0.5]), ]; + dirs[0] = randNormalVec2(vec2.create()); + let lastDir = dirs[0]; + while (dirs.length < 10) { + const nextDir = vec2.rotate( + vec2.create(), + lastDir, + vec2.ZEROS, + (Math.PI + 1.0) * 0.5 + ); + dirs.push(nextDir); + lastDir = nextDir; + } + + const steepness = 0.1; + const numWaves = 3; const gerstnerWaves = [ // BAD: @@ -185,7 +200,16 @@ export async function initOcean() { // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), // // GOOD: - createGerstnerWave(2, 1, dirs[2], 0.5, 1), + createGerstnerWave(steepness, 2, dirs[1], 0.3, 0.8, numWaves), + createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), + createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), + + // Maybe: + // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), + // createGerstnerWave(steepness, 0.75, dirs[2], 2.0, 0.7, 5), + // createGerstnerWave(steepness, 0.65, dirs[3], 0.59, 0.9, 5), + // createGerstnerWave(steepness, 0.55, dirs[4], 0.1, 1.2, 5), + // createGerstnerWave(steepness, 0.33, dirs[5], 0.8, 2.0, 5), ]; const uvToPos = (out: vec3, uv: vec2) => { @@ -254,7 +278,8 @@ export async function initOcean() { vec3.copy(outNorm, gNorm); // HACK: smooth out norm? - vec3.copy(outNorm, norm); + // vec3.copy(outNorm, norm); + vec3.set(outNorm, 0, 1, 0); // vec3.add(outNorm, outNorm, vec3.scale(tempVec3(), norm, 2.0)); // vec3.normalize(outNorm, outNorm); }; @@ -379,6 +404,18 @@ EM.registerSystem( ); function createGerstnerWave( + steepness: number, + amplitude: number, + direction: vec2, + wavelength: number, + speed: number, + numWaves: number +) { + const Q = mathMix(0, 1 / (wavelength * amplitude * numWaves), steepness); + console.log(`Q: ${Q}`); + return _createGerstnerWave(Q, amplitude, direction, wavelength, speed); +} +function _createGerstnerWave( Q: number, A: number, D: vec2, diff --git a/src/math.ts b/src/math.ts index d07b74b7..06991840 100644 --- a/src/math.ts +++ b/src/math.ts @@ -57,6 +57,9 @@ export function mathMap( const progress = (n - inMin) / (inMax - inMin); return progress * (outMax - outMin) + outMin; } +export function mathMix(a: number, b: number, p: number) { + return a * (1 - p) + b * p; +} export function mathMapNEase( n: number, inMin: number, From 67825fc685255f159acf1520e01d1915fd1ea383 Mon Sep 17 00:00:00 2001 From: darzu Date: Sun, 21 Aug 2022 10:19:39 -0600 Subject: [PATCH 06/16] reviving ghost --- src/game/game-hyperspace.ts | 18 +++++++++++++----- src/game/game-sandbox.ts | 7 +++++++ src/game/ocean.ts | 8 +++++--- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/game/game-hyperspace.ts b/src/game/game-hyperspace.ts index bd8be1b4..1884078d 100644 --- a/src/game/game-hyperspace.ts +++ b/src/game/game-hyperspace.ts @@ -26,7 +26,7 @@ import { noisePipes } from "../render/pipelines/std-noise.js"; import { DevConsoleDef } from "../console.js"; import { initOcean, OceanDef, oceanJfa, UVPosDef, UVDirDef } from "./ocean.js"; import { asyncTimeout } from "../util.js"; -import { vec2, vec3 } from "../gl-matrix.js"; +import { quat, vec2, vec3 } from "../gl-matrix.js"; import { AnimateToDef, EASE_INQUAD } from "../animate-to.js"; import { createSpawner, SpawnerDef } from "./spawner.js"; import { tempVec3 } from "../temp-pool.js"; @@ -37,6 +37,7 @@ import { positionsTexturePtr, surfacesTexturePtr, } from "../render/pipelines/std-scene.js"; +import { createGhost } from "./game-sandbox.js"; // export let jfaMaxStep = VISUALIZE_JFA ? 0 : 999; @@ -126,10 +127,10 @@ export async function initHyperspaceGame(em: EntityManager) { const res = await em.whenResources(AssetsDef, RendererDef); - // const ghost = createGhost(em); - // em.ensureComponentOn(ghost, RenderableConstructDef, res.assets.cube.proto); - // ghost.controllable.speed *= 3; - // ghost.controllable.sprintMul *= 3; + const ghost = createGhost(em); + em.ensureComponentOn(ghost, RenderableConstructDef, res.assets.cube.proto); + ghost.controllable.speed *= 3; + ghost.controllable.sprintMul *= 3; { // // debug camera @@ -144,6 +145,13 @@ export async function initHyperspaceGame(em: EntityManager) { // vec3.copy(g.cameraFollow.positionOffset, [0.0, 0.0, 0.0]); // g.cameraFollow.yawOffset = 0.0; // g.cameraFollow.pitchOffset = -0.486; + + let g = ghost; + vec3.copy(g.position, [-6.25, -45.38, -20.86]); + quat.copy(g.rotation, [0.0, 0.76, 0.0, 0.65]); + vec3.copy(g.cameraFollow.positionOffset, [2.0, 2.0, 8.0]); + g.cameraFollow.yawOffset = 0.0; + g.cameraFollow.pitchOffset = -0.621; } // one-time GPU jobs diff --git a/src/game/game-sandbox.ts b/src/game/game-sandbox.ts index a37f2b06..dde9ebdb 100644 --- a/src/game/game-sandbox.ts +++ b/src/game/game-sandbox.ts @@ -66,6 +66,7 @@ import { ControllableDef } from "./controllable.js"; import { GlobalCursor3dDef } from "./cursor.js"; import { ForceDef, SpringGridDef } from "./spring.js"; import { TextDef } from "./ui.js"; +import { AuthorityDef, MeDef } from "../net/components.js"; export const GhostDef = EM.defineComponent("ghost", () => ({})); @@ -82,6 +83,12 @@ export function createGhost(em: EntityManager) { // quat.rotateY(g.rotation, quat.IDENTITY, (-5 * Math.PI) / 8); // quat.rotateX(g.cameraFollow.rotationOffset, quat.IDENTITY, -Math.PI / 8); em.ensureComponentOn(g, LinearVelocityDef); + const res = em.entities.get(0)!; + if (MeDef.isOn(res)) { + em.ensureComponentOn(g, AuthorityDef, res.me.pid); + } else { + console.warn(`No MeDef when creating ghost!`); + } return g; } diff --git a/src/game/ocean.ts b/src/game/ocean.ts index e7121473..3e0dfe1f 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -177,7 +177,9 @@ export async function initOcean() { // vec2.normalize(vec2.create(), [0.5, 0.5]), // vec2.normalize(vec2.create(), [0.5, 0.5]), ]; - dirs[0] = randNormalVec2(vec2.create()); + // dirs[0] = randNormalVec2(vec2.create()); + dirs[0] = vec2.fromValues(0.5, 0.5); + vec2.normalize(dirs[0], dirs[0]); let lastDir = dirs[0]; while (dirs.length < 10) { const nextDir = vec2.rotate( @@ -201,8 +203,8 @@ export async function initOcean() { // // GOOD: createGerstnerWave(steepness, 2, dirs[1], 0.3, 0.8, numWaves), - createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), - createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), + // createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), + // createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), // Maybe: // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), From 90443bd69f737d7c016a76e8a09b2cf9635585d1 Mon Sep 17 00:00:00 2001 From: darzu Date: Sun, 21 Aug 2022 10:50:56 -0600 Subject: [PATCH 07/16] ghost works better! --- src/game/game-hyperspace.ts | 9 ++++----- src/game/game-sandbox.ts | 17 ++++++++++++----- src/game/ocean.ts | 4 ++-- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/game/game-hyperspace.ts b/src/game/game-hyperspace.ts index 1884078d..c52c8faf 100644 --- a/src/game/game-hyperspace.ts +++ b/src/game/game-hyperspace.ts @@ -128,10 +128,9 @@ export async function initHyperspaceGame(em: EntityManager) { const res = await em.whenResources(AssetsDef, RendererDef); const ghost = createGhost(em); - em.ensureComponentOn(ghost, RenderableConstructDef, res.assets.cube.proto); + // em.ensureComponentOn(ghost, RenderableConstructDef, res.assets.cube.proto); ghost.controllable.speed *= 3; ghost.controllable.sprintMul *= 3; - { // // debug camera // vec3.copy(ghost.position, [-185.02, 66.25, -69.04]); @@ -147,11 +146,11 @@ export async function initHyperspaceGame(em: EntityManager) { // g.cameraFollow.pitchOffset = -0.486; let g = ghost; - vec3.copy(g.position, [-6.25, -45.38, -20.86]); - quat.copy(g.rotation, [0.0, 0.76, 0.0, 0.65]); + vec3.copy(g.position, [-463.91, 8.79, 123.46]); + quat.copy(g.rotation, [0.0, 1.0, 0.0, 0.04]); vec3.copy(g.cameraFollow.positionOffset, [2.0, 2.0, 8.0]); g.cameraFollow.yawOffset = 0.0; - g.cameraFollow.pitchOffset = -0.621; + g.cameraFollow.pitchOffset = -0.659; } // one-time GPU jobs diff --git a/src/game/game-sandbox.ts b/src/game/game-sandbox.ts index dde9ebdb..fbb8bac8 100644 --- a/src/game/game-sandbox.ts +++ b/src/game/game-sandbox.ts @@ -83,12 +83,19 @@ export function createGhost(em: EntityManager) { // quat.rotateY(g.rotation, quat.IDENTITY, (-5 * Math.PI) / 8); // quat.rotateX(g.cameraFollow.rotationOffset, quat.IDENTITY, -Math.PI / 8); em.ensureComponentOn(g, LinearVelocityDef); - const res = em.entities.get(0)!; - if (MeDef.isOn(res)) { + + em.whenResources(RendererDef, AssetsDef).then((res) => { + em.ensureComponentOn( + g, + RenderableConstructDef, + res.assets.cube.proto, + true + // false + ); + }); + em.whenResources(MeDef).then((res) => { em.ensureComponentOn(g, AuthorityDef, res.me.pid); - } else { - console.warn(`No MeDef when creating ghost!`); - } + }); return g; } diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 3e0dfe1f..3eafe6eb 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -203,8 +203,8 @@ export async function initOcean() { // // GOOD: createGerstnerWave(steepness, 2, dirs[1], 0.3, 0.8, numWaves), - // createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), - // createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), + createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), + createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), // Maybe: // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), From 2be065da58c8a41497705270d83a7c4867184b44 Mon Sep 17 00:00:00 2001 From: darzu Date: Sun, 21 Aug 2022 11:04:20 -0600 Subject: [PATCH 08/16] smooth waves --- src/game/ocean.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 3eafe6eb..3acf256c 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -202,9 +202,9 @@ export async function initOcean() { // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), // // GOOD: - createGerstnerWave(steepness, 2, dirs[1], 0.3, 0.8, numWaves), - createGerstnerWave(steepness, 1, dirs[2], 0.5, 1, numWaves), - createGerstnerWave(steepness, 0.5, dirs[3], 0.8, 1.3, numWaves), + createGerstnerWave(steepness, 2, dirs[1], 0.1, 0.3, numWaves), + createGerstnerWave(steepness, 1, dirs[2], 0.2, 0.5, numWaves), + createGerstnerWave(steepness, 0.5, dirs[3], 0.4, 1.3, numWaves), // Maybe: // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), From e92fd54feb3f04f20510bfa707f60b4488d29f08 Mon Sep 17 00:00:00 2001 From: darzu Date: Sun, 21 Aug 2022 16:44:55 -0600 Subject: [PATCH 09/16] damn its hard to get good gerstners --- public/shaders/std-gerstner.wgsl | 21 +++++++++++++----- src/game/ocean.ts | 37 +++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/public/shaders/std-gerstner.wgsl b/public/shaders/std-gerstner.wgsl index 4529dea2..d1a06aba 100644 --- a/public/shaders/std-gerstner.wgsl +++ b/public/shaders/std-gerstner.wgsl @@ -1,16 +1,27 @@ +fn rotateDir(a: vec2, rad: f32) -> vec2 { + let sinC = sin(rad); + let cosC = cos(rad); + return vec2( + a.x * cosC - a.y * sinC, + a.x * sinC + a.y * cosC + ); +} + fn gerstner(uv: vec2, t: f32) -> mat2x3 { var displacement = vec3(0.0, 0.0, 0.0); var normal = vec3(0.0, 0.0, 0.0); for (var i = 0u; i < scene.numGerstnerWaves; i++) { let wave = gerstnerWaves.ms[i]; - let dot_w_d_uv_phi_t = dot(wave.w * wave.D, uv) + wave.phi * t; + // let D = rotateDir(wave.D, 0.7); + let D = wave.D; + let dot_w_d_uv_phi_t = dot(wave.w * D, uv) + wave.phi * t; let _cos = cos(dot_w_d_uv_phi_t); let _sin = sin(dot_w_d_uv_phi_t); - displacement.x += wave.Q * wave.A + wave.D.x * _cos; - displacement.z += wave.Q * wave.A + wave.D.y * _cos; + displacement.x += wave.Q * wave.A + D.x * _cos; + displacement.z += wave.Q * wave.A + D.y * _cos; displacement.y += wave.A * _sin; - normal.x += -1.0 * wave.D.x * wave.w * wave.A * _cos; - normal.z += -1.0 * wave.D.y * wave.w * wave.A * _cos; + normal.x += -1.0 * D.x * wave.w * wave.A * _cos; + normal.z += -1.0 * D.y * wave.w * wave.A * _cos; normal.y += wave.Q * wave.w * wave.A * _sin; } normal.y = 1.0 - normal.y; diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 3acf256c..f890f828 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -4,7 +4,7 @@ import { createRef, Ref } from "../em_helpers.js"; import { EM, EntityManager } from "../entity-manager.js"; import { vec3, vec2, mat3, mat4 } from "../gl-matrix.js"; import { InputsDef } from "../inputs.js"; -import { clamp, mathMix } from "../math.js"; +import { clamp, mathMap, mathMix } from "../math.js"; import { PhysicsParentDef, PositionDef, @@ -186,14 +186,23 @@ export async function initOcean() { vec2.create(), lastDir, vec2.ZEROS, - (Math.PI + 1.0) * 0.5 + (Math.PI + 1.4) * 0.5 ); dirs.push(nextDir); lastDir = nextDir; } const steepness = 0.1; - const numWaves = 3; + const numWaves = 6; + + function mkDir(hour: number): vec2 { + return vec2.rotate( + vec2.create(), + [1, 0], + vec2.ZEROS, + mathMap(hour, 0, 12, Math.PI * 1.5, 3.5 * Math.PI) + ); + } const gerstnerWaves = [ // BAD: @@ -202,9 +211,20 @@ export async function initOcean() { // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), // // GOOD: - createGerstnerWave(steepness, 2, dirs[1], 0.1, 0.3, numWaves), - createGerstnerWave(steepness, 1, dirs[2], 0.2, 0.5, numWaves), - createGerstnerWave(steepness, 0.5, dirs[3], 0.4, 1.3, numWaves), + createGerstnerWave(steepness, 4, mkDir(10), 0.1, 0.3, numWaves), + createGerstnerWave(steepness, 4, mkDir(4.3), 0.14, 0.32, numWaves), + createGerstnerWave(steepness, 2, mkDir(5), 0.2, 0.5, numWaves), + createGerstnerWave(steepness, 1, mkDir(11.4), 0.1, 1.3, numWaves), + createGerstnerWave(steepness, 3, mkDir(1), 0.3, 1.2, numWaves), + createGerstnerWave(steepness, 2, mkDir(7.5), 0.5, 0.55, numWaves), + // createGerstnerWave(steepness, 0.5, mkDir(3), 0.4, 0.4, numWaves), + // createGerstnerWave(steepness, 0.2, mkDir(9.4), 0.6, 0.2, numWaves), + + // createGerstnerWave(steepness, 0.2, dirs[6], 0.2, 0.7, numWaves), + // createGerstnerWave(steepness, 0.2, dirs[7], 0.2, 0.8, numWaves), + // createGerstnerWave(steepness, 0.2, dirs[8], 0.2, 0.9, numWaves), + + // createGerstnerWave(steepness, 0.5, dirs[4], 0.4, 1.6, numWaves), // Maybe: // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), @@ -413,9 +433,10 @@ function createGerstnerWave( speed: number, numWaves: number ) { - const Q = mathMix(0, 1 / (wavelength * amplitude * numWaves), steepness); + const A = amplitude / numWaves; + const Q = mathMix(0, 1 / (wavelength * A * numWaves), steepness); console.log(`Q: ${Q}`); - return _createGerstnerWave(Q, amplitude, direction, wavelength, speed); + return _createGerstnerWave(Q, A, direction, wavelength, speed); } function _createGerstnerWave( Q: number, From ea32da13735e06552af068888ba046f22f25f207 Mon Sep 17 00:00:00 2001 From: darzu Date: Mon, 22 Aug 2022 12:58:24 -0600 Subject: [PATCH 10/16] fixed gerstner calc; WIP on wave params --- public/shaders/std-gerstner.wgsl | 6 +- src/game/assets.ts | 14 ++- src/game/game-hyperspace.ts | 6 +- src/game/ocean.ts | 151 +++++++++++++++++++----------- src/render/mesh-pool.ts | 2 +- src/render/pipelines/std-ocean.ts | 2 +- 6 files changed, 111 insertions(+), 70 deletions(-) diff --git a/public/shaders/std-gerstner.wgsl b/public/shaders/std-gerstner.wgsl index d1a06aba..7fda9f30 100644 --- a/public/shaders/std-gerstner.wgsl +++ b/public/shaders/std-gerstner.wgsl @@ -14,11 +14,11 @@ fn gerstner(uv: vec2, t: f32) -> mat2x3 { let wave = gerstnerWaves.ms[i]; // let D = rotateDir(wave.D, 0.7); let D = wave.D; - let dot_w_d_uv_phi_t = dot(wave.w * D, uv) + wave.phi * t; + let dot_w_d_uv_phi_t = wave.w * dot(D, uv) + wave.phi * t; let _cos = cos(dot_w_d_uv_phi_t); let _sin = sin(dot_w_d_uv_phi_t); - displacement.x += wave.Q * wave.A + D.x * _cos; - displacement.z += wave.Q * wave.A + D.y * _cos; + displacement.x += wave.Q * wave.A * D.x * _cos; + displacement.z += wave.Q * wave.A * D.y * _cos; displacement.y += wave.A * _sin; normal.x += -1.0 * D.x * wave.w * wave.A * _cos; normal.z += -1.0 * D.y * wave.w * wave.A * _cos; diff --git a/src/game/assets.ts b/src/game/assets.ts index a61d072f..42493c55 100644 --- a/src/game/assets.ts +++ b/src/game/assets.ts @@ -93,6 +93,7 @@ const MeshTransforms: Partial<{ mat4.fromYRotation(mat4.create(), -Math.PI * 0.5), vec3.fromValues(-5, 0, 0) ), + // ocean: mat4.fromScaling(mat4.create(), [0.1, 0.1, 0.1]), ocean: mat4.fromScaling(mat4.create(), [2, 2, 2]), }; @@ -236,16 +237,13 @@ const MeshModify: Partial<{ // for (let i = 0; i < 100; i++) { // subdivideQuad(i); // } - let startXi = Math.floor(grid.length * 0.3); - let endXi = Math.floor(grid.length * 0.6); - let startYi = Math.floor(grid[0].length * 0.2); - let endYi = Math.floor(grid[0].length * 0.8); + let startXi = Math.floor(grid.length * 0.85); + let endXi = Math.floor(grid.length * 1.0); + let startYi = Math.floor(grid[0].length * 0.0); + let endYi = Math.floor(grid[0].length * 0.2); for (let xi = startXi; xi < endXi; xi++) { for (let yi = startYi; yi < endYi; yi++) { - let recurse = 0; - if (grid.length * 0.35 < xi && xi < grid.length * 0.5) recurse = 1; - // else if (grid.length * 0.4 < xi && xi < grid.length * 0.45) recurse = 2; - subdivideQuad(gridXYtoQuad(xi, yi), recurse); + subdivideQuad(gridXYtoQuad(xi, yi), 2); } } diff --git a/src/game/game-hyperspace.ts b/src/game/game-hyperspace.ts index c52c8faf..2f7f2938 100644 --- a/src/game/game-hyperspace.ts +++ b/src/game/game-hyperspace.ts @@ -146,11 +146,11 @@ export async function initHyperspaceGame(em: EntityManager) { // g.cameraFollow.pitchOffset = -0.486; let g = ghost; - vec3.copy(g.position, [-463.91, 8.79, 123.46]); - quat.copy(g.rotation, [0.0, 1.0, 0.0, 0.04]); + vec3.copy(g.position, [-56.8, 9.21, 9.16]); + quat.copy(g.rotation, [0.0, 1.0, 0.0, 0.06]); vec3.copy(g.cameraFollow.positionOffset, [2.0, 2.0, 8.0]); g.cameraFollow.yawOffset = 0.0; - g.cameraFollow.pitchOffset = -0.659; + g.cameraFollow.pitchOffset = -0.523; } // one-time GPU jobs diff --git a/src/game/ocean.ts b/src/game/ocean.ts index f890f828..91b902b0 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -192,47 +192,68 @@ export async function initOcean() { lastDir = nextDir; } - const steepness = 0.1; - const numWaves = 6; - - function mkDir(hour: number): vec2 { - return vec2.rotate( - vec2.create(), - [1, 0], - vec2.ZEROS, - mathMap(hour, 0, 12, Math.PI * 1.5, 3.5 * Math.PI) - ); - } - - const gerstnerWaves = [ - // BAD: - // createGerstnerWave(2, 5, dirs[0], 0.01, 0.5), - // createGerstnerWave(1.08 * 2.0, 10 * 0.5, dirs[1], 0.5 / 20.0, 0.5), - // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), - // - // GOOD: - createGerstnerWave(steepness, 4, mkDir(10), 0.1, 0.3, numWaves), - createGerstnerWave(steepness, 4, mkDir(4.3), 0.14, 0.32, numWaves), - createGerstnerWave(steepness, 2, mkDir(5), 0.2, 0.5, numWaves), - createGerstnerWave(steepness, 1, mkDir(11.4), 0.1, 1.3, numWaves), - createGerstnerWave(steepness, 3, mkDir(1), 0.3, 1.2, numWaves), - createGerstnerWave(steepness, 2, mkDir(7.5), 0.5, 0.55, numWaves), - // createGerstnerWave(steepness, 0.5, mkDir(3), 0.4, 0.4, numWaves), - // createGerstnerWave(steepness, 0.2, mkDir(9.4), 0.6, 0.2, numWaves), - - // createGerstnerWave(steepness, 0.2, dirs[6], 0.2, 0.7, numWaves), - // createGerstnerWave(steepness, 0.2, dirs[7], 0.2, 0.8, numWaves), - // createGerstnerWave(steepness, 0.2, dirs[8], 0.2, 0.9, numWaves), - - // createGerstnerWave(steepness, 0.5, dirs[4], 0.4, 1.6, numWaves), - - // Maybe: - // createGerstnerWave(steepness, 1.5, dirs[1], 0.3, 0.5, 5), - // createGerstnerWave(steepness, 0.75, dirs[2], 2.0, 0.7, 5), - // createGerstnerWave(steepness, 0.65, dirs[3], 0.59, 0.9, 5), - // createGerstnerWave(steepness, 0.55, dirs[4], 0.1, 1.2, 5), - // createGerstnerWave(steepness, 0.33, dirs[5], 0.8, 2.0, 5), - ]; + const steepness = 1.0; + const numWaves = 3; + + const S = 1 / (0.1 * 5); + + // const gerstnerWaves = [ + // // BAD: + // // createGerstnerWave(2, 5, dirs[0], 0.01, 0.5), + // // createGerstnerWave(1.08 * 2.0, 10 * 0.5, dirs[1], 0.5 / 20.0, 0.5), + // // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), + // // + // // _createGerstnerWave(S, 5.0, mkDir(3), 0.1, 0.5), + // // _createGerstnerWave(0.5, 2, mkDir(10), 0.1, 0.3), + // // _createGerstnerWave(0.5, 2, mkDir(11), 0.2, 1.0), + // createGerstnerWave(steepness, 6, hrToDir(10), 0.1, 0.3, numWaves), + // // createGerstnerWave(steepness, 2, mkDir(4.3), 0.2, 0.5, numWaves), + // // createGerstnerWave(steepness, 1.5, mkDir(1.2), 0.4, 1.3, numWaves), + // // GOOD: + // // createGerstnerWave(steepness, 4, mkDir(10), 0.1, 0.3, numWaves), + // // createGerstnerWave(steepness, 4, mkDir(4.3), 0.14, 0.32, numWaves), + // // createGerstnerWave(steepness, 2, mkDir(5), 0.2, 0.5, numWaves), + // // createGerstnerWave(steepness, 1, mkDir(11.4), 0.1, 1.3, numWaves), + // // createGerstnerWave(steepness, 3, mkDir(1), 0.3, 1.2, numWaves), + // // createGerstnerWave(steepness, 2, mkDir(7.5), 0.5, 0.55, numWaves), + // // createGerstnerWave(steepness, 0.5, mkDir(3), 0.4, 0.4, numWaves), + // // createGerstnerWave(steepness, 0.2, mkDir(9.4), 0.6, 0.2, numWaves), + // // createGerstnerWave(steepness, 0.2, dirs[6], 0.2, 0.7, numWaves), + // // createGerstnerWave(steepness, 0.2, dirs[7], 0.2, 0.8, numWaves), + // // createGerstnerWave(steepness, 0.2, dirs[8], 0.2, 0.9, numWaves), + // // createGerstnerWave(steepness, 0.5, dirs[4], 0.4, 1.6, numWaves), + // // Maybe: + // // createGerstnerWave(steepness, 1.5 * 5.0, dirs[1], 0.3, 0.5, 5), + // // createGerstnerWave(steepness, 0.75 * 5.0, dirs[2], 2.0, 0.7, 5), + // // createGerstnerWave(steepness, 0.65 * 5.0, dirs[3], 0.59, 0.9, 5), + // // createGerstnerWave(steepness, 0.55 * 5.0, dirs[4], 0.1, 1.2, 5), + // // createGerstnerWave(steepness, 0.33 * 5.0, dirs[5], 0.8, 2.0, 5), + // ]; + const gerstnerWaves = createGerstnerWaves({ + ampScale: 1, + speedScale: 0.5, + steepness: 1.0, + // prettier-ignore + waves: [ + // big + // { amp: 0.3, wavelen: 0.50 * 0.1, dirHr: 12-1.4, speed: 1.1 * 0.5 }, + // { amp: 0.3, wavelen: 0.60 * 0.1, dirHr: 1.1, speed: 2.8 * 0.5 }, + // { amp: 0.3, wavelen: 0.75 * 0.1, dirHr: 6.3, speed: 0.5 * 0.5 }, + // { amp: 0.3, wavelen: 0.70 * 0.1, dirHr: 7.2, speed: 4.2 * 0.5 }, + + // med + { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, + // { amp: 0.5, wavelen: 0.75, dirHr: 7.3, speed: 0.5 }, + // { amp: 0.5, wavelen: 0.7, dirHr: 8.2, speed: 4.2 }, + + // small + // { amp: 0.2, wavelen: 0.50 * 2.0, dirHr: 1.4, speed: 1.1 * 1.2 }, + // { amp: 0.2, wavelen: 0.60 * 2.0, dirHr: 3.1, speed: 2.8 * 1.2 }, + // { amp: 0.2, wavelen: 0.75 * 2.0, dirHr: 8.3, speed: 5.5 * 1.2 }, + // { amp: 0.2, wavelen: 0.70 * 2.0, dirHr: 9.2, speed: 4.2 * 1.2 }, + ], + }); const uvToPos = (out: vec3, uv: vec2) => { const x = uv[0] * uvToPosReader.size[0]; @@ -425,19 +446,39 @@ EM.registerSystem( "oceanUVDirToRot" ); -function createGerstnerWave( - steepness: number, - amplitude: number, - direction: vec2, - wavelength: number, - speed: number, - numWaves: number -) { - const A = amplitude / numWaves; - const Q = mathMix(0, 1 / (wavelength * A * numWaves), steepness); - console.log(`Q: ${Q}`); - return _createGerstnerWave(Q, A, direction, wavelength, speed); +type GerstnerSet = { + steepness: number; + ampScale: number; + speedScale: number; + waves: { + amp: number; + dirHr: number; + wavelen: number; + speed: number; + }[]; +}; + +function hrToDir(hour: number): vec2 { + return vec2.rotate( + vec2.create(), + [1, 0], + vec2.ZEROS, + mathMap(hour, 0, 12, Math.PI * 1.5, 3.5 * Math.PI) + ); +} + +function createGerstnerWaves(set: GerstnerSet): GerstnerWaveTS[] { + const res: GerstnerWaveTS[] = []; + for (let w of set.waves) { + const A = w.amp * set.ampScale; + const D = hrToDir(w.dirHr); + const Q = mathMix(0, 1 / (w.wavelen * A * set.waves.length), set.steepness); + const phi = w.speed * set.speedScale; + res.push(_createGerstnerWave(Q, A, D, w.wavelen, phi)); + } + return res; } + function _createGerstnerWave( Q: number, A: number, @@ -445,7 +486,9 @@ function _createGerstnerWave( w: number, phi: number ): GerstnerWaveTS { - return { D, Q, A, w, phi, padding1: 0, padding2: 0 }; + const res = { D, Q, A, w, phi, padding1: 0, padding2: 0 }; + // console.dir(res); + return res; } // TODO(@darzu): debug movement on the ocean diff --git a/src/render/mesh-pool.ts b/src/render/mesh-pool.ts index 483f48bd..9e22193d 100644 --- a/src/render/mesh-pool.ts +++ b/src/render/mesh-pool.ts @@ -71,7 +71,7 @@ function logMeshPoolStats(opts: MeshPoolOpts) { const uniStruct = opts.unis.struct; if (MAX_INDICES < maxVerts) - throw `Too many vertices (${maxVerts})! W/ Uint16, we can only support '${maxVerts}' verts`; + throw `Too many vertices (${maxVerts})! W/ Uint16, we can only support '${MAX_INDICES}' verts`; // log our estimated space usage stats console.log( diff --git a/src/render/pipelines/std-ocean.ts b/src/render/pipelines/std-ocean.ts index 30340232..6ef131df 100644 --- a/src/render/pipelines/std-ocean.ts +++ b/src/render/pipelines/std-ocean.ts @@ -70,7 +70,7 @@ export const OceanUniStruct = createCyStruct( export type OceanUniTS = CyToTS; export type OceanMeshHandle = MeshHandle; -const MAX_GERSTNER_WAVES = 8; +const MAX_GERSTNER_WAVES = 12; export const GerstnerWaveStruct = createCyStruct( { From 56d00614c85d312c03a83839ae360bc4b5229a2e Mon Sep 17 00:00:00 2001 From: darzu Date: Mon, 22 Aug 2022 13:35:48 -0600 Subject: [PATCH 11/16] waves progress --- src/game/ocean.ts | 107 +++++++++++++++++++--------------------------- src/math.ts | 8 +++- 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 91b902b0..47072973 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -4,7 +4,7 @@ import { createRef, Ref } from "../em_helpers.js"; import { EM, EntityManager } from "../entity-manager.js"; import { vec3, vec2, mat3, mat4 } from "../gl-matrix.js"; import { InputsDef } from "../inputs.js"; -import { clamp, mathMap, mathMix } from "../math.js"; +import { clamp, mathMap, mathMix, mathWrap } from "../math.js"; import { PhysicsParentDef, PositionDef, @@ -192,68 +192,42 @@ export async function initOcean() { lastDir = nextDir; } - const steepness = 1.0; - const numWaves = 3; - - const S = 1 / (0.1 * 5); - - // const gerstnerWaves = [ - // // BAD: - // // createGerstnerWave(2, 5, dirs[0], 0.01, 0.5), - // // createGerstnerWave(1.08 * 2.0, 10 * 0.5, dirs[1], 0.5 / 20.0, 0.5), - // // createGerstnerWave(0.7, 0.5, dirs[3], 0.5 / 1.0, 1), - // // - // // _createGerstnerWave(S, 5.0, mkDir(3), 0.1, 0.5), - // // _createGerstnerWave(0.5, 2, mkDir(10), 0.1, 0.3), - // // _createGerstnerWave(0.5, 2, mkDir(11), 0.2, 1.0), - // createGerstnerWave(steepness, 6, hrToDir(10), 0.1, 0.3, numWaves), - // // createGerstnerWave(steepness, 2, mkDir(4.3), 0.2, 0.5, numWaves), - // // createGerstnerWave(steepness, 1.5, mkDir(1.2), 0.4, 1.3, numWaves), - // // GOOD: - // // createGerstnerWave(steepness, 4, mkDir(10), 0.1, 0.3, numWaves), - // // createGerstnerWave(steepness, 4, mkDir(4.3), 0.14, 0.32, numWaves), - // // createGerstnerWave(steepness, 2, mkDir(5), 0.2, 0.5, numWaves), - // // createGerstnerWave(steepness, 1, mkDir(11.4), 0.1, 1.3, numWaves), - // // createGerstnerWave(steepness, 3, mkDir(1), 0.3, 1.2, numWaves), - // // createGerstnerWave(steepness, 2, mkDir(7.5), 0.5, 0.55, numWaves), - // // createGerstnerWave(steepness, 0.5, mkDir(3), 0.4, 0.4, numWaves), - // // createGerstnerWave(steepness, 0.2, mkDir(9.4), 0.6, 0.2, numWaves), - // // createGerstnerWave(steepness, 0.2, dirs[6], 0.2, 0.7, numWaves), - // // createGerstnerWave(steepness, 0.2, dirs[7], 0.2, 0.8, numWaves), - // // createGerstnerWave(steepness, 0.2, dirs[8], 0.2, 0.9, numWaves), - // // createGerstnerWave(steepness, 0.5, dirs[4], 0.4, 1.6, numWaves), - // // Maybe: - // // createGerstnerWave(steepness, 1.5 * 5.0, dirs[1], 0.3, 0.5, 5), - // // createGerstnerWave(steepness, 0.75 * 5.0, dirs[2], 2.0, 0.7, 5), - // // createGerstnerWave(steepness, 0.65 * 5.0, dirs[3], 0.59, 0.9, 5), - // // createGerstnerWave(steepness, 0.55 * 5.0, dirs[4], 0.1, 1.2, 5), - // // createGerstnerWave(steepness, 0.33 * 5.0, dirs[5], 0.8, 2.0, 5), - // ]; - const gerstnerWaves = createGerstnerWaves({ + // prettier-ignore + const gerstnerWaves = createGerstnerWaves( + { ampScale: 1, speedScale: 0.5, steepness: 1.0, - // prettier-ignore + sizeScale: 1.0, + rotateHr: 0, waves: [ - // big - // { amp: 0.3, wavelen: 0.50 * 0.1, dirHr: 12-1.4, speed: 1.1 * 0.5 }, - // { amp: 0.3, wavelen: 0.60 * 0.1, dirHr: 1.1, speed: 2.8 * 0.5 }, - // { amp: 0.3, wavelen: 0.75 * 0.1, dirHr: 6.3, speed: 0.5 * 0.5 }, - // { amp: 0.3, wavelen: 0.70 * 0.1, dirHr: 7.2, speed: 4.2 * 0.5 }, - - // med { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, - // { amp: 0.5, wavelen: 0.75, dirHr: 7.3, speed: 0.5 }, - // { amp: 0.5, wavelen: 0.7, dirHr: 8.2, speed: 4.2 }, - - // small - // { amp: 0.2, wavelen: 0.50 * 2.0, dirHr: 1.4, speed: 1.1 * 1.2 }, - // { amp: 0.2, wavelen: 0.60 * 2.0, dirHr: 3.1, speed: 2.8 * 1.2 }, - // { amp: 0.2, wavelen: 0.75 * 2.0, dirHr: 8.3, speed: 5.5 * 1.2 }, - // { amp: 0.2, wavelen: 0.70 * 2.0, dirHr: 9.2, speed: 4.2 * 1.2 }, ], - }); + }, + { + ampScale: 0.2, + speedScale: 0.2, + steepness: 1.0, + sizeScale: 4.0, + rotateHr: 8, + waves: [ + { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + }, + { + ampScale: 0.5, + speedScale: 2.0, + steepness: 1.0, + sizeScale: 0.5, + rotateHr: 4, + waves: [ + { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + }, + ); const uvToPos = (out: vec3, uv: vec2) => { const x = uv[0] * uvToPosReader.size[0]; @@ -450,6 +424,8 @@ type GerstnerSet = { steepness: number; ampScale: number; speedScale: number; + sizeScale: number; + rotateHr: number; waves: { amp: number; dirHr: number; @@ -459,6 +435,7 @@ type GerstnerSet = { }; function hrToDir(hour: number): vec2 { + hour = mathWrap(hour, 12); return vec2.rotate( vec2.create(), [1, 0], @@ -467,14 +444,18 @@ function hrToDir(hour: number): vec2 { ); } -function createGerstnerWaves(set: GerstnerSet): GerstnerWaveTS[] { +function createGerstnerWaves(...sets: GerstnerSet[]): GerstnerWaveTS[] { const res: GerstnerWaveTS[] = []; - for (let w of set.waves) { - const A = w.amp * set.ampScale; - const D = hrToDir(w.dirHr); - const Q = mathMix(0, 1 / (w.wavelen * A * set.waves.length), set.steepness); - const phi = w.speed * set.speedScale; - res.push(_createGerstnerWave(Q, A, D, w.wavelen, phi)); + const numWaves = sets.reduce((p, n) => p + n.waves.length, 0); + for (let set of sets) { + for (let w of set.waves) { + const A = w.amp * set.ampScale * set.sizeScale; + const D = hrToDir(w.dirHr + set.rotateHr); + const W = w.wavelen / set.sizeScale; + const Q = mathMix(0, 1 / (W * A * numWaves), set.steepness); + const phi = w.speed * set.speedScale; // / set.sizeScale; + res.push(_createGerstnerWave(Q, A, D, W, phi)); + } } return res; } diff --git a/src/math.ts b/src/math.ts index 06991840..cb8b0a5c 100644 --- a/src/math.ts +++ b/src/math.ts @@ -57,7 +57,13 @@ export function mathMap( const progress = (n - inMin) / (inMax - inMin); return progress * (outMax - outMin) + outMin; } -export function mathMix(a: number, b: number, p: number) { +export function mathWrap(n: number, max: number): number { + // TODO(@darzu): support min? + const r = max; + const p = ((n % r) + r) % r; // TODO(@darzu): probably a more compact way to do this + return p; +} +export function mathMix(a: number, b: number, p: number): number { return a * (1 - p) + b * p; } export function mathMapNEase( From 7992f803289cab85806b0b8d99b85634bfcab27e Mon Sep 17 00:00:00 2001 From: darzu Date: Mon, 22 Aug 2022 13:59:33 -0600 Subject: [PATCH 12/16] more experimenting --- src/game/ocean.ts | 88 ++++++++++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 47072973..33f85fcd 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -193,40 +193,57 @@ export async function initOcean() { } // prettier-ignore + const S = 1.0; + const T = 1.0; const gerstnerWaves = createGerstnerWaves( - { - ampScale: 1, - speedScale: 0.5, - steepness: 1.0, - sizeScale: 1.0, - rotateHr: 0, - waves: [ - { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, - { - ampScale: 0.2, - speedScale: 0.2, - steepness: 1.0, - sizeScale: 4.0, - rotateHr: 8, - waves: [ - { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, - { - ampScale: 0.5, - speedScale: 2.0, - steepness: 1.0, - sizeScale: 0.5, - rotateHr: 4, - waves: [ - { amp: 0.5, wavelen: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, wavelen: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, + { + steepness: 2.0, + rotateHr: 0, + ampScale: 0.8, + speedScale: 1.0 * T, + sizeScale: 1.0 * S, + freqScale: 1.0, + waves: [ + { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + }, + { + steepness: 0.2, + rotateHr: 6.4, + ampScale: 1.0, + speedScale: 0.21 * T, + sizeScale: 1.0 * S, + freqScale: 0.7, + waves: [ + { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + }, + { + steepness: 0.2, + rotateHr: 2.7, + ampScale: 0.2, + speedScale: 0.25 * T, + sizeScale: 1.0 * S, + freqScale: 0.7, + waves: [ + { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + }, + { + steepness: 0.2, + rotateHr: 9.2, + ampScale: 0.2, + speedScale: 0.17 * T, + sizeScale: 1.0 * S, + freqScale: 0.7, + waves: [ + { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, + { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, + ], + } ); const uvToPos = (out: vec3, uv: vec2) => { @@ -425,11 +442,12 @@ type GerstnerSet = { ampScale: number; speedScale: number; sizeScale: number; + freqScale: number; rotateHr: number; waves: { amp: number; dirHr: number; - wavelen: number; + freq: number; speed: number; }[]; }; @@ -451,7 +469,7 @@ function createGerstnerWaves(...sets: GerstnerSet[]): GerstnerWaveTS[] { for (let w of set.waves) { const A = w.amp * set.ampScale * set.sizeScale; const D = hrToDir(w.dirHr + set.rotateHr); - const W = w.wavelen / set.sizeScale; + const W = (w.freq * set.freqScale) / set.sizeScale; const Q = mathMix(0, 1 / (W * A * numWaves), set.steepness); const phi = w.speed * set.speedScale; // / set.sizeScale; res.push(_createGerstnerWave(Q, A, D, W, phi)); From c8ed153ca21b966acca1a01ed28f16c3b9de6cbb Mon Sep 17 00:00:00 2001 From: darzu Date: Wed, 24 Aug 2022 14:59:15 -0700 Subject: [PATCH 13/16] notes and progress on steepness params --- notes/general.txt | 9 ++++ notes/renderer.txt | 17 ++++++- notes/water.txt | 18 +++++++- src/game/ocean.ts | 111 +++++++++++++++------------------------------ 4 files changed, 78 insertions(+), 77 deletions(-) diff --git a/notes/general.txt b/notes/general.txt index 48db0fbe..50aae637 100644 --- a/notes/general.txt +++ b/notes/general.txt @@ -1247,3 +1247,12 @@ Recoverable vs unrecoverable errors: with regards to panic think instead: error-but-not-a-bug vs bug https://blog.burntsushi.net/unwrap/#what-about-recoverable-vs-unrecoverable-errors + +"A Mega-List of Learning Resources for Game Creators": + https://github.com/notpresident35/learn-awesome-gamedev + +Twitter contacts for gamedevs? + https://docs.google.com/spreadsheets/d/e/2PACX-1vRYveFAl_GuvBo0iuy3EvgPm0fWAGci-Z8e5CZ6hoYz9n8gcoK4dgE0RML-x0pWqRNjGqte-V7phtqB/pubhtml + +Game AI: + http://www.gameaipro.com \ No newline at end of file diff --git a/notes/renderer.txt b/notes/renderer.txt index c7ac6553..4181e8be 100644 --- a/notes/renderer.txt +++ b/notes/renderer.txt @@ -961,4 +961,19 @@ Anti-aliasing: example project: https://sokpop.itch.io/sokpop-fake-3d-demo Decals: - https://tuket.github.io/posts/2022-08-05-explosion-decals/ \ No newline at end of file + https://tuket.github.io/posts/2022-08-05-explosion-decals/ + +Falconeer clouds: + https://youtu.be/5d8tx6K6hkk?t=6896 + spheres, noise texture, fresnel effect to hide outside edges + tri-planar mapping to ea sphere + e.g. tri-planar: https://gamedevelopment.tutsplus.com/articles/use-tri-planar-texture-mapping-for-better-terrain--gamedev-13821 + clouds are lit by using the skybox gradient (not lit on sphere) + "if i were to turn off the distortion, they would disappear into the sky" + sorting and clipping isn't an issue since clouds are so similar w/ tri-planar mapping + still does alpha sorting? + +Falconeer floodlights: + triangular prism, + addative, + fade out w/ depth blend so u don't get ugly intersections \ No newline at end of file diff --git a/notes/water.txt b/notes/water.txt index b371dde6..4f634ff7 100644 --- a/notes/water.txt +++ b/notes/water.txt @@ -80,4 +80,20 @@ GPU gems water, gerstner waves: https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1-effective-water-simulation-physical-models Sea of Thieves (not very useful): - https://www.youtube.com/watch?v=9nxlmCq4220 \ No newline at end of file + https://www.youtube.com/watch?v=9nxlmCq4220 + +Waves in Sprigland, 8/24/22: + What have we learned: + It's not easy to get the right parameters for gerstner waves + What are the open questions: + Do we need more resolution to our mesh before the waves look good? + So far, it hasn't been easier with a tighter mesh + What's the immediate, plausible path: + Go back to the big mesh, unsubdivided, + get 2-3 waves that look passable but probably not great + continue with other gameplay content + eventually: + [ ] higher LOD triangle meshes, tiled, follows camera + - Falconeer uses ~60k tris + [ ] sliders for real-time adjustment + [ ] diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 33f85fcd..938b409b 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -192,59 +192,14 @@ export async function initOcean() { lastDir = nextDir; } - // prettier-ignore const S = 1.0; const T = 1.0; - const gerstnerWaves = createGerstnerWaves( - { - steepness: 2.0, - rotateHr: 0, - ampScale: 0.8, - speedScale: 1.0 * T, - sizeScale: 1.0 * S, - freqScale: 1.0, - waves: [ - { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, - { - steepness: 0.2, - rotateHr: 6.4, - ampScale: 1.0, - speedScale: 0.21 * T, - sizeScale: 1.0 * S, - freqScale: 0.7, - waves: [ - { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, - { - steepness: 0.2, - rotateHr: 2.7, - ampScale: 0.2, - speedScale: 0.25 * T, - sizeScale: 1.0 * S, - freqScale: 0.7, - waves: [ - { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - }, - { - steepness: 0.2, - rotateHr: 9.2, - ampScale: 0.2, - speedScale: 0.17 * T, - sizeScale: 1.0 * S, - freqScale: 0.7, - waves: [ - { amp: 0.5, freq: 0.5, dirHr: 0.4, speed: 1.1 }, - { amp: 0.5, freq: 0.9, dirHr: 2.1, speed: 2.8 }, - ], - } - ); + // prettier-ignore + const gerstnerWaves = createGerstnerWaves([ + { steep: 10.0, amp: 1, freq: 0.5, dirHr: 2.0, speed: 0.5 }, + { steep: 10.0, amp: 1, freq: 0.5, dirHr: 6.0, speed: 0.5 }, + // { steep: 0.4, amp: 1.0, freq: 0.1, dirHr: 4.0, speed: 0.2 }, + ]); const uvToPos = (out: vec3, uv: vec2) => { const x = uv[0] * uvToPosReader.size[0]; @@ -438,19 +393,12 @@ EM.registerSystem( ); type GerstnerSet = { - steepness: number; - ampScale: number; - speedScale: number; - sizeScale: number; - freqScale: number; - rotateHr: number; - waves: { - amp: number; - dirHr: number; - freq: number; - speed: number; - }[]; -}; + steep: number; + amp: number; + dirHr: number; + freq: number; + speed: number; +}[]; function hrToDir(hour: number): vec2 { hour = mathWrap(hour, 12); @@ -462,18 +410,31 @@ function hrToDir(hour: number): vec2 { ); } -function createGerstnerWaves(...sets: GerstnerSet[]): GerstnerWaveTS[] { +function createGerstnerWaves(waves: GerstnerSet): GerstnerWaveTS[] { const res: GerstnerWaveTS[] = []; - const numWaves = sets.reduce((p, n) => p + n.waves.length, 0); - for (let set of sets) { - for (let w of set.waves) { - const A = w.amp * set.ampScale * set.sizeScale; - const D = hrToDir(w.dirHr + set.rotateHr); - const W = (w.freq * set.freqScale) / set.sizeScale; - const Q = mathMix(0, 1 / (W * A * numWaves), set.steepness); - const phi = w.speed * set.speedScale; // / set.sizeScale; - res.push(_createGerstnerWave(Q, A, D, W, phi)); - } + const numWaves = waves.length; + const steepDenom = waves.reduce((p, n) => p + n.amp * n.freq, 0); + const steepBudget = 1 / steepDenom; + const steepTotal = waves.reduce((p, n) => p + n.steep, 0); + const steepScale = steepBudget / steepTotal; + // TODO(@darzu): confirm/fix this math! + console.log( + ` + steepDenom: ${steepDenom}, + steepBudget: ${steepBudget}, + steepTotal: ${steepTotal}, + steepScale: ${steepScale}, + ` + ); + for (let w of waves) { + const A = w.amp; + const D = hrToDir(w.dirHr); + const W = w.freq; + const Q = w.steep * steepScale; + console.log(`Q: ${Q}`); + const phi = w.speed; + res.push(_createGerstnerWave(Q, A, D, W, phi)); + console.dir(res[res.length - 1]); } return res; } From 212e19010fdf09a67ef4fe5f51d7f2e403e2aadd Mon Sep 17 00:00:00 2001 From: darzu Date: Fri, 26 Aug 2022 09:28:12 -0700 Subject: [PATCH 14/16] steepness seems to be working! --- src/game/ocean.ts | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 938b409b..8c8592ca 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -194,12 +194,14 @@ export async function initOcean() { const S = 1.0; const T = 1.0; - // prettier-ignore - const gerstnerWaves = createGerstnerWaves([ - { steep: 10.0, amp: 1, freq: 0.5, dirHr: 2.0, speed: 0.5 }, - { steep: 10.0, amp: 1, freq: 0.5, dirHr: 6.0, speed: 0.5 }, - // { steep: 0.4, amp: 1.0, freq: 0.1, dirHr: 4.0, speed: 0.2 }, - ]); + const gerstnerWaves = createGerstnerWaves({ + steepTotal: 2.0, + waves: [ + { steep: 10.0, amp: 1, freq: 0.5, dirHr: 2.0, speed: 0.5 }, + { steep: 10.0, amp: 1, freq: 0.5, dirHr: 6.0, speed: 0.5 }, + // { steep: 0.4, amp: 1.0, freq: 0.1, dirHr: 4.0, speed: 0.2 }, + ], + }); const uvToPos = (out: vec3, uv: vec2) => { const x = uv[0] * uvToPosReader.size[0]; @@ -393,12 +395,15 @@ EM.registerSystem( ); type GerstnerSet = { - steep: number; - amp: number; - dirHr: number; - freq: number; - speed: number; -}[]; + steepTotal: number; + waves: { + steep: number; + amp: number; + dirHr: number; + freq: number; + speed: number; + }[]; +}; function hrToDir(hour: number): vec2 { hour = mathWrap(hour, 12); @@ -410,23 +415,22 @@ function hrToDir(hour: number): vec2 { ); } -function createGerstnerWaves(waves: GerstnerSet): GerstnerWaveTS[] { +function createGerstnerWaves(set: GerstnerSet): GerstnerWaveTS[] { const res: GerstnerWaveTS[] = []; - const numWaves = waves.length; - const steepDenom = waves.reduce((p, n) => p + n.amp * n.freq, 0); - const steepBudget = 1 / steepDenom; - const steepTotal = waves.reduce((p, n) => p + n.steep, 0); - const steepScale = steepBudget / steepTotal; + const steepDenom = set.waves.reduce((p, n) => p + n.amp * n.freq, 0); + const steepBudget = set.steepTotal / steepDenom; + const steepSum = set.waves.reduce((p, n) => p + n.steep, 0); + const steepScale = steepBudget / steepSum; // TODO(@darzu): confirm/fix this math! console.log( ` steepDenom: ${steepDenom}, steepBudget: ${steepBudget}, - steepTotal: ${steepTotal}, + steepSum: ${steepSum}, steepScale: ${steepScale}, ` ); - for (let w of waves) { + for (let w of set.waves) { const A = w.amp; const D = hrToDir(w.dirHr); const W = w.freq; From 093ed768f194ced0de83ccf7b71f1b4762ccac6b Mon Sep 17 00:00:00 2001 From: darzu Date: Sat, 3 Sep 2022 10:24:49 -0700 Subject: [PATCH 15/16] notes and still trying gerstners --- notes/ideas.txt | 24 +++++++++++++++++++++++- notes/renderer.txt | 13 ++++++++++++- notes/typescript.txt | 14 +++++++++++++- src/game/ocean.ts | 32 +++++++++++++++++++------------- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/notes/ideas.txt b/notes/ideas.txt index 5e57ed54..f20b03ae 100644 --- a/notes/ideas.txt +++ b/notes/ideas.txt @@ -62,4 +62,26 @@ Unrelated: shows you your social page one after another with a timer at the top optionally a start button per-site so you know it's fully loaded an exception system, but it gets logged and you can see how much excess time you spent - you get to save links to a reading list \ No newline at end of file + you get to save links to a reading list + +Workshop game: + You have a garage, + (that becomes a detached garage and then gets upgraded in size) + With tools: + woodworking, + welding, + smelting, + smithing, + pottery, + sewing, + glass blowing, + stain glass, + machining, + Acquired over time + Maybe you build your own tools? + Maybe u sell at the local farmers market? + Life sim? + u start in a city, + move out for more space, + family? + \ No newline at end of file diff --git a/notes/renderer.txt b/notes/renderer.txt index 4181e8be..da3194d5 100644 --- a/notes/renderer.txt +++ b/notes/renderer.txt @@ -976,4 +976,15 @@ Falconeer clouds: Falconeer floodlights: triangular prism, addative, - fade out w/ depth blend so u don't get ugly intersections \ No newline at end of file + fade out w/ depth blend so u don't get ugly intersections + +Reternal particle system: + This is a great talk about the custom particle system used in Reternal for everything incl tentacles + https://www.youtube.com/watch?v=qbkb8ap7vts + This could be a great starting point for our own particle system, and I think it fits nicely with the Cy workflow + + "Semi-Lagrangian grid-based fluid sim" + centered around the player, running at all time + simulates air flow + particles can simple from it + and affect it? \ No newline at end of file diff --git a/notes/typescript.txt b/notes/typescript.txt index 183d322a..71111bee 100644 --- a/notes/typescript.txt +++ b/notes/typescript.txt @@ -8,4 +8,16 @@ https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API Refactors I want to be able to do: Inline funcition: splat the body of a function into it's call site program slice: chop away everything that isn't needed to make this one thing work - \ No newline at end of file + +Doug's transform script: + https://github.com/dwoos/sprig-transforms/blob/main/src/matrixMigrate.ts + +Fake nominal typing: + https://stackoverflow.com/a/61296495 + https://evertpot.com/opaque-ts-types/ + + // stripped out when emitted to JS + declare const validProductId: unique symbol; + type ProductId = number & { + [validProductId]: true + } diff --git a/src/game/ocean.ts b/src/game/ocean.ts index 8c8592ca..9e246743 100644 --- a/src/game/ocean.ts +++ b/src/game/ocean.ts @@ -192,14 +192,21 @@ export async function initOcean() { lastDir = nextDir; } - const S = 1.0; - const T = 1.0; + const S = 0.05; + const L = 20; + const A = 0.4; const gerstnerWaves = createGerstnerWaves({ - steepTotal: 2.0, + steepTotal: 1.0, + // prettier-ignore waves: [ - { steep: 10.0, amp: 1, freq: 0.5, dirHr: 2.0, speed: 0.5 }, - { steep: 10.0, amp: 1, freq: 0.5, dirHr: 6.0, speed: 0.5 }, - // { steep: 0.4, amp: 1.0, freq: 0.1, dirHr: 4.0, speed: 0.2 }, + // { steep: 10.0, amp: 1, len: 1 / 0.5, dirHr: 2.0, speed: 0.5 }, + // { steep: 10.0, amp: 1, len: 1 / 0.5, dirHr: 6.0, speed: 0.5 }, + { len: 0.1 * L, dir: [0.99, 0.1], steep: 0.9, amp: 4 * A, speed: 9.94 * S }, + { len: 0.1 * L, dir: [-0.37, 0.9], steep: 0.55, amp: 5 * A, speed: 15 * S }, + { len: 0.4 * L, dir: [-0.87, 0.4], steep: 1.0, amp: 1 * A, speed: 12 * S }, + { len: 0.8 * L, dir: [-0.3, 0.9], steep: 0.7, amp: 0.5 * A, speed: 0.9 * S }, + { len: 1.0 * L, dir: [0.001, 0.9], steep: 0.51, amp: -0.3 * A, speed: 0.7 * S }, + { len: 1.2 * L, dir: [0.98, -0.1], steep: 0.35, amp: 0.2 * A, speed: 0.5 * S }, ], }); @@ -396,13 +403,12 @@ EM.registerSystem( type GerstnerSet = { steepTotal: number; - waves: { + waves: ({ steep: number; amp: number; - dirHr: number; - freq: number; + len: number; speed: number; - }[]; + } & ({ dirHr: number } | { dir: vec2 }))[]; }; function hrToDir(hour: number): vec2 { @@ -417,7 +423,7 @@ function hrToDir(hour: number): vec2 { function createGerstnerWaves(set: GerstnerSet): GerstnerWaveTS[] { const res: GerstnerWaveTS[] = []; - const steepDenom = set.waves.reduce((p, n) => p + n.amp * n.freq, 0); + const steepDenom = set.waves.reduce((p, n) => p + n.amp * (1 / n.len), 0); const steepBudget = set.steepTotal / steepDenom; const steepSum = set.waves.reduce((p, n) => p + n.steep, 0); const steepScale = steepBudget / steepSum; @@ -432,8 +438,8 @@ function createGerstnerWaves(set: GerstnerSet): GerstnerWaveTS[] { ); for (let w of set.waves) { const A = w.amp; - const D = hrToDir(w.dirHr); - const W = w.freq; + const D = "dirHr" in w ? hrToDir(w.dirHr) : w.dir; // vec2.normalize(vec2.create(), w.dir); + const W = 1 / w.len; const Q = w.steep * steepScale; console.log(`Q: ${Q}`); const phi = w.speed; From ecf28458a20d560336472af44212561735310960 Mon Sep 17 00:00:00 2001 From: darzu Date: Tue, 6 Sep 2022 13:44:12 -0700 Subject: [PATCH 16/16] notes and webgpu fix --- notes/ship_game.txt | 21 ++++++++++++++++++++- public/shaders/std-mesh.wgsl | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/notes/ship_game.txt b/notes/ship_game.txt index 41970396..01c27986 100644 --- a/notes/ship_game.txt +++ b/notes/ship_game.txt @@ -338,4 +338,23 @@ Gameplay hackathon ideas: - crow's nest - impassible rocks - raising and lowering obstacles like turtle islands in Valheim - - grapple hook floating objects \ No newline at end of file + - grapple hook floating objects + +Okay okay I know this is probably bad idea, and yes I've been playing to much Factorio, but hear me out: + +Say at the start of your journey, you get to choose to either: +- bring planks and nails +or +- bring logs, ingots, a mill, forge and anvil +the idea is that you can either bring specialized goods or base materials and crafting stations to use those goods. +With specialized goods it's obviously less work to use them in repair or other tasks but the risk is that you will run out of the right materials. +Whereas with generalized goods you have a lot more flexibility but it's a trade off in craft station space, complexity, and time. + +This is actually how ship journeys used to work to a degree! They would stock extra sail cloth, huge lengths of rope, planks, and nails and then sew and work them to the sizes needed at sea. + +Maybe after every combat, you have to go asses the damage to your ship and come up with a parts list. Then you need to source all those parts in order to do a repair. While your ship isn't repaired, you have a magic shield that is protected you but the bigger the breach the faster it drains your energy reserves. You need to get those wizard-wood planks in place to stabalize the energy leaks into the hyperspace ocean. + +It’s also another mechanism to make combat dangerous: u might need a huge, costly repair afterwards +Also there’s a hand tools vs powertools tradeoff: power tools are faster, more expensive, take more space, take power to operate + +If we’re low-poly enough, u could render the exact output on the ship. The process wouldn’t be perfect and u would see individual marks from the process. The ship would really feel like ur personal creation \ No newline at end of file diff --git a/public/shaders/std-mesh.wgsl b/public/shaders/std-mesh.wgsl index ac8a532d..47360f0c 100644 --- a/public/shaders/std-mesh.wgsl +++ b/public/shaders/std-mesh.wgsl @@ -112,7 +112,7 @@ fn frag_main(input: VertexOutput) -> FragOut { var lightingColor: vec3 = vec3(0.0, 0.0, 0.0); - let unlit = meshUni.flags & 1u >> 0u; + let unlit = meshUni.flags & (1u >> 0u); for (var i: u32 = 0u; i < scene.numPointLights; i++) { let light = pointLights.ms[i]; let toLight = light.position - input.worldPos.xyz;