UNPKG

@deck.gl/core

Version:

deck.gl core library

305 lines (265 loc) 10.6 kB
// deck.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors import {COORDINATE_SYSTEM, PROJECTION_MODE, UNIT} from '../../lib/constants'; // We are generating these from the js code in constants.js const COORDINATE_SYSTEM_WGSL_CONSTANTS = Object.keys(COORDINATE_SYSTEM) .map(key => `const COORDINATE_SYSTEM_${key}: i32 = ${COORDINATE_SYSTEM[key]};`) .join(''); const PROJECTION_MODE_WGSL_CONSTANTS = Object.keys(PROJECTION_MODE) .map(key => `const PROJECTION_MODE_${key}: i32 = ${PROJECTION_MODE[key]};`) .join(''); const UNIT_WGSL_CONSTANTS = Object.keys(UNIT) .map(key => `const UNIT_${key.toUpperCase()}: i32 = ${UNIT[key]};`) .join(''); export const projectWGSLHeader = /* wgsl */ `\ ${COORDINATE_SYSTEM_WGSL_CONSTANTS} ${PROJECTION_MODE_WGSL_CONSTANTS} ${UNIT_WGSL_CONSTANTS} const TILE_SIZE: f32 = 512.0; const PI: f32 = 3.1415926536; const WORLD_SCALE: f32 = TILE_SIZE / (PI * 2.0); const ZERO_64_LOW: vec3<f32> = vec3<f32>(0.0, 0.0, 0.0); const EARTH_RADIUS: f32 = 6370972.0; // meters const GLOBE_RADIUS: f32 = 256.0; // ----------------------------------------------------------------------------- // Uniform block (converted from GLSL uniform block) // ----------------------------------------------------------------------------- struct ProjectUniforms { wrapLongitude: i32, coordinateSystem: i32, commonUnitsPerMeter: vec3<f32>, projectionMode: i32, scale: f32, commonUnitsPerWorldUnit: vec3<f32>, commonUnitsPerWorldUnit2: vec3<f32>, center: vec4<f32>, modelMatrix: mat4x4<f32>, viewProjectionMatrix: mat4x4<f32>, viewportSize: vec2<f32>, devicePixelRatio: f32, focalDistance: f32, cameraPosition: vec3<f32>, coordinateOrigin: vec3<f32>, commonOrigin: vec3<f32>, pseudoMeters: i32, }; @group(0) @binding(0) var<uniform> project: ProjectUniforms; // ----------------------------------------------------------------------------- // Geometry data // (In your GLSL code, "geometry" was assumed to be available globally. In WGSL, // you might supply this via vertex attributes or a uniform. Here we define a // uniform struct for demonstration.) // ----------------------------------------------------------------------------- // Structure to carry additional geometry data used by deck.gl filters. struct Geometry { worldPosition: vec3<f32>, worldPositionAlt: vec3<f32>, position: vec4<f32>, uv: vec2<f32>, pickingColor: vec3<f32>, }; // @group(0) @binding(1) var<private> geometry: Geometry; `; export const projectWGSL = /* wgsl */ `\ ${projectWGSLHeader} // ----------------------------------------------------------------------------- // Functions // ----------------------------------------------------------------------------- // Returns an adjustment factor for commonUnitsPerMeter fn _project_size_at_latitude(lat: f32) -> f32 { let y = clamp(lat, -89.9, 89.9); return 1.0 / cos(radians(y)); } // Overloaded version: scales a value in meters at a given latitude. fn _project_size_at_latitude_m(meters: f32, lat: f32) -> f32 { return meters * project.commonUnitsPerMeter.z * _project_size_at_latitude(lat); } // Computes a non-linear scale factor based on geometry. // (Note: This function relies on "geometry" being provided.) fn project_size() -> f32 { if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR && project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT && project.pseudoMeters == 0) { if (geometry.position.w == 0.0) { return _project_size_at_latitude(geometry.worldPosition.y); } let y: f32 = geometry.position.y / TILE_SIZE * 2.0 - 1.0; let y2 = y * y; let y4 = y2 * y2; let y6 = y4 * y2; return 1.0 + 4.9348 * y2 + 4.0587 * y4 + 1.5642 * y6; } return 1.0; } // Overloads to scale offsets (meters to world units) fn project_size_float(meters: f32) -> f32 { return meters * project.commonUnitsPerMeter.z * project_size(); } fn project_size_vec2(meters: vec2<f32>) -> vec2<f32> { return meters * project.commonUnitsPerMeter.xy * project_size(); } fn project_size_vec3(meters: vec3<f32>) -> vec3<f32> { return meters * project.commonUnitsPerMeter * project_size(); } fn project_size_vec4(meters: vec4<f32>) -> vec4<f32> { return vec4<f32>(meters.xyz * project.commonUnitsPerMeter, meters.w); } // Returns a rotation matrix aligning the z‑axis with the given up vector. fn project_get_orientation_matrix(up: vec3<f32>) -> mat3x3<f32> { let uz = normalize(up); let ux = select( vec3<f32>(1.0, 0.0, 0.0), normalize(vec3<f32>(uz.y, -uz.x, 0.0)), abs(uz.z) == 1.0 ); let uy = cross(uz, ux); return mat3x3<f32>(ux, uy, uz); } // Since WGSL does not support "out" parameters, we return a struct. struct RotationResult { needsRotation: bool, transform: mat3x3<f32>, }; fn project_needs_rotation(commonPosition: vec3<f32>) -> RotationResult { if (project.projectionMode == PROJECTION_MODE_GLOBE) { return RotationResult(true, project_get_orientation_matrix(commonPosition)); } else { return RotationResult(false, mat3x3<f32>()); // identity alternative if needed }; } // Projects a normal vector from the current coordinate system to world space. fn project_normal(vector: vec3<f32>) -> vec3<f32> { let normal_modelspace = project.modelMatrix * vec4<f32>(vector, 0.0); var n = normalize(normal_modelspace.xyz * project.commonUnitsPerMeter); let rotResult = project_needs_rotation(geometry.position.xyz); if (rotResult.needsRotation) { n = rotResult.transform * n; } return n; } // Applies a scale offset based on y-offset (dy) fn project_offset_(offset: vec4<f32>) -> vec4<f32> { let dy: f32 = offset.y; let commonUnitsPerWorldUnit = project.commonUnitsPerWorldUnit + project.commonUnitsPerWorldUnit2 * dy; return vec4<f32>(offset.xyz * commonUnitsPerWorldUnit, offset.w); } // Projects lng/lat coordinates to a unit tile [0,1] fn project_mercator_(lnglat: vec2<f32>) -> vec2<f32> { var x = lnglat.x; if (project.wrapLongitude != 0) { x = ((x + 180.0) % 360.0) - 180.0; } let y = clamp(lnglat.y, -89.9, 89.9); return vec2<f32>( radians(x) + PI, PI + log(tan(PI * 0.25 + radians(y) * 0.5)) ) * WORLD_SCALE; } // Projects lng/lat/z coordinates for a globe projection. fn project_globe_(lnglatz: vec3<f32>) -> vec3<f32> { let lambda = radians(lnglatz.x); let phi = radians(lnglatz.y); let cosPhi = cos(phi); let D = (lnglatz.z / EARTH_RADIUS + 1.0) * GLOBE_RADIUS; return vec3<f32>( sin(lambda) * cosPhi, -cos(lambda) * cosPhi, sin(phi) ) * D; } // Projects positions (with an optional 64-bit low part) from the input // coordinate system to the common space. fn project_position_vec4_f64(position: vec4<f32>, position64Low: vec3<f32>) -> vec4<f32> { var position_world = project.modelMatrix * position; // Work around for a Mac+NVIDIA bug: if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR) { if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) { return vec4<f32>( project_mercator_(position_world.xy), _project_size_at_latitude_m(position_world.z, position_world.y), position_world.w ); } if (project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN) { position_world = vec4f(position_world.xyz + project.coordinateOrigin, position_world.w); } } if (project.projectionMode == PROJECTION_MODE_GLOBE) { if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) { return vec4<f32>( project_globe_(position_world.xyz), position_world.w ); } } if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) { if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) { if (abs(position_world.y - project.coordinateOrigin.y) > 0.25) { return vec4<f32>( project_mercator_(position_world.xy) - project.commonOrigin.xy, project_size_float(position_world.z), position_world.w ); } } } if (project.projectionMode == PROJECTION_MODE_IDENTITY || (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET && (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT || project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN))) { position_world = vec4f(position_world.xyz - project.coordinateOrigin, position_world.w); } return project_offset_(position_world) + project_offset_(project.modelMatrix * vec4<f32>(position64Low, 0.0)); } // Overloaded versions for different input types. fn project_position_vec4_f32(position: vec4<f32>) -> vec4<f32> { return project_position_vec4_f64(position, ZERO_64_LOW); } fn project_position_vec3_f64(position: vec3<f32>, position64Low: vec3<f32>) -> vec3<f32> { let projected_position = project_position_vec4_f64(vec4<f32>(position, 1.0), position64Low); return projected_position.xyz; } fn project_position_vec3_f32(position: vec3<f32>) -> vec3<f32> { let projected_position = project_position_vec4_f64(vec4<f32>(position, 1.0), ZERO_64_LOW); return projected_position.xyz; } fn project_position_vec2_f32(position: vec2<f32>) -> vec2<f32> { let projected_position = project_position_vec4_f64(vec4<f32>(position, 0.0, 1.0), ZERO_64_LOW); return projected_position.xy; } // Transforms a common space position to clip space. fn project_common_position_to_clipspace_with_projection(position: vec4<f32>, viewProjectionMatrix: mat4x4<f32>, center: vec4<f32>) -> vec4<f32> { return viewProjectionMatrix * position + center; } // Uses the project viewProjectionMatrix and center. fn project_common_position_to_clipspace(position: vec4<f32>) -> vec4<f32> { return project_common_position_to_clipspace_with_projection(position, project.viewProjectionMatrix, project.center); } // Returns a clip space offset corresponding to a given number of screen pixels. fn project_pixel_size_to_clipspace(pixels: vec2<f32>) -> vec2<f32> { let offset = pixels / project.viewportSize * project.devicePixelRatio * 2.0; return offset * project.focalDistance; } fn project_meter_size_to_pixel(meters: f32) -> f32 { return project_size_float(meters) * project.scale; } fn project_unit_size_to_pixel(size: f32, unit: i32) -> f32 { if (unit == UNIT_METERS) { return project_meter_size_to_pixel(size); } else if (unit == UNIT_COMMON) { return size * project.scale; } // UNIT_PIXELS: no scaling applied. return size; } fn project_pixel_size_float(pixels: f32) -> f32 { return pixels / project.scale; } fn project_pixel_size_vec2(pixels: vec2<f32>) -> vec2<f32> { return pixels / project.scale; } `;