feat: client-side rendering
Some checks are pending
Verify Latest Dependencies / Verify Latest Dependencies (push) Waiting to run
build and test / wxbox - latest (push) Waiting to run

This commit is contained in:
core 2025-05-31 12:41:35 -04:00
parent 61df1e725c
commit 26b393a719
Signed by: core
GPG key ID: FDBF740DADDCEECF
7 changed files with 106 additions and 61 deletions

22
.idea/workspace.xml generated
View file

@ -7,13 +7,12 @@
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="2d855648-9644-469a-afa2-59beb52bb1d6" name="Changes" comment="mostly functional v2 rendering">
<change afterPath="$PROJECT_DIR$/crates/pal/src/wasm.rs" afterDir="false" />
<list default="true" id="2d855648-9644-469a-afa2-59beb52bb1d6" name="Changes" comment="csr rendering v2">
<change afterPath="$PROJECT_DIR$/client/src/lib/map/default_palettes.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.lock" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.lock" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/client/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client/src/lib/Map.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/Map.svelte" afterDir="false" />
<change beforePath="$PROJECT_DIR$/crates/pal/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/crates/pal/Cargo.toml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/client/src/lib/map/fragment.glsl" beforeDir="false" afterPath="$PROJECT_DIR$/client/src/lib/map/fragment.glsl" afterDir="false" />
<change beforePath="$PROJECT_DIR$/crates/pal/src/lib.rs" beforeDir="false" afterPath="$PROJECT_DIR$/crates/pal/src/lib.rs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
@ -22,7 +21,7 @@
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DarkyenusTimeTracker">
<option name="totalTimeSeconds" value="11086" />
<option name="totalTimeSeconds" value="14770" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
@ -166,7 +165,15 @@
<option name="project" value="LOCAL" />
<updated>1748662734805</updated>
</task>
<option name="localTasksCounter" value="4" />
<task id="LOCAL-00004" summary="csr rendering v2">
<option name="closed" value="true" />
<created>1748704540785</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1748704540785</updated>
</task>
<option name="localTasksCounter" value="5" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -177,6 +184,7 @@
<MESSAGE value="feat: client work" />
<MESSAGE value="debugging" />
<MESSAGE value="mostly functional v2 rendering" />
<option name="LAST_COMMIT_MESSAGE" value="mostly functional v2 rendering" />
<MESSAGE value="csr rendering v2" />
<option name="LAST_COMMIT_MESSAGE" value="csr rendering v2" />
</component>
</project>

View file

@ -4,7 +4,7 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"dev": "bun update:protobuf && bun update:wxbox-pal && vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",

View file

@ -17,6 +17,8 @@
import type { DigitalRadarData, Radial } from './generated_interop/digitalRadarData';
import {forwardGeodesic, radToDeg} from "$lib/vincenty";
import {degToRad} from "$lib/vincenty.js";
import init_palette_parser_wasm, {parse_palette} from "$lib/map/pal/wxbox_pal";
import {WXBOX_STANDARD_REF} from "$lib/map/default_palettes";
const BELOW_THRESHOLD = -9999.0;
const RANGE_FOLDED = -9998.0;
@ -167,6 +169,8 @@
if (container.messageType.oneofKind == 'digitalRadarData') {
const drd: DigitalRadarData = container.messageType.digitalRadarData;
await init_palette_parser_wasm();
const dataLayer = {
id: 'dataGl',
type: 'custom',
@ -187,14 +191,6 @@
this.aPos = gl.getAttribLocation(this.program, 'a_pos');
/*
110.574 km = 1 deg
deg = 1/110.574 km
111.320 * cos(latitude) km = 1 deg
*/
const lat = 38.976111;
const long = -77.4875;
@ -214,7 +210,7 @@
if (radial.product && radial.product.data && radial.product.data.data) {
const angle_a = radial.azimuthAngleDegrees;
const angle_b = angle_a + radial.azimuthSpacingDegrees * 1.2;
const angle_b = (i == drd.radials.length - 1) ? drd.radials[0].azimuthAngleDegrees : drd.radials[i+1].azimuthAngleDegrees;
const start_range = radial.product.data.startRange; // m
const sample_interval = radial.product.data.sampleInterval; // m
@ -294,10 +290,61 @@
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.uniform1i(gl.getUniformLocation(this.program, 'data'), 10);
// parse, transform, and send over the palette
const palette = parse_palette(WXBOX_STANDARD_REF);
gl.uniform1i(gl.getUniformLocation(this.program, 'paletteLength'), palette.length);
const rboundsData: number[] = [];
const rcolorData: number[] = [];
const rcolor2Data: number[] = [];
for (const paletteEntry of palette) {
const [bounds, color] = paletteEntry;
rboundsData.push(...bounds);
rcolorData.push(...[color[0].red, color[0].green, color[0].blue, color[0].alpha]);
rcolor2Data.push(...[color[1].red, color[1].green, color[1].blue, color[1].alpha]);
}
// create the bounds texture first
const boundsData = new Float32Array(rboundsData);
gl.activeTexture(gl.TEXTURE0 + 11);
this.boundsTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.boundsTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, 1, palette.length, 0, gl.RG, gl.FLOAT, boundsData);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.uniform1i(gl.getUniformLocation(this.program, 'bounds'), 11);
// then the colorData texture
const colorData = new Uint8Array(rcolorData);
gl.activeTexture(gl.TEXTURE0 + 12);
this.colorDataTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.colorDataTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, palette.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, colorData);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.uniform1i(gl.getUniformLocation(this.program, 'colorData'), 12);
// then the colorData2 texture
const color2Data = new Uint8Array(rcolor2Data);
gl.activeTexture(gl.TEXTURE0 + 13);
this.color2DataTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, this.color2DataTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, palette.length, 0, gl.RGBA, gl.UNSIGNED_BYTE, color2Data);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.uniform1i(gl.getUniformLocation(this.program, 'color2Data'), 13);
},
render(gl, args) {
gl.activeTexture(gl.TEXTURE0 + 10);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.activeTexture(gl.TEXTURE0 + 11);
gl.bindTexture(gl.TEXTURE_2D, this.boundsTexture);
gl.activeTexture(gl.TEXTURE0 + 12);
gl.bindTexture(gl.TEXTURE_2D, this.colorDataTexture);
gl.activeTexture(gl.TEXTURE0 + 13);
gl.bindTexture(gl.TEXTURE_2D, this.color2DataTexture);
gl.useProgram(this.program);
gl.uniformMatrix4fv(

View file

@ -0,0 +1,8 @@
export const WXBOX_STANDARD_REF = `Color4: 10 164 164 255 0 100 100 192 255
Color4: 20 64 128 255 255 32 64 128 255
Color4: 30 0 255 0 255 0 128 0 255
Color4: 40 255 255 0 255 255 128 0 255
Color4: 50 255 0 0 255 160 0 0 255
Color4: 60 255 0 255 255 128 0 128 255
Color4: 70 255 255 255 255 128 128 128 255
Color4: 80 128 128 128 255`;

View file

@ -7,12 +7,16 @@ const float PI = 3.14159265358979323846264338327950288;
layout(location = 0) out vec4 fragColor;
in vec2 raw_pos;
in vec4 vertexColor;
flat in int azimuthNumber;
flat in float azimuthAngle;
uniform sampler2D data;
uniform sampler2D bounds;
uniform sampler2D colorData;
uniform sampler2D color2Data;
uniform int paletteLength;
uniform float radarLat;
uniform float radarLng;
@ -27,18 +31,8 @@ Wgs84Coordinates rawPosToWgs84(vec2 raw_pos) {
return Wgs84Coordinates(lat,lng);
}
float haversineDistance(Wgs84Coordinates from, Wgs84Coordinates to, float radius) {
return 2.0 * radius * asin(sqrt(pow(2.0, sin((to.lat-from.lat)/2.0)) + cos(from.lat) * cos(to.lat) * pow(2.0, sin((to.lng - from.lng)/2.0))));
}
const float R = 6371000.0; // m
float random (vec2 st) {
return fract(sin(dot(st.xy,
vec2(12.9898,78.233)))*
43758.5453123);
}
void main() {
Wgs84Coordinates thisPixelCoordinates = rawPosToWgs84(raw_pos);
Wgs84Coordinates radarCoordinates = Wgs84Coordinates(radarLat,radarLng);
@ -65,37 +59,30 @@ void main() {
fragColor = vec4(0,0,0,0);
return;
}
int selector = int(mod(float(azimuthNumber), 2.0));
if (selector == 0) {
fragColor = vec4(float(azimuthAngle) / 360.0, 0, 0, 1);
} else if (selector == 1) {
fragColor = vec4(0,float(azimuthAngle) / 360.0, 0,1);
} else {
fragColor = vec4(0,0,float(azimuthAngle)/360.0,1);
}
int gate_number = int(floor((distance_meters - 2125.0) / 250.0));
float rawValue = texelFetch(data, ivec2(gate_number, azimuthNumber), 0).r;
if (rawValue > 80.0) {
fragColor = vec4(128.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 1.0);
} else if (rawValue > 70.0) {
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
} else if (rawValue > 60.0) {
fragColor = vec4(1.0, 0.0, 1.0, 1.0);
} else if (rawValue > 50.0) {
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else if (rawValue > 40.0) {
fragColor = vec4(1.0, 1.0, 0.0, 1.0);
} else if (rawValue > 30.0) {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
} else if (rawValue > 20.0) {
fragColor = vec4(64.0 / 255.0, 128.0 / 255.0, 1.0, 1.0);
} else if (rawValue > 10.0) {
fragColor = vec4(164.0 / 255.0, 164.0 / 255.0, 1.0, 1.0);
} else {
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
// colorize!
for (int i = paletteLength; i > 0; i--) {
vec2 rangeBounds = texelFetch(bounds, ivec2(0, i), 0).rg;
float lower = rangeBounds.r;
float higher = rangeBounds.g;
if (rawValue >= lower && rawValue < higher) {
float mapped_end = higher - lower;
float mapped_point = rawValue - lower;
float t = mapped_point / mapped_end;
vec4 color_start = texelFetch(colorData, ivec2(0, i), 0);
vec4 color_end = texelFetch(color2Data, ivec2(0, i), 0);
fragColor = mix(color_start, color_end, t);
return;
}
}
fragColor = vec4(0,0,0,0);
return;
}

View file

@ -6,7 +6,6 @@ uniform mat4 u_matrix;
in vec2 a_pos;
out vec2 raw_pos;
out vec4 vertexColor;
flat out int azimuthNumber;
flat out float azimuthAngle;
@ -21,9 +20,4 @@ void main() {
gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
azimuthAngle = triangleAzimuthLookup[gl_VertexID / 3];
azimuthNumber = gl_VertexID / 3;
float randColor = rand(vec2(triangleAzimuthLookup[gl_VertexID/3] / 720.0,triangleAzimuthLookup[gl_VertexID/3] / 720.0));
float randColor2 = rand(vec2(randColor, randColor));
float randColor3 = rand(vec2(randColor, randColor));
vertexColor = vec4(randColor,randColor2,randColor3,1.0);
}

View file

@ -1,5 +1,6 @@
pub mod default_palettes;
pub mod parser;
#[cfg(feature = "wasm")]
mod wasm;
pub use ordered_float::OrderedFloat;