UNPKG

@webviz/subsurface-viewer

Version:

3D visualization component for subsurface reservoir data

495 lines 22.6 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { COORDINATE_SYSTEM, Layer, picking, project32 } from "@deck.gl/core"; import { GL } from "@luma.gl/constants"; import { Geometry, Model } from "@luma.gl/engine"; import { lighting } from "@luma.gl/shadertools"; import { phongMaterial } from "../shader_modules/phong-lighting/phong-material"; import { precisionForTests } from "../shader_modules/test-precision/precisionForTests"; import { createPropertyData } from "../utils/layerTools"; import { getImageData, } from "../utils/colormapTools"; import { isTypedArray, loadDataArray, toNormalizedColor, toTypedArray, } from "../../utils"; import fsShader from "./triangle.fs.glsl"; import vsShader from "./triangle.vs.glsl"; import fsTexShader from "./texTriangle.fs.glsl"; import vsTexShader from "./texTriangle.vs.glsl"; import fsLineShader from "./triangleLine.fs.glsl"; import vsLineShader from "./triangleLine.vs.glsl"; const defaultMaterial = { ambient: 0.35, diffuse: 0.9, shininess: 32, specularColor: [38, 38, 38], }; const defaultColormapSetup = { valueRange: [0, 1], clampRange: undefined, clampColor: [0, 255, 0, 200], // green color for clamped values undefinedValue: Number.NaN, undefinedColor: [255, 0, 0, 200], // red color for undefined values smooth: true, }; const defaultProps = { "@@type": "GpglValueMappedSurfaceLayer", name: "Value Mapped Surface Layer", id: "value-mapped-surface", data: ["dummy"], showMesh: false, colormap: { colormapName: "seismic", }, colormapSetup: defaultColormapSetup, color: [100, 100, 255], coordinateSystem: COORDINATE_SYSTEM.CARTESIAN, material: true, // default material smoothShading: false, depthTest: true, ZIncreasingDownwards: true, // deck.gl default props visible: true, pickable: true, }; /** * GpglValueMappedSurfaceLayer is a layer handling surfaces textured by a value map. * These values are converted to color using a colormap and associated setup. * * A primary use case is to display seismic data. * The surfaces are expected to be small compared to the ones handled by the triangles layer. */ export class GpglValueMappedSurfaceLayer extends Layer { setShaderModuleProps(args) { // If material is a boolean, convert it to the default material // We need to set a different default material than in the shader module if (typeof args["phongMaterial"] === "boolean" && args["phongMaterial"] === true) { // eslint-disable-next-line @typescript-eslint/no-explicit-any args["phongMaterial"] = defaultMaterial; } super.setShaderModuleProps(Object.assign(Object.assign({}, args), { lighting: Object.assign(Object.assign({}, args["lighting"]), { enabled: this.props.material !== false }) })); } initializeState(context) { const gl = context.device; this._createModels(gl).then(([triangleModels, meshModels]) => { this.setState({ triangleModels: triangleModels, meshModels: meshModels, }); }); } updateState(params) { const rebuild = params.props.valueMappedTriangles !== params.oldProps.valueMappedTriangles || params.props.triangleMeshes !== params.oldProps.triangleMeshes || params.props.showMesh !== params.oldProps.showMesh || params.props.colormap !== params.oldProps.colormap; super.updateState(params); if (rebuild) { this.initializeState(params.context); } } _consolidateColormap(colormap) { var _a; if (colormap instanceof Uint8Array) { // If colormapProps is a Uint8Array, return it directly return colormap; } if (!colormap || "colormapName" in colormap) { return { colormapName: (_a = colormap === null || colormap === void 0 ? void 0 : colormap.colormapName) !== null && _a !== void 0 ? _a : "seismic", colorTables: this.context.userData .colorTables, }; } return colormap; } _createColormapTexture(device, colormap) { const textureProps = { sampler: { addressModeU: "clamp-to-edge", addressModeV: "clamp-to-edge", minFilter: "linear", magFilter: "linear", }, width: 256, height: 1, format: "rgb8unorm-webgl", }; return device.createTexture(Object.assign(Object.assign({}, textureProps), { data: getImageData(this._consolidateColormap(colormap)) })); } _createValuesTexture(device, valueMap) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; if ((valueMap === null || valueMap === void 0 ? void 0 : valueMap.values) === undefined) { return undefined; } // fetch data const propertiesData = yield loadDataArray(valueMap.values, Float32Array); if (!propertiesData) { return undefined; } const smooth = (_b = (_a = this.props.colormapSetup) === null || _a === void 0 ? void 0 : _a.smooth) !== null && _b !== void 0 ? _b : defaultColormapSetup.smooth; const textureProps = { sampler: { addressModeU: "clamp-to-edge", addressModeV: "clamp-to-edge", minFilter: smooth ? "linear" : "nearest", magFilter: smooth ? "linear" : "nearest", }, width: valueMap.width, height: valueMap.height, format: "r32float", }; return device.createTexture(Object.assign(Object.assign({}, textureProps), { width: valueMap.width, height: valueMap.height, data: propertiesData })); }); } _createModels(device) { const _super = Object.create(null, { getShaders: { get: () => super.getShaders } }); return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const models = []; const meshModels = []; // Collect promises for all valueMappedTriangles const triangleModelPromises = (_b = (_a = this.props.valueMappedTriangles) === null || _a === void 0 ? void 0 : _a.map((texturedTrgs, i) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const trglGeometry = { topology: texturedTrgs.topology, attributes: { positions: { value: yield loadDataArray(texturedTrgs.vertices, Float32Array), size: 3, }, normals: { value: yield loadDataArray((_a = texturedTrgs.normals) !== null && _a !== void 0 ? _a : [], Float32Array), size: 3, }, texCoords: { value: yield loadDataArray((_b = texturedTrgs.texCoords) !== null && _b !== void 0 ? _b : [], Float32Array), size: 2, }, }, vertexCount: texturedTrgs.vertexIndices.size, indices: { value: yield loadDataArray(texturedTrgs.vertexIndices.value, Uint32Array), size: 1, }, }; const colormap = texturedTrgs.valueMap ? this._createColormapTexture(device, this.props.colormap) : undefined; const floatValues = texturedTrgs.valueMap ? yield this._createValuesTexture(device, texturedTrgs.valueMap) : undefined; const bindings = colormap && floatValues ? { colormap, valueTexture: floatValues } : undefined; const fs = bindings ? fsTexShader : fsShader; const vs = bindings ? vsTexShader : vsShader; const uniforms = bindings ? texTrianglesUniforms : trianglesUniforms; const model = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-trgl-${i}` }, _super.getShaders.call(this, { vs: vs, fs: fs, modules: [ project32, picking, lighting, phongMaterial, uniforms, precisionForTests, ], })), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry(trglGeometry), bindings: bindings, isInstanced: false })); // default mesh model if none if provided let meshModel = undefined; if (!((_c = this.props.triangleMeshes) === null || _c === void 0 ? void 0 : _c.length) && this.props.showMesh) { const indices = computeMeshIndices(texturedTrgs.vertexIndices.value, texturedTrgs.topology); const meshGeometry = { topology: meshTopology(texturedTrgs.topology), attributes: { positions: { value: toTypedArray(texturedTrgs.vertices, Float32Array), size: 3, }, }, vertexCount: indices.length, indices: { value: toTypedArray(indices, Uint32Array), size: 1, }, }; meshModel = new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-mesh-line-${i}` }, _super.getShaders.call(this, { vs: vsLineShader, fs: fsLineShader, modules: [ project32, picking, triangleMeshUniforms, precisionForTests, ], })), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry(meshGeometry), isInstanced: false })); } return { model, meshModel }; }))) !== null && _b !== void 0 ? _b : []; // Await all promises and push to models/meshModels arrays const triangleResults = yield Promise.all(triangleModelPromises); triangleResults.forEach(({ model, meshModel }) => { models.push(model); if (meshModel) { meshModels.push(meshModel); } }); (_c = this.props.triangleMeshes) === null || _c === void 0 ? void 0 : _c.forEach((mesh, i) => { var _a, _b; const meshGeometry = { topology: mesh.topology, attributes: { positions: { value: toTypedArray((_a = mesh.vertices) !== null && _a !== void 0 ? _a : (_b = this.props.valueMappedTriangles[i]) === null || _b === void 0 ? void 0 : _b.vertices, Float32Array), size: 3, }, }, vertexCount: mesh.vertexIndices.size, indices: { value: toTypedArray(mesh.vertexIndices.value, Uint32Array), size: 1, }, }; meshModels.push(new Model(device, Object.assign(Object.assign({ id: `${this.props.id}-mesh-line-${i}` }, _super.getShaders.call(this, { vs: vsLineShader, fs: fsLineShader, modules: [ project32, picking, triangleMeshUniforms, precisionForTests, ], })), { bufferLayout: this.getAttributeManager().getBufferLayouts(), geometry: new Geometry(meshGeometry), isInstanced: false }))); }); return [models, meshModels]; }); } getModels() { var _a, _b; const triangleModels = ((_a = this.state["triangleModels"]) !== null && _a !== void 0 ? _a : []); const meshModels = ((_b = this.state["meshModels"]) !== null && _b !== void 0 ? _b : []); return [...triangleModels, ...meshModels]; } _getClampColors(color) { if (isTypedArray(color)) { // If clampColor is a Uint8Array, return it directly if (color.length >= 8) { return [ toNormalizedColor(color.slice(0, 4)), toNormalizedColor(color.slice(4, 8)), ]; } else if (color.length === 6) { return [ toNormalizedColor(color.slice(0, 3)), toNormalizedColor(color.slice(3, 6)), ]; } const col = toNormalizedColor(color); return [col, col]; } if (Array.isArray(color) && color.length === 2) { return [toNormalizedColor(color[0]), toNormalizedColor(color[1])]; } const col = color ? toNormalizedColor(color) : undefined; return [col, col]; } // eslint-disable-next-line @typescript-eslint/no-explicit-any draw(args) { var _a, _b; if (!this.state["triangleModels"] && !this.state["meshModels"]) { return; } const triangleModels = this.state["triangleModels"]; const meshModels = this.state["meshModels"]; const { gl } = args.context; if (!this.props.depthTest) { gl.disable(GL.DEPTH_TEST); } gl.enable(GL.POLYGON_OFFSET_FILL); gl.polygonOffset(1, 1); // const [lowClampColors, highClampColor] = this._getClampColors((_b = (_a = this.props.colormapSetup) === null || _a === void 0 ? void 0 : _a.clampColor) !== null && _b !== void 0 ? _b : defaultColormapSetup.clampColor); // render all the triangle surfaces triangleModels === null || triangleModels === void 0 ? void 0 : triangleModels.forEach((model) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r; model.shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { triangles: { colormapRange: (_b = (_a = this.props.colormapSetup) === null || _a === void 0 ? void 0 : _a.valueRange) !== null && _b !== void 0 ? _b : defaultColormapSetup.valueRange, clampRange: ((_c = this.props.colormapSetup) === null || _c === void 0 ? void 0 : _c.clampRange) === null ? null : ((_g = (_e = (_d = this.props.colormapSetup) === null || _d === void 0 ? void 0 : _d.clampRange) !== null && _e !== void 0 ? _e : (_f = this.props.colormapSetup) === null || _f === void 0 ? void 0 : _f.valueRange) !== null && _g !== void 0 ? _g : defaultColormapSetup.valueRange), useClampColors: ((_h = this.props.colormapSetup) === null || _h === void 0 ? void 0 : _h.clampColor) !== null && ((_j = this.props.colormapSetup) === null || _j === void 0 ? void 0 : _j.clampRange) !== null, lowClampColor: lowClampColors, highClampColor: highClampColor, undefinedValue: (_l = (_k = this.props.colormapSetup) === null || _k === void 0 ? void 0 : _k.undefinedValue) !== null && _l !== void 0 ? _l : defaultColormapSetup.undefinedValue, undefinedColor: toNormalizedColor((_o = (_m = this.props.colormapSetup) === null || _m === void 0 ? void 0 : _m.undefinedColor) !== null && _o !== void 0 ? _o : defaultColormapSetup.undefinedColor), // Normalize color to [0,1] range. uColor: toNormalizedColor((_p = this.props.color) !== null && _p !== void 0 ? _p : defaultProps.color), smoothShading: (_q = this.props.smoothShading) !== null && _q !== void 0 ? _q : defaultProps.smoothShading, ZIncreasingDownwards: (_r = this.props.ZIncreasingDownwards) !== null && _r !== void 0 ? _r : defaultProps.ZIncreasingDownwards, } })); model.draw(args.context.renderPass); }); gl.disable(GL.POLYGON_OFFSET_FILL); // render all the triangle meshes if (this.props.showMesh) { meshModels === null || meshModels === void 0 ? void 0 : meshModels.forEach((meshModel) => { var _a; meshModel.shaderInputs.setProps(Object.assign(Object.assign({}, args.uniforms), { triangleMesh: { ZIncreasingDownwards: (_a = this.props.ZIncreasingDownwards) !== null && _a !== void 0 ? _a : defaultProps.ZIncreasingDownwards, } })); meshModel.draw(args.context.renderPass); }); } if (!this.props.depthTest) { gl.enable(GL.DEPTH_TEST); } } decodePickingColor() { return 0; } getPickingInfo({ info }) { var _a; if (!info.color) { return info; } const layer_properties = []; const zScale = this.props.modelMatrix ? this.props.modelMatrix[10] : 1; if (typeof ((_a = info.coordinate) === null || _a === void 0 ? void 0 : _a[2]) !== "undefined") { const depth = (this.props.ZIncreasingDownwards ? -info.coordinate[2] : info.coordinate[2]) / Math.max(0.001, zScale); layer_properties.push(createPropertyData("Depth", depth)); } return Object.assign(Object.assign({}, info), { properties: layer_properties }); } } GpglValueMappedSurfaceLayer.layerName = "GpglValueMappedSurfaceLayer"; GpglValueMappedSurfaceLayer.defaultProps = defaultProps; const triangleMeshUniformsBlock = `\ uniform triangleMeshUniforms { bool ZIncreasingDownwards; } triangleMesh; `; // NOTE: this must exactly the same name than in the uniform block const triangleMeshUniforms = { name: "triangleMesh", vs: triangleMeshUniformsBlock, fs: undefined, uniformTypes: { ZIncreasingDownwards: "f32", }, }; const triangleUniformsBlock = /*glsl*/ `\ uniform trianglesUniforms { vec4 uColor; bool smoothShading; bool ZIncreasingDownwards; } triangles; `; // NOTE: this must exactly the same name than in the uniform block const trianglesUniforms = { name: "triangles", vs: triangleUniformsBlock, fs: triangleUniformsBlock, uniformTypes: { uColor: "vec4<f32>", smoothShading: "u32", ZIncreasingDownwards: "u32", }, }; const texTriangleUniformsBlock = /*glsl*/ `\ uniform trianglesUniforms { vec2 colormapRange; vec2 clampRange; bool useClampColors; vec4 lowClampColor; vec4 highClampColor; float undefinedValue; vec4 undefinedColor; bool smoothShading; bool ZIncreasingDownwards; } triangles; `; // NOTE: this must exactly the same name than in the uniform block const texTrianglesUniforms = { name: "triangles", vs: texTriangleUniformsBlock, fs: texTriangleUniformsBlock, uniformTypes: { colormapRange: "vec2<f32>", clampRange: "vec2<f32>", useClampColors: "u32", lowClampColor: "vec4<f32>", highClampColor: "vec4<f32>", undefinedValue: "f32", undefinedColor: "vec4<f32>", smoothShading: "u32", ZIncreasingDownwards: "u32", }, }; // Helper functions function meshTopology(topology) { switch (topology) { case "triangle-list": return "line-list"; case "triangle-strip": return "line-strip"; default: throw new Error(`Unknown topology: ${topology}`); } } function computeMeshIndices(trglIndices, topology) { if (topology === "triangle-list") { // non optimal, and some edges might be missing // non trivial to fix that :/ return trglIndices; } if (trglIndices.length === 4) { // special case for a rectangle return [ trglIndices[2], trglIndices[0], trglIndices[1], trglIndices[2], trglIndices[3], trglIndices[1], ]; } // triangle strip. // missing edges must be added if (Array.isArray(trglIndices) && typeof trglIndices[0] === "number") { const indices1 = trglIndices .filter((_v, index) => index !== 0 && index % 2 === 0) .reverse(); const indices2 = trglIndices.filter((_v, index) => index !== trglIndices.length - 1 && index % 2 === 1); return indices1.concat(trglIndices, indices2); } else { const idxArray = trglIndices; const indices1 = idxArray .filter((_v, index) => index !== 0 && index % 2 === 0) .reverse(); const indices2 = idxArray.filter((_v, index) => index !== trglIndices.length - 1 && index % 2 === 1); const mergedIndices = new Uint32Array(idxArray.length + indices1.length + indices2.length); mergedIndices.set(indices1, 0); mergedIndices.set(idxArray, indices1.length); mergedIndices.set(indices2, indices1.length + idxArray.length); return mergedIndices; } } //# sourceMappingURL=gpglValueMappedSurfaceLayer.js.map