From f7a79cdeb4d457106abacb88de2918190e7aba80 Mon Sep 17 00:00:00 2001 From: nilscb Date: Thu, 12 Oct 2023 12:13:48 +0200 Subject: [PATCH] perf: Performance improvements to makeFullMesh function in MapLayer #1662 (#1689) * In progress * In progress * In prcess of using typed arrays in web worker * Got it working with trianlges. Next is lines. * Lines working.. next optimize lines by using indeces instead.. * Lines by indices works..next is constant over a cell * Constant over a cell works (and lines).. next transferable objects * Dont draw inactice cell. Modified cellCenteredPropertiesLayer story with one undefined cell * Removed MeshType etc and send all attributes as typed arrays. Next.. send them transerable.. * Tranferable objects as return values from webworker works..next make webworker optional.. * Removed webworker. * Send normals as signed 8 bit to save memory. * In progress * Lint & typecheck * smal fix * performance: Performance improvements to makeFullMesh function in MapLayer #1662 * Code comment fixes. * Code comment fixes. - use camelcase for names. Added comments to functions to explain behaviior. * Lint fix. * Code comment fix. Changed deprecated ComponentStory to StoryFn * Storybook complained about this one. --- .../src/layers/map/fragment.fs.glsl.ts | 18 +- .../src/layers/map/fragment_lines.glsl.ts | 7 - .../src/layers/map/mapLayer.stories.tsx | 110 +++- .../src/layers/map/mapLayer.ts | 198 +++---- .../src/layers/map/privateMapLayer.ts | 62 +- .../subsurface-viewer/src/layers/map/utils.ts | 529 ++++++++++++++++++ .../src/layers/map/webworker.ts | 484 ---------------- 7 files changed, 773 insertions(+), 635 deletions(-) create mode 100644 typescript/packages/subsurface-viewer/src/layers/map/utils.ts delete mode 100644 typescript/packages/subsurface-viewer/src/layers/map/webworker.ts diff --git a/typescript/packages/subsurface-viewer/src/layers/map/fragment.fs.glsl.ts b/typescript/packages/subsurface-viewer/src/layers/map/fragment.fs.glsl.ts index b329756f3..c7487f142 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/fragment.fs.glsl.ts +++ b/typescript/packages/subsurface-viewer/src/layers/map/fragment.fs.glsl.ts @@ -34,21 +34,19 @@ uniform bool isColorMapClampColorTransparent; uniform bool smoothShading; -void main(void) { +void main(void) { geometry.uv = vTexCoord; vec3 normal = normals_commonspace; + // These are sent as Int8 + normal[0] /= 127.0; + normal[1] /= 127.0; + normal[2] /= 127.0; - if (!smoothShading) { + if (!smoothShading || (normal[0] == 0.0 && normal[1] == 0.0 && normal[2] == 0.0)) { normal = normalize(cross(dFdx(position_commonspace.xyz), dFdy(position_commonspace.xyz))); - } - - // // Discard transparent pixels. KEEP - // if (!picking_uActive && isnan(propertyValue)) { - // discard; - // return; - // } - + } + //Picking pass. if (picking_uActive) { // Express triangle index in 255 system. diff --git a/typescript/packages/subsurface-viewer/src/layers/map/fragment_lines.glsl.ts b/typescript/packages/subsurface-viewer/src/layers/map/fragment_lines.glsl.ts index 4bbe93a66..fbab63295 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/fragment_lines.glsl.ts +++ b/typescript/packages/subsurface-viewer/src/layers/map/fragment_lines.glsl.ts @@ -7,13 +7,6 @@ precision highp float; out vec4 fragColor; void main(void) { - - // Picking pass. - if (picking_uActive) { - discard; - return; - } - fragColor = vec4(0.0, 0.0, 0.0, 1.0); } `; diff --git a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx index 22e4da453..e1fb85205 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx +++ b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.stories.tsx @@ -5,17 +5,19 @@ import type { ViewsType } from "../../components/Map"; import { useHoverInfo } from "../../components/Map"; import SubsurfaceViewer from "../../SubsurfaceViewer"; import InfoCard from "../../components/InfoCard"; -import type { ComponentStory, ComponentMeta } from "@storybook/react"; +import type { ComponentStory, ComponentMeta, StoryFn } from "@storybook/react"; import { Slider } from "@mui/material"; import { ContinuousLegend, ColorLegend, createColorMapFunction, } from "@emerson-eps/color-tables"; -import MapLayer from "./mapLayer"; import Axes2DLayer from "../axes2d/axes2DLayer"; +import AxesLayer from "../axes/axesLayer"; +import MapLayer from "./mapLayer"; import { ViewFooter } from "../../components/ViewFooter"; import { View } from "@deck.gl/core/typed"; +import type { colorMapFunctionType } from "../utils/layerTools"; const PREFIX = "MapLayer3dPng"; @@ -179,7 +181,7 @@ const cellCenteredPropertiesLayer = { // One property pr cell. propertiesData: [0.9, 1.0, 1.1, - 0.6, 0.7, 0.8, + 0.6, undefined, 0.8, 0.3, 0.4, 0.5, 0.0, 0.1, 0.2], /*eslint-enable */ @@ -871,6 +873,108 @@ NodeCenteredPropMapWithArrayInput.parameters = { }, }; +function makeGaussian(amplitude, x0, y0, stdX, stdY) { + return function (amplitude, x0, y0, stdX, stdY, x, y) { + const exponent = -( + Math.pow(x - x0, 2) / (2 * Math.pow(stdX, 2)) + + Math.pow(y - y0, 2) / (2 * Math.pow(stdY, 2)) + ); + return amplitude * Math.pow(Math.E, exponent); + }.bind(null, amplitude, x0, y0, stdX, stdY); +} + +function makeData(n: number, amplitude: number): Float32Array { + const X0 = 0; + const Y0 = 0; + const stdX = 75; + const stdY = 50; + const f = makeGaussian(amplitude, X0, Y0, stdX, stdY); + + const data = new Float32Array(n * n).map((val, index) => { + const x = (index % n) - n / 2; + const y = Math.floor(index / n) - n / 2; + return f(x, y); // keep + 0.3 * Math.random(); + }); + + return data; +} + +//-- MapLayer with native javascript arrays as input -- +const TypedArrayInputStory: StoryFn = (args: { + dimension: number; +}) => { + const subsurfaceViewerArgs = { + id: "map", + layers: [ + new MapLayer({ + frame: { + origin: [-args.dimension / 2, -args.dimension / 2], + count: [args.dimension, args.dimension], + increment: [1, 1], + rotDeg: 0, + }, + meshData: makeData(args.dimension, 99), + propertiesData: makeData(args.dimension, 1), + gridLines: false, + material: true, + ZIncreasingDownwards: false, + contours: [0, 5], + colorMapFunction: nearestColorMap as colorMapFunctionType, + }), + new AxesLayer({ + ZIncreasingDownwards: false, + bounds: [ + -args.dimension / 2, + -args.dimension / 2, + -10, + args.dimension / 2, + args.dimension / 2, + 60, + ], + }), + ], + cameraPosition: { + rotationOrbit: 45, + rotationX: 45, + zoom: [-100, -100, -10, 100, 100, 60], + target: [0, 0, 0], + }, + views: { + layout: [1, 1], + viewports: [ + { + id: "view_1", + show3D: true, + }, + ], + }, + }; + return ; +}; + +export const TypedArrayInput: StoryFn = (args) => { + return ; +}; + +TypedArrayInput.args = { + dimension: 300, +}; + +TypedArrayInput.argTypes = { + dimension: { + control: { type: "range", min: 150, max: 300, step: 1 }, + }, +}; + +TypedArrayInput.parameters = { + docs: { + ...defaultParameters.docs, + description: { + story: "Both mesh and property data given as typed arrays arrays (as opposed to URL).", + }, + }, +}; + export const GradientFunctionColorMap: ComponentStory< typeof SubsurfaceViewer > = () => { diff --git a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.ts b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.ts index 1d88c4185..47aeabf5d 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.ts +++ b/typescript/packages/subsurface-viewer/src/layers/map/mapLayer.ts @@ -9,7 +9,7 @@ import type { import { getModelMatrix } from "../utils/layerTools"; import { isEqual } from "lodash"; import * as png from "@vivaxy/png"; -import { makeFullMesh } from "./webworker"; +import { makeFullMesh } from "./utils"; import type { Matrix4 } from "math.gl"; // Rotate x,y around x0, y0 rad radians @@ -54,12 +54,18 @@ export type Params = { isMesh: boolean; frame: Frame; smoothShading: boolean; + gridLines: boolean; + ZIncreasingDownwards: boolean; }; -async function load_mesh_and_properties( - meshData: string | number[], - propertiesData: string | number[], - ZIncreasingDownwards: boolean +/** + * Will load data for the mesh and the properties. Both of which may be given as arrays (javascript or typed) + * or as a URL to the data in binary format. + * Return value: A promise with the data given as typed arrays. + */ +async function loadMeshAndProperties( + meshData: string | number[] | Float32Array, + propertiesData: string | number[] | Float32Array ) { // Keep //const t0 = performance.now(); @@ -77,7 +83,10 @@ async function load_mesh_and_properties( //-- PROPERTIES. -- let properties: Float32Array; - if (Array.isArray(propertiesData)) { + if (ArrayBuffer.isView(propertiesData)) { + // Input data is typed array. + properties = propertiesData; // Note no copy. Make sure input data is not altered. + } else if (Array.isArray(propertiesData)) { // Input data is native javascript array. properties = new Float32Array(propertiesData); } else { @@ -121,7 +130,10 @@ async function load_mesh_and_properties( //-- MESH -- let mesh: Float32Array = new Float32Array(); if (isMesh) { - if (Array.isArray(meshData)) { + if (ArrayBuffer.isView(meshData)) { + // Input data is typed array. + mesh = meshData; // Note no copy. Make sure data is never altered. + } else if (Array.isArray(meshData)) { // Input data is native javascript array. mesh = new Float32Array(meshData); } else { @@ -163,15 +175,9 @@ async function load_mesh_and_properties( } } - if (!ZIncreasingDownwards) { - for (let i = 0; i < mesh.length; i++) { - mesh[i] *= -1; - } - } - - //const t1 = performance.now(); // Keep this. - //console.log(`Task loading took ${(t1 - t0) * 0.001} seconds.`); + // const t1 = performance.now(); + // console.debug(`Task loading took ${(t1 - t0) * 0.001} seconds.`); return Promise.all([isMesh, mesh, properties]); } @@ -183,7 +189,7 @@ export interface MapLayerProps extends ExtendedLayerProps { /** Url to the height (z values) mesh. */ meshUrl: string; // Deprecated - meshData: string | number[]; + meshData: string | number[] | Float32Array; /** Horizontal extent of the terrain mesh. Format: { @@ -203,7 +209,7 @@ export interface MapLayerProps extends ExtendedLayerProps { * colored. */ propertiesUrl: string; // Deprecated - propertiesData: string | number[]; + propertiesData: string | number[] | Float32Array; /** Contourlines reference point and interval. * A value of [-1.0, -1.0] will disable contour lines. @@ -321,94 +327,90 @@ export default class MapLayer extends CompositeLayer { const propertiesData = this.props.propertiesData ?? this.props.propertiesUrl; - const p = load_mesh_and_properties( - meshData, - propertiesData, - this.props.ZIncreasingDownwards - ); + const p = loadMeshAndProperties(meshData, propertiesData); p.then(([isMesh, meshData, propertiesData]) => { - // Using inline web worker for calculating the triangle mesh from - // loaded input data so not to halt the GUI thread. - const blob = new Blob( - ["self.onmessage = ", makeFullMesh.toString()], - { type: "text/javascript" } - ); - const url = URL.createObjectURL(blob); - const webWorker = new Worker(url); - - const webworkerParams = { + const params: Params = { meshData, propertiesData, isMesh, frame: this.props.frame, smoothShading: this.props.smoothShading, + gridLines: this.props.gridLines, + ZIncreasingDownwards: this.props.ZIncreasingDownwards, }; - webWorker.postMessage(webworkerParams); - webWorker.onmessage = (e) => { - const [mesh, mesh_lines, meshZValueRange, propertyValueRange] = - e.data; + const [ + positions, + normals, + triangleIndices, + vertexProperties, + vertexIndices, + lineIndices, + meshZValueRange, + propertyValueRange, + ] = makeFullMesh(params); - this.setState({ - ...this.state, - mesh, - mesh_lines, - propertyValueRange, - }); + this.setState({ + ...this.state, + positions, + normals, + triangleIndices, + vertexProperties, + vertexIndices, + lineIndices, + propertyValueRange, + }); - if ( - typeof this.props.setReportedBoundingBox !== "undefined" && - reportBoundingBox - ) { - const xinc = this.props.frame?.increment?.[0] ?? 0; - const yinc = this.props.frame?.increment?.[1] ?? 0; - - const nnodes_x = this.props.frame?.count?.[0] ?? 2; // number of nodes in x direction - const nnodes_y = this.props.frame?.count?.[1] ?? 2; - - const xMin = this.props.frame?.origin?.[0] ?? 0; - const yMin = this.props.frame?.origin?.[1] ?? 0; - const zMin = -meshZValueRange[0]; - const xMax = xMin + xinc * (nnodes_x - 1); - const yMax = yMin + yinc * (nnodes_y - 1); - const zMax = -meshZValueRange[1]; - - // If map is rotated the bounding box must reflect that. - const center = - this.props.frame.rotPoint ?? this.props.frame.origin; - const rotDeg = this.props.frame.rotDeg ?? 0; - const rotRad = (rotDeg * (2.0 * Math.PI)) / 360.0; - - // Rotate x,y around "center" "rad" radians - const [x0, y0] = rotate(xMin, yMin, center[0], center[1], rotRad); // eslint-disable-line - const [x1, y1] = rotate(xMax, yMin, center[0], center[1], rotRad); // eslint-disable-line - const [x2, y2] = rotate(xMax, yMax, center[0], center[1], rotRad); // eslint-disable-line - const [x3, y3] = rotate(xMin, yMax, center[0], center[1], rotRad); // eslint-disable-line - - // Rotated bounds in x/y plane. - const x_min = Math.min(x0, x1, x2, x3); - const x_max = Math.max(x0, x1, x2, x3); - const y_min = Math.min(y0, y1, y2, y3); - const y_max = Math.max(y0, y1, y2, y3); - - this.props.setReportedBoundingBox([ - x_min, - y_min, - zMin, - x_max, - y_max, - zMax, - ]); - } - - webWorker.terminate(); - - this.setState({ - ...this.state, - isFinishedLoading: true, - }); - }; + if ( + typeof this.props.setReportedBoundingBox !== "undefined" && + reportBoundingBox + ) { + const xinc = this.props.frame?.increment?.[0] ?? 0; + const yinc = this.props.frame?.increment?.[1] ?? 0; + + const nnodes_x = this.props.frame?.count?.[0] ?? 2; // number of nodes in x direction + const nnodes_y = this.props.frame?.count?.[1] ?? 2; + + const xMin = this.props.frame?.origin?.[0] ?? 0; + const yMin = this.props.frame?.origin?.[1] ?? 0; + const zMin = -meshZValueRange[0]; + const xMax = xMin + xinc * (nnodes_x - 1); + const yMax = yMin + yinc * (nnodes_y - 1); + const zMax = -meshZValueRange[1]; + + // If map is rotated the bounding box must reflect that. + const center = + this.props.frame.rotPoint ?? this.props.frame.origin; + const rotDeg = this.props.frame.rotDeg ?? 0; + const rotRad = (rotDeg * (2.0 * Math.PI)) / 360.0; + + // Rotate x,y around "center" "rad" radians + const [x0, y0] = rotate(xMin, yMin, center[0], center[1], rotRad); // eslint-disable-line + const [x1, y1] = rotate(xMax, yMin, center[0], center[1], rotRad); // eslint-disable-line + const [x2, y2] = rotate(xMax, yMax, center[0], center[1], rotRad); // eslint-disable-line + const [x3, y3] = rotate(xMin, yMax, center[0], center[1], rotRad); // eslint-disable-line + + // Rotated bounds in x/y plane. + const x_min = Math.min(x0, x1, x2, x3); + const x_max = Math.max(x0, x1, x2, x3); + const y_min = Math.min(y0, y1, y2, y3); + const y_max = Math.max(y0, y1, y2, y3); + + this.props.setReportedBoundingBox([ + x_min, + y_min, + zMin, + x_max, + y_max, + zMax, + ]); + } + + this.setState({ + ...this.state, + isFinishedLoading: true, + }); }); } @@ -478,8 +480,12 @@ export default class MapLayer extends CompositeLayer { const layer = new privateMapLayer( this.getSubLayerProps({ - mesh: this.state["mesh"], - meshLines: this.state["mesh_lines"], + positions: this.state["positions"], + normals: this.state["normals"], + triangleIndices: this.state["triangleIndices"], + vertexProperties: this.state["vertexProperties"], + vertexIndices: this.state["vertexIndices"], + lineIndices: this.state["lineIndices"], pickable: this.props.pickable, modelMatrix: rotatingModelMatrix, contours: this.props.contours, diff --git a/typescript/packages/subsurface-viewer/src/layers/map/privateMapLayer.ts b/typescript/packages/subsurface-viewer/src/layers/map/privateMapLayer.ts index ede0bee1c..22eed33cb 100644 --- a/typescript/packages/subsurface-viewer/src/layers/map/privateMapLayer.ts +++ b/typescript/packages/subsurface-viewer/src/layers/map/privateMapLayer.ts @@ -32,27 +32,6 @@ const DEFAULT_TEXTURE_PARAMETERS = { [GL.TEXTURE_WRAP_T]: GL.CLAMP_TO_EDGE, }; -export type MeshType = { - drawMode?: number; - attributes: { - positions: { value: Float32Array; size: number }; - TEXCOORD_0?: { value: Float32Array; size: number }; - normals: { value: Float32Array; size: number }; - properties: { value: Float32Array; size: number }; - vertex_indexs: { value: Int32Array; size: number }; - }; - vertexCount: number; - indices: { value: Uint32Array; size: number }; -}; - -export type MeshTypeLines = { - drawMode: number; - attributes: { - positions: { value: Float32Array; size: number }; - }; - vertexCount: number; -}; - export type Material = | { ambient: number; @@ -62,8 +41,12 @@ export type Material = } | boolean; export interface privateMapLayerProps extends ExtendedLayerProps { - mesh: MeshType; - meshLines: MeshTypeLines; + positions: Float32Array; + normals: Int8Array; + triangleIndices: Uint32Array; + vertexProperties: Float32Array; + vertexIndices: Int32Array; + lineIndices: Uint32Array; contours: [number, number]; gridLines: boolean; isContoursDepth: boolean; @@ -131,15 +114,17 @@ export default class privateMapLayer extends Layer { vs: vsShader, fs: fsShader, geometry: new Geometry({ - drawMode: this.props.mesh.drawMode, + drawMode: gl.TRIANGLES, attributes: { - positions: this.props.mesh.attributes.positions, - normals: this.props.mesh.attributes.normals, - properties: this.props.mesh.attributes.properties, - vertex_indexs: this.props.mesh.attributes.vertex_indexs, + positions: { value: this.props.positions, size: 3 }, + // Only add normals if they are defined. + ...(this.props.normals.length > 0 && { + normals: { value: this.props.normals, size: 3 }, + }), + properties: { value: this.props.vertexProperties, size: 1 }, + vertex_indexs: { value: this.props.vertexIndices, size: 1 }, }, - vertexCount: this.props.mesh.vertexCount, - indices: this.props.mesh.indices, + indices: { value: this.props.triangleIndices, size: 1 }, }), modules: [project, picking, localPhongLighting], isInstanced: false, // This only works when set to false. @@ -150,8 +135,14 @@ export default class privateMapLayer extends Layer { id: `${this.props.id}-lines`, vs: vsLineShader, fs: fsLineShader, - geometry: new Geometry(this.props.meshLines), - modules: [project, picking], + geometry: new Geometry({ + drawMode: gl.LINES, + attributes: { + positions: { value: this.props.positions, size: 3 }, + }, + indices: { value: this.props.lineIndices, size: 1 }, + }), + modules: [project], isInstanced: false, }); @@ -200,7 +191,8 @@ export default class privateMapLayer extends Layer { const isColorMapClampColorTransparent: boolean = (this.props.colorMapClampColor as boolean) === false; - const smoothShading = this.props.smoothShading; + const smoothShading = + this.props.normals.length == 0 ? false : this.props.smoothShading; gl.enable(GL.POLYGON_OFFSET_FILL); if (!this.props.depthTest) { @@ -269,11 +261,11 @@ export default class privateMapLayer extends Layer { const vertexIndex = 256 * 256 * r + 256 * g + b; - const vertexs = this.props.mesh.attributes.positions.value; + const vertexs = this.props.positions; const depth = -vertexs[3 * vertexIndex + 2]; layer_properties.push(createPropertyData("Depth", depth)); - const properties = this.props.mesh.attributes.properties.value; + const properties = this.props.vertexProperties; const property = properties[vertexIndex]; layer_properties.push(createPropertyData("Property", property)); diff --git a/typescript/packages/subsurface-viewer/src/layers/map/utils.ts b/typescript/packages/subsurface-viewer/src/layers/map/utils.ts new file mode 100644 index 000000000..d33528642 --- /dev/null +++ b/typescript/packages/subsurface-viewer/src/layers/map/utils.ts @@ -0,0 +1,529 @@ +import type { Params } from "./mapLayer"; + +type Vec = [number, number, number]; + +function getFloat32ArrayMinMax(data: Float32Array) { + let max = -99999999; + let min = 99999999; + for (let i = 0; i < data.length; i++) { + max = data[i] > max ? data[i] : max; + min = data[i] < min ? data[i] : min; + } + return [min, max]; +} + +function crossProduct(a: Vec, b: Vec): Vec { + const c = [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ]; + return c as Vec; +} + +// Check if a number exists and has a defined value. +function isDefined(x: unknown): boolean { + return typeof x === "number" && !isNaN(x); +} + +function normalize(a: Vec): void { + const L = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); + a[0] /= L; + a[1] /= L; + a[2] /= L; +} + +function calcNormal( + w: number, + h: number, + nx: number, + ny: number, + isMesh: boolean, + smoothShading: boolean, + meshData: Float32Array, + ox: number, + oy: number, + dx: number, + dy: number, + multZ: number +) { + if (!smoothShading) { + return [1, 1, 1]; + } + + if (!isMesh) { + return [0, 0, 1]; + } + + const i0 = h * nx + w; + const i1 = h * nx + (w - 1); + const i2 = (h + 1) * nx + w; + const i3 = h * nx + (w + 1); + const i4 = (h - 1) * nx + w; + + const i0_act = isDefined(meshData[i0]); // eslint-disable-line + const i1_act = (w - 1) >= 0 && isDefined(meshData[i1]); // eslint-disable-line + const i2_act = (h + 1) < ny && isDefined(meshData[i2]); // eslint-disable-line + const i3_act = (w + 1) < nx && isDefined(meshData[i3]); // eslint-disable-line + const i4_act = (h - 1) >= 0 && isDefined(meshData[i4]); // eslint-disable-line + + const noNormal = [0, 0, 0]; // signals a normal could not be calculated. + if (!i0_act) { + return noNormal; + } + + const hh = ny - 1 - h; // Note use hh for h for getting y values. + const p0 = [ox + w * dx, oy + hh * dy, i0_act ? -meshData[i0] * multZ : 0]; // eslint-disable-line + const p1 = [ ox + (w - 1) * dx, oy + hh * dy, i1_act ? -meshData[i1] * multZ : 0]; // eslint-disable-line + const p2 = [ ox + w * dx, oy + (hh + 1) * dy, i2_act ? -meshData[i2] * multZ : 0]; // eslint-disable-line + const p3 = [ ox + (w + 1) * dx, oy + hh * dy, i3_act ? -meshData[i3] * multZ : 0]; // eslint-disable-line + const p4 = [ ox + w * dx, oy + (hh - 1) * dy, i4_act ? -meshData[i4] * multZ : 0]; // eslint-disable-line + + const v1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]] as Vec; + const v2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]] as Vec; + const v3 = [p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2]] as Vec; + const v4 = [p4[0] - p0[0], p4[1] - p0[1], p4[2] - p0[2]] as Vec; + + // Estimating a normal vector at p0: + // Take cross product of vectors v1, v2, + // Do this for all 4 quadrants. + // The resulting normal will be the mean of these four normals. + // p2 + // | + // p1 - p0 - p3 + // | + // p4 + + const normals: Vec[] = []; + if (i1_act && i2_act) { + const normal = crossProduct(v2, v1); + normals.push(normal); + } + + if (i2_act && i3_act) { + const normal = crossProduct(v3, v2); + normals.push(normal); + } + + if (i3_act && i4_act) { + const normal = crossProduct(v4, v3); + normals.push(normal); + } + + if (i4_act && i1_act) { + const normal = crossProduct(v1, v4); + normals.push(normal); + } + + if (normals.length === 0) { + return noNormal; + } + + const mean = normals[0]; + for (let i = 1; i < normals.length; i++) { + mean[0] += normals[i][0]; + mean[1] += normals[i][1]; + mean[2] += normals[i][2]; + } + + normalize(mean); + return mean; +} + +/** Given the input data will build and return the attributes (vertices and indices for triangles and lines) + * that is used by WebGl. Using indice, lines and triangles share common vertices to save memory. + */ +export function makeFullMesh(params: Params) { + // Keep + //const t0 = performance.now(); + + const meshData = params.meshData; + const propertiesData = params.propertiesData; + const isMesh = params.isMesh; + const frame = params.frame; + const smoothShading = params.smoothShading; + const gridLines = params.gridLines; + const ZIncreasingDownwards = params.ZIncreasingDownwards; + + const multZ = ZIncreasingDownwards ? 1 : -1; + + const meshZValueRange = getFloat32ArrayMinMax(meshData); + const propertyValueRange = getFloat32ArrayMinMax(propertiesData); + + // Dimensions. + const ox = frame.origin[0]; + const oy = frame.origin[1]; + + const dx = frame.increment[0]; + const dy = frame.increment[1]; + + const nx = frame.count[0]; // number of nodes in x direction + const ny = frame.count[1]; + + const propLength = propertiesData.length; + const isCellCenteredProperties = propLength === (nx - 1) * (ny - 1); + + if (propLength !== (nx - 1) * (ny - 1) && propLength !== nx * ny) { + console.error( + "There should be as many property values as nodes (nx*ny) OR as many as cells (nx - 1) * (ny - 1)." + ); + } + + const nNodes = nx * ny; + const nCells = (nx - 1) * (ny - 1); + const nTriangles = nCells * 2; + + const positions = new Float32Array( + isCellCenteredProperties ? nCells * 6 * 3 : nNodes * 3 + ); + const normals = new Int8Array( + isCellCenteredProperties || !smoothShading ? 0 : nNodes * 3 + ); + const triangleIndices = new Uint32Array(nTriangles * 3); + const vertexProperties = new Float32Array( + isCellCenteredProperties ? nCells * 6 : nNodes + ); + const vertexIndices = new Int32Array( + isCellCenteredProperties ? nCells * 6 : nNodes + ); + let nLineIndices = 0; + if (gridLines) { + nLineIndices = isCellCenteredProperties + ? nTriangles * 2 * 2 + : nCells * 4 + (nx - 1) * 2 + (ny - 1) * 2; + } + const lineIndices = new Uint32Array(nLineIndices); + + // Note: Assumed layout of the incomming 2D array of data: + // First coloumn corresponds to lowest x value. Last column highest x value. + // First row corresponds to max y value. Last row corresponds to lowest y value. + // This must be taken into account when calculating vertex x,y values and texture coordinates. + + if (!isCellCenteredProperties) { + // PROPERTIES IS SET INTERPOLATED OVER A CELL. + // Loop vertices. + let i = 0; + for (let h = 0; h < ny; h++) { + for (let w = 0; w < nx; w++) { + const i0 = h * nx + w; + + const x0 = ox + w * dx; + const y0 = oy + (ny - 1 - h) * dy; // See note above. + const z = isMesh ? -meshData[i0] * multZ : 0; + + const propertyValue = propertiesData[i0]; + + positions[3 * i + 0] = x0; + positions[3 * i + 1] = y0; + positions[3 * i + 2] = z; + + if (smoothShading) { + const normal = calcNormal(w, h, nx, ny, isMesh, smoothShading, meshData, ox, oy, dx, dy, multZ); // eslint-disable-line + normals[3 * i + 0] = normal[0] * 127; // Normalize to signed 8 bit. + normals[3 * i + 1] = normal[1] * 127; + normals[3 * i + 2] = normal[2] * 127; + } + + vertexProperties[i] = propertyValue; + vertexIndices[i] = i; + + i++; + } + } + + // Loop cells. + i = 0; + let j = 0; + for (let h = 0; h < ny - 1; h++) { + for (let w = 0; w < nx - 1; w++) { + const i0 = h * nx + w; + const i1 = h * nx + (w + 1); + const i2 = (h + 1) * nx + (w + 1); + const i3 = (h + 1) * nx + w; + + const i0_act = !isMesh || (isDefined(meshData[i0]) && isDefined(propertiesData[i0])); // eslint-disable-line + const i1_act = !isMesh || (isDefined(meshData[i1]) && isDefined(propertiesData[i1])); // eslint-disable-line + const i2_act = !isMesh || (isDefined(meshData[i2]) && isDefined(propertiesData[i2])); // eslint-disable-line + const i3_act = !isMesh || (isDefined(meshData[i3]) && isDefined(propertiesData[i3])); // eslint-disable-line + + // Triangles. + if (i1_act && i3_act) { + // diagonal i1, i3 + if (i0_act) { + // t1 - i0 provoking index. + triangleIndices[i++] = i1; + triangleIndices[i++] = i3; + triangleIndices[i++] = i0; + } + + if (i2_act) { + // t2 - i2 provoking index. + triangleIndices[i++] = i1; + triangleIndices[i++] = i3; + triangleIndices[i++] = i2; + } + } else if (i0_act && i2_act) { + // diagonal i0, i2 + if (i1_act) { + // t1 - i0 provoking index. + triangleIndices[i++] = i1; + triangleIndices[i++] = i2; + triangleIndices[i++] = i0; + } + + if (i3_act) { + // t2 - i2 provoking index. + triangleIndices[i++] = i3; + triangleIndices[i++] = i0; + triangleIndices[i++] = i2; + } + } + + // Lines. + if (gridLines) { + if (i0_act && i1_act) { + lineIndices[j++] = i0; + lineIndices[j++] = i1; + } + + if (i0_act && i3_act) { + lineIndices[j++] = i0; + lineIndices[j++] = i3; + } + + if (h == ny - 2 && i2_act && i3_act) { + lineIndices[j++] = i3; + lineIndices[j++] = i2; + } + + if (w == nx - 2 && i1_act && i2_act) { + lineIndices[j++] = i1; + lineIndices[j++] = i2; + } + + // diagonal + if ((i0_act && !i2_act) || (!i0_act && i2_act)) { + lineIndices[j++] = i1; + lineIndices[j++] = i3; + } + + // diagonal + if ((i3_act && !i1_act) || (!i3_act && i1_act)) { + lineIndices[j++] = i0; + lineIndices[j++] = i2; + } + } + } + } + } else { + // PROPERTIES IS SET CONSTANT OVER A CELL. + + // Loop cells. + let i = 0; + let j = 0; + let k = 0; + let l = 0; + for (let h = 0; h < ny - 1; h++) { + for (let w = 0; w < nx - 1; w++) { + const hh = ny - 1 - h; // See note above. + + const i0 = h * nx + w; + const i1 = h * nx + (w + 1); + const i2 = (h + 1) * nx + (w + 1); + const i3 = (h + 1) * nx + w; + + const i0_act = !isMesh || isDefined(meshData[i0]); // eslint-disable-line + const i1_act = !isMesh || isDefined(meshData[i1]); // eslint-disable-line + const i2_act = !isMesh || isDefined(meshData[i2]); // eslint-disable-line + const i3_act = !isMesh || isDefined(meshData[i3]); // eslint-disable-line + + const x0 = ox + w * dx; + const y0 = oy + hh * dy; + const z0 = isMesh ? -meshData[i0] * multZ : 0; + + const x1 = ox + (w + 1) * dx; + const y1 = oy + hh * dy; + const z1 = isMesh ? -meshData[i1] * multZ : 0; + + const x2 = ox + (w + 1) * dx; + const y2 = oy + (hh - 1) * dy; // Note hh - 1 here. + const z2 = isMesh ? -meshData[i2] * multZ : 0; + + const x3 = ox + w * dx; + const y3 = oy + (hh - 1) * dy; // Note hh - 1 here. + const z3 = isMesh ? -meshData[i3] * multZ : 0; + + const propertyIndex = h * (nx - 1) + w; // (nx - 1) -> the width of the property 2D array is one less than for the nodes in this case. + const propertyValue = propertiesData[propertyIndex]; + + if (!isDefined(propertyValue)) { + // Inactive cell, dont draw. + continue; + } + + // Triangles. + if (i1_act && i3_act) { + // diagonal i1, i3 + if (i0_act) { + // t1 - i0 provoking index. + triangleIndices[i] = i; + const L1 = i; + i++; + positions[j++] = x1; + positions[j++] = y1; + positions[j++] = z1; + + triangleIndices[i] = i; + const L2 = i; + i++; + positions[j++] = x3; + positions[j++] = y3; + positions[j++] = z3; + + triangleIndices[i] = i; + const L3 = i; + i++; + positions[j++] = x0; + positions[j++] = y0; + positions[j++] = z0; + + if (gridLines) { + lineIndices[l++] = L3; + lineIndices[l++] = L1; + + lineIndices[l++] = L3; + lineIndices[l++] = L2; + } + + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + } + + if (i2_act) { + // t2 - i2 provoking index. + triangleIndices[i] = i; + const L1 = i; + i++; + positions[j++] = x1; + positions[j++] = y1; + positions[j++] = z1; + + triangleIndices[i] = i; + const L2 = i; + i++; + positions[j++] = x3; + positions[j++] = y3; + positions[j++] = z3; + + triangleIndices[i] = i; + const L3 = i; + i++; + positions[j++] = x2; + positions[j++] = y2; + positions[j++] = z2; + + if (gridLines) { + lineIndices[l++] = L1; + lineIndices[l++] = L3; + + lineIndices[l++] = L2; + lineIndices[l++] = L3; + } + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + } + } else if (i0_act && i2_act) { + // diagonal i0, i2 + if (i1_act) { + //t1 - i0 provoking index. + triangleIndices[i] = i; + const L1 = i; + i++; + positions[j++] = x1; + positions[j++] = y1; + positions[j++] = z1; + + triangleIndices[i] = i; + const L2 = i; + i++; + positions[j++] = x2; + positions[j++] = y2; + positions[j++] = z2; + + triangleIndices[i] = i; + const L3 = i; + i++; + positions[j++] = x0; + positions[j++] = y0; + positions[j++] = z0; + + if (gridLines) { + lineIndices[l++] = L1; + lineIndices[l++] = L3; + + lineIndices[l++] = L1; + lineIndices[l++] = L2; + } + + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + } + + if (i3_act) { + // t2 - i2 provoking index. + triangleIndices[i] = i; + const L1 = i; + i++; + positions[j++] = x3; + positions[j++] = y3; + positions[j++] = z3; + + triangleIndices[i] = i; + const L2 = i; + i++; + positions[j++] = x0; + positions[j++] = y0; + positions[j++] = z0; + + triangleIndices[i] = i; + const L3 = i; + i++; + positions[j++] = x2; + positions[j++] = y2; + positions[j++] = z2; + + if (gridLines) { + lineIndices[l++] = L1; + lineIndices[l++] = L2; + + lineIndices[l++] = L1; + lineIndices[l++] = L3; + } + + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + vertexProperties[k++] = propertyValue; + } + } + } + } + } + + // Keep this. + // const t1 = performance.now(); + // console.debug(`Task makeMesh took ${(t1 - t0) * 0.001} seconds.`); + + return [ + positions, + normals, + triangleIndices, + vertexProperties, + vertexIndices, + lineIndices, + meshZValueRange, + propertyValueRange, + ]; +} diff --git a/typescript/packages/subsurface-viewer/src/layers/map/webworker.ts b/typescript/packages/subsurface-viewer/src/layers/map/webworker.ts deleted file mode 100644 index 08287f955..000000000 --- a/typescript/packages/subsurface-viewer/src/layers/map/webworker.ts +++ /dev/null @@ -1,484 +0,0 @@ -import type { MeshType, MeshTypeLines } from "./privateMapLayer"; -import type { Params } from "./mapLayer"; - -type Vec = [number, number, number]; - -export function makeFullMesh(e: { data: Params }): void { - const params = e.data; - - // Keep - //const t0 = performance.now(); - - const meshData = params.meshData; - const propertiesData = params.propertiesData; - const isMesh = params.isMesh; - const frame = params.frame; - const smoothShading = params.smoothShading; - - function getFloat32ArrayMinMax(data: Float32Array) { - let max = -99999999; - let min = 99999999; - for (let i = 0; i < data.length; i++) { - max = data[i] > max ? data[i] : max; - min = data[i] < min ? data[i] : min; - } - return [min, max]; - } - - function crossProduct(a: Vec, b: Vec): Vec { - const c = [ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], - ]; - return c as Vec; - } - - // Check if a number exists and has a defined value. - function isDefined(x: unknown): boolean { - return typeof x === "number" && !isNaN(x); - } - - function normalize(a: Vec): void { - const L = Math.sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); - a[0] /= L; - a[1] /= L; - a[2] /= L; - } - - function calcNormal( - w: number, - h: number, - nx: number, - ny: number, - isMesh: boolean, - smoothShading: boolean, - meshData: Float32Array, - ox: number, - oy: number - ) { - if (!smoothShading) { - return [1, 1, 1]; - } - - if (!isMesh) { - return [0, 0, 1]; - } - - const i0 = h * nx + w; - const i1 = h * nx + (w - 1); - const i2 = (h + 1) * nx + w; - const i3 = h * nx + (w + 1); - const i4 = (h - 1) * nx + w; - - const i0_act = isDefined(meshData[i0]); // eslint-disable-line - const i1_act = (w - 1) >= 0 && isDefined(meshData[i1]); // eslint-disable-line - const i2_act = (h + 1) < ny && isDefined(meshData[i2]); // eslint-disable-line - const i3_act = (w + 1) < nx && isDefined(meshData[i3]); // eslint-disable-line - const i4_act = (h - 1) >= 0 && isDefined(meshData[i4]); // eslint-disable-line - - const noNormal = [0, 0, 1]; // signals a normal could not be calculated. - if (!i0_act) { - return noNormal; - } - - const hh = ny - 1 - h; // Note use hh for h for getting y values. - const p0 = [ox + w * dx, oy + hh * dy, i0_act ? -meshData[i0] : 0]; // eslint-disable-line - const p1 = [ ox + (w - 1) * dx, oy + hh * dy, i1_act ? -meshData[i1] : 0]; // eslint-disable-line - const p2 = [ ox + w * dx, oy + (hh + 1) * dy, i2_act ? -meshData[i2] : 0]; // eslint-disable-line - const p3 = [ ox + (w + 1) * dx, oy + hh * dy, i3_act ? -meshData[i3] : 0]; // eslint-disable-line - const p4 = [ ox + w * dx, oy + (hh - 1) * dy, i4_act ? -meshData[i4] : 0]; // eslint-disable-line - - const v1 = [p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]] as Vec; - const v2 = [p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]] as Vec; - const v3 = [p3[0] - p0[0], p3[1] - p0[1], p3[2] - p0[2]] as Vec; - const v4 = [p4[0] - p0[0], p4[1] - p0[1], p4[2] - p0[2]] as Vec; - - // Estimating a normal vector at p0: - // Take cross product of vectors v1, v2, - // Do this for all 4 quadrants. - // The resulting normal will be the mean of these four normals. - // p2 - // | - // p1 - p0 - p3 - // | - // p4 - - const normals: Vec[] = []; - if (i1_act && i2_act) { - const normal = crossProduct(v2, v1); - normals.push(normal); - } - - if (i2_act && i3_act) { - const normal = crossProduct(v3, v2); - normals.push(normal); - } - - if (i3_act && i4_act) { - const normal = crossProduct(v4, v3); - normals.push(normal); - } - - if (i4_act && i1_act) { - const normal = crossProduct(v1, v4); - normals.push(normal); - } - - if (normals.length === 0) { - return noNormal; - } - - const mean = normals[0]; - for (let i = 1; i < normals.length; i++) { - mean[0] += normals[i][0]; - mean[1] += normals[i][1]; - mean[2] += normals[i][2]; - } - - normalize(mean); - return mean; - } - - const meshZValueRange = getFloat32ArrayMinMax(meshData); - const propertyValueRange = getFloat32ArrayMinMax(propertiesData); - - // Dimensions. - const ox = frame.origin[0]; - const oy = frame.origin[1]; - - const dx = frame.increment[0]; - const dy = frame.increment[1]; - - const nx = frame.count[0]; - const ny = frame.count[1]; - - const propLength = propertiesData.length; - const isCellCenteredProperties = propLength === (nx - 1) * (ny - 1); - - if (propLength !== (nx - 1) * (ny - 1) && propLength !== nx * ny) { - console.error( - "There should be as many property values as nodes (nx*ny) OR as many as cells (nx - 1) * (ny - 1)." - ); - } - - const positions: number[] = []; - const normals: number[] = []; - const indices: number[] = []; - const vertexProperties: number[] = []; - const vertexIndexs: number[] = []; - const line_positions: number[] = []; - - // Note: Assumed layout of the incomming 2D array of data: - // First coloumn corresponds to lowest x value. Last column highest x value. - // First row corresponds to max y value. Last row corresponds to lowest y value. - // This must be taken into account when calculating vertex x,y values and texture coordinates. - - if (!isCellCenteredProperties) { - // PROPERTIES IS SET INTERPOLATED OVER A CELL. - let i = 0; - // Loop over nodes. - for (let h = 0; h < ny; h++) { - for (let w = 0; w < nx; w++) { - const i0 = h * nx + w; - - const x0 = ox + w * dx; - const y0 = oy + (ny - 1 - h) * dy; // See note above. - const z = isMesh ? -meshData[i0] : 0; - - const propertyValue = propertiesData[i0]; - - positions.push(x0, y0, z); - - const normal = calcNormal(w, h, nx, ny, isMesh, smoothShading, meshData, ox, oy); // eslint-disable-line - normals.push(normal[0], normal[1], normal[2]); - - vertexProperties.push(propertyValue); - vertexIndexs.push(i++); - } - } - - for (let h = 0; h < ny - 1; h++) { - for (let w = 0; w < nx - 1; w++) { - const i0 = h * nx + w; - const i1 = h * nx + (w + 1); - const i2 = (h + 1) * nx + (w + 1); - const i3 = (h + 1) * nx + w; - - const i0_act = !isMesh || (isDefined(meshData[i0]) && isDefined(propertiesData[i0])); // eslint-disable-line - const i1_act = !isMesh || (isDefined(meshData[i1]) && isDefined(propertiesData[i1])); // eslint-disable-line - const i2_act = !isMesh || (isDefined(meshData[i2]) && isDefined(propertiesData[i2])); // eslint-disable-line - const i3_act = !isMesh || (isDefined(meshData[i3]) && isDefined(propertiesData[i3])); // eslint-disable-line - - const hh = ny - h - 1; // See note above. - - const x0 = ox + w * dx; - const y0 = oy + hh * dy; - const z0 = isMesh ? -meshData[i0] : 0; - - const x1 = ox + (w + 1) * dx; - const y1 = oy + hh * dy; - const z1 = isMesh ? -meshData[i1] : 0; - - const x2 = ox + (w + 1) * dx; - const y2 = oy + (hh - 1) * dy; - const z2 = isMesh ? -meshData[i2] : 0; - - const x3 = ox + w * dx; - const y3 = oy + (hh - 1) * dy; - const z3 = isMesh ? -meshData[i3] : 0; - - if (i1_act && i3_act) { - // diagonal i1, i3 - if (i0_act) { - indices.push(i1, i3, i0); // t1 - i0 provoking index. - - line_positions.push(x0, y0, z0); - line_positions.push(x3, y3, z3); - - line_positions.push(x0, y0, z0); - line_positions.push(x1, y1, z1); - } - - if (i2_act) { - indices.push(i1, i3, i2); // t2 - i2 provoking index. - - line_positions.push(x2, y2, z2); - line_positions.push(x3, y3, z3); - - line_positions.push(x2, y2, z2); - line_positions.push(x1, y1, z1); - } - - // diagonal - if ((i0_act && !i2_act) || (!i0_act && i2_act)) { - line_positions.push(x1, y1, z1); - line_positions.push(x3, y3, z3); - } - } else if (i0_act && i2_act) { - // diagonal i0, i2 - if (i1_act) { - indices.push(i1, i2, i0); // t1 - i0 provoking index. - } - - if (i3_act) { - indices.push(i3, i0, i2); // t2 - i2 provoking index. - } - - // diagonal - if ((i3_act && !i1_act) || (!i3_act && i1_act)) { - line_positions.push(x0, y0, z0); - line_positions.push(x2, y2, z2); - } - } - } - } - } else { - // PROPERTIES IS SET CONSTANT OVER A CELL. - let i_indices = 0; - let i_vertices = 0; - // Loop over cells. - for (let h = 0; h < ny - 1; h++) { - for (let w = 0; w < nx - 1; w++) { - const hh = ny - 1 - h; // See note above. - - const i0 = h * nx + w; - const i1 = h * nx + (w + 1); - const i2 = (h + 1) * nx + (w + 1); - const i3 = (h + 1) * nx + w; - - const normal0 = calcNormal(w, h, nx, ny, isMesh, smoothShading, meshData, ox, oy); // eslint-disable-line - const normal1 = calcNormal(w + 1, h, nx, ny, isMesh, smoothShading, meshData, ox, oy); // eslint-disable-line - const normal2 = calcNormal(w + 1, h + 1, nx, ny, isMesh, smoothShading, meshData, ox, oy); // eslint-disable-line - const normal3 = calcNormal(w, h + 1, nx, ny, isMesh, smoothShading, meshData, ox, oy); // eslint-disable-line - - const i0_act = !isMesh || isDefined(meshData[i0]); // eslint-disable-line - const i1_act = !isMesh || isDefined(meshData[i1]); // eslint-disable-line - const i2_act = !isMesh || isDefined(meshData[i2]); // eslint-disable-line - const i3_act = !isMesh || isDefined(meshData[i3]); // eslint-disable-line - - const x0 = ox + w * dx; - const y0 = oy + hh * dy; - const z0 = isMesh ? -meshData[i0] : 0; - - const x1 = ox + (w + 1) * dx; - const y1 = oy + hh * dy; - const z1 = isMesh ? -meshData[i1] : 0; - - const x2 = ox + (w + 1) * dx; - const y2 = oy + (hh - 1) * dy; // Note hh - 1 here. - const z2 = isMesh ? -meshData[i2] : 0; - - const x3 = ox + w * dx; - const y3 = oy + (hh - 1) * dy; // Note hh - 1 here. - const z3 = isMesh ? -meshData[i3] : 0; - - const propertyIndex = h * (nx - 1) + w; // (nx - 1) -> the width of the property 2D array is one less than for the nodes in this case. - const propertyValue = propertiesData[propertyIndex]; - - if (!isDefined(propertyValue)) { - // Inactive cell, dont draw. - continue; - } - - if (i1_act && i3_act) { - // diagonal i1, i3 - if (i0_act) { - // t1 - i0 provoking index. - positions.push(x1, y1, z1); - positions.push(x3, y3, z3); - positions.push(x0, y0, z0); - - normals.push(normal1[0], normal1[1], normal1[2]); - normals.push(normal3[0], normal3[1], normal3[2]); - normals.push(normal0[0], normal0[1], normal0[2]); - - vertexIndexs.push( - i_vertices++, - i_vertices++, - i_vertices++ - ); - - indices.push(i_indices++, i_indices++, i_indices++); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - - line_positions.push(x0, y0, z0); - line_positions.push(x3, y3, z3); - - line_positions.push(x0, y0, z0); - line_positions.push(x1, y1, z1); - } - - if (i2_act) { - // t2 - i2 provoking index. - positions.push(x1, y1, z1); - positions.push(x3, y3, z3); - positions.push(x2, y2, z2); - - normals.push(normal1[0], normal1[1], normal1[2]); - normals.push(normal3[0], normal3[1], normal3[2]); - normals.push(normal2[0], normal2[1], normal2[2]); - - vertexIndexs.push( - i_vertices++, - i_vertices++, - i_vertices++ - ); - - indices.push(i_indices++, i_indices++, i_indices++); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - - line_positions.push(x2, y2, z2); - line_positions.push(x3, y3, z3); - - line_positions.push(x2, y2, z2); - line_positions.push(x1, y1, z1); - } - - // diagonal - if ((i0_act && !i2_act) || (!i0_act && i2_act)) { - line_positions.push(x1, y1, z1); - line_positions.push(x3, y3, z3); - } - } else if (i0_act && i2_act) { - // diagonal i0, i2 - if (i1_act) { - // t1 - i0 provoking index. - positions.push(x1, y1, z1); - positions.push(x2, y2, z2); - positions.push(x0, y0, z0); - - normals.push(normal1[0], normal1[1], normal1[2]); - normals.push(normal2[0], normal2[1], normal2[2]); - normals.push(normal0[0], normal0[1], normal0[2]); - - vertexIndexs.push( - i_vertices++, - i_vertices++, - i_vertices++ - ); - - indices.push(i_indices++, i_indices++, i_indices++); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - - line_positions.push(x1, y1, z1); - line_positions.push(x0, y0, z0); - - line_positions.push(x1, y1, z1); - line_positions.push(x2, y2, z2); - } - - if (i3_act) { - // t2 - i2 provoking index. - positions.push(x0, y0, z0); - positions.push(x3, y3, z3); - positions.push(x2, y2, z2); - - normals.push(normal0[0], normal0[1], normal0[2]); - normals.push(normal3[0], normal3[1], normal3[2]); - normals.push(normal2[0], normal2[1], normal2[2]); - - vertexIndexs.push( - i_vertices++, - i_vertices++, - i_vertices++ - ); - - indices.push(i_indices++, i_indices++, i_indices++); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - vertexProperties.push(propertyValue); - - line_positions.push(x3, y3, z3); - line_positions.push(x0, y0, z0); - - line_positions.push(x3, y3, z3); - line_positions.push(x2, y2, z2); - } - - // diagonal - if ((i3_act && !i1_act) || (!i3_act && i1_act)) { - line_positions.push(x0, y0, z0); - line_positions.push(x2, y2, z2); - } - } - } - } - } - - const mesh: MeshType = { - drawMode: 4, // corresponds to GL.TRIANGLES, - attributes: { - positions: { value: new Float32Array(positions), size: 3 }, - normals: { value: new Float32Array(normals), size: 3 }, - properties: { value: new Float32Array(vertexProperties), size: 1 }, - vertex_indexs: { value: new Int32Array(vertexIndexs), size: 1 }, - }, - vertexCount: indices.length, - indices: { value: new Uint32Array(indices), size: 1 }, - }; - - const mesh_lines: MeshTypeLines = { - drawMode: 1, // corresponds to GL.LINES, - attributes: { - positions: { value: new Float32Array(line_positions), size: 3 }, - }, - vertexCount: line_positions.length / 3, - }; - - //const t1 = performance.now(); - // Keep this. - //console.log(`Task makeMesh took ${(t1 - t0) * 0.001} seconds.`); - - // Note: typescript gives this error "error TS2554: Expected 2-3 arguments, but got 1." - // Disabling this for now as the second argument should be optional. - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - postMessage([mesh, mesh_lines, meshZValueRange, propertyValueRange]); -}