From afb4de4327f903afd29fa8ef3ef3a2cf319abcd2 Mon Sep 17 00:00:00 2001 From: Alexis Jacomy Date: Mon, 8 Jul 2024 11:20:16 +0200 Subject: [PATCH] [all] Implements depth in all node programs This commit is related to #1427. Details: - Updates depth management in sigma.ts to clarify the difference between zIndex and depth in nodes and edges data - Adds depth management in all node programs - Adds depth management in createNodeCompoundProgram --- packages/node-border/src/factory.ts | 23 +++++++---- packages/node-border/src/shader-frag.ts | 6 ++- packages/node-border/src/shader-vert.ts | 9 ++++ packages/node-image/src/factory.ts | 41 +++++++++++-------- packages/node-image/src/shader-frag.ts | 8 ++++ packages/node-image/src/shader-vert.ts | 9 ++++ packages/node-piechart/src/factory.ts | 28 +++++++------ packages/node-piechart/src/shader-frag.ts | 4 ++ packages/node-piechart/src/shader-vert.ts | 9 ++++ packages/sigma/src/rendering/node.ts | 7 +++- packages/sigma/src/rendering/program.ts | 18 +++++--- .../programs/node-circle/frag.glsl.ts | 16 ++++---- .../rendering/programs/node-circle/index.ts | 12 ++++-- .../programs/node-circle/vert.glsl.ts | 8 ++++ .../programs/node-point/frag.glsl.ts | 16 ++++---- .../rendering/programs/node-point/index.ts | 17 +++++--- .../programs/node-point/vert.glsl.ts | 8 ++++ packages/sigma/src/sigma.ts | 16 ++++---- packages/sigma/src/types.ts | 1 + 19 files changed, 176 insertions(+), 80 deletions(-) diff --git a/packages/node-border/src/factory.ts b/packages/node-border/src/factory.ts index 6bdf39ca0..60a198b72 100644 --- a/packages/node-border/src/factory.ts +++ b/packages/node-border/src/factory.ts @@ -20,18 +20,11 @@ export default function getNodeBorderProgram< }; const { borders } = options; - const UNIFORMS = [ - "u_sizeRatio", - "u_correctionRatio", - "u_matrix", - ...borders.flatMap(({ color }, i) => ("value" in color ? [`u_borderColor_${i + 1}`] : [])), - ]; - return class NodeBorderProgram< N extends Attributes = Attributes, E extends Attributes = Attributes, G extends Attributes = Attributes, - > extends NodeProgram<(typeof UNIFORMS)[number], N, E, G> { + > extends NodeProgram { static readonly ANGLE_1 = 0; static readonly ANGLE_2 = (2 * Math.PI) / 3; static readonly ANGLE_3 = (4 * Math.PI) / 3; @@ -42,10 +35,17 @@ export default function getNodeBorderProgram< VERTEX_SHADER_SOURCE: getVertexShader(options), FRAGMENT_SHADER_SOURCE: getFragmentShader(options), METHOD: WebGLRenderingContext.TRIANGLES, - UNIFORMS, + UNIFORMS: [ + "u_sizeRatio", + "u_correctionRatio", + "u_matrix", + ...borders.flatMap(({ color }, i) => ("value" in color ? [`u_borderColor_${i + 1}`] : [])), + ...(this.hasDepth ? ["u_maxZIndex"] : []), + ], ATTRIBUTES: [ { name: "a_position", size: 2, type: FLOAT }, { name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true }, + ...(this.hasDepth ? [{ name: "a_zIndex", size: 1, type: FLOAT }] : []), { name: "a_size", size: 1, type: FLOAT }, ...borders.flatMap(({ color }, i) => "attribute" in color @@ -67,6 +67,9 @@ export default function getNodeBorderProgram< array[startIndex++] = data.x; array[startIndex++] = data.y; array[startIndex++] = nodeIndex; + if (this.hasDepth) { + array[startIndex++] = data.depth; + } array[startIndex++] = data.size; borders.forEach(({ color }) => { if ("attribute" in color) @@ -91,6 +94,8 @@ export default function getNodeBorderProgram< gl.uniform4f(location, r / 255, g / 255, b / 255, a / 255); } }); + + if (this.hasDepth) gl.uniform1f(uniformLocations.u_maxZIndex, params.maxNodesDepth); } }; } diff --git a/packages/node-border/src/shader-frag.ts b/packages/node-border/src/shader-frag.ts index c4594bc25..af5037d31 100644 --- a/packages/node-border/src/shader-frag.ts +++ b/packages/node-border/src/shader-frag.ts @@ -88,8 +88,10 @@ ${borders return res.join("\n"); }) .join("\n")} - if (dist > adjustedBorderSize_0) { - gl_FragColor = borderColor_0; + if (dist > v_radius) { + #ifdef HAS_DEPTH + discard; + #endif } else ${borders .map( (_, i) => `if (dist > adjustedBorderSize_${i} - aaBorder) { diff --git a/packages/node-border/src/shader-vert.ts b/packages/node-border/src/shader-vert.ts index f9ca4e235..88f68e4d2 100644 --- a/packages/node-border/src/shader-vert.ts +++ b/packages/node-border/src/shader-vert.ts @@ -30,6 +30,11 @@ ${borders .join("\n")} #endif +#ifdef HAS_DEPTH +attribute float a_zIndex; +uniform float u_maxZIndex; +#endif + const float bias = 255.0 / 254.0; const vec4 transparent = vec4(0.0, 0.0, 0.0, 0.0); @@ -46,6 +51,10 @@ void main() { v_radius = size / 2.0; v_diffVector = diffVector; + #ifdef HAS_DEPTH + gl_Position.z = a_zIndex / u_maxZIndex; + #endif + #ifdef PICKING_MODE v_color = a_id; #else diff --git a/packages/node-image/src/factory.ts b/packages/node-image/src/factory.ts index 7407a2786..ebde2ef72 100644 --- a/packages/node-image/src/factory.ts +++ b/packages/node-image/src/factory.ts @@ -46,17 +46,6 @@ const DEFAULT_CREATE_NODE_IMAGE_OPTIONS: CreateNodeImageProgramOptions { + return class NodeImageProgram extends NodeProgram { static readonly ANGLE_1 = 0; static readonly ANGLE_2 = (2 * Math.PI) / 3; static readonly ANGLE_3 = (4 * Math.PI) / 3; @@ -114,12 +103,23 @@ export default function getNodeImageProgram< VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE: getFragmentShader({ texturesCount: textureManager.getTextures().length }), METHOD: WebGLRenderingContext.TRIANGLES, - UNIFORMS, + UNIFORMS: [ + "u_sizeRatio", + "u_correctionRatio", + "u_cameraAngle", + "u_percentagePadding", + "u_matrix", + "u_colorizeImages", + "u_keepWithinCircle", + "u_atlas", + ...(this.hasDepth ? ["u_maxZIndex"] : []), + ], ATTRIBUTES: [ { name: "a_position", size: 2, type: FLOAT }, { name: "a_size", size: 1, type: FLOAT }, { name: "a_color", size: 4, type: UNSIGNED_BYTE, normalized: true }, { name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true }, + ...(this.hasDepth ? [{ name: "a_zIndex", size: 1, type: FLOAT }] : []), { name: "a_texture", size: 4, type: FLOAT }, { name: "a_textureIndex", size: 1, type: FLOAT }, ], @@ -131,7 +131,6 @@ export default function getNodeImageProgram< atlas: Atlas; textures: WebGLTexture[]; textureImages: ImageData[]; - latestRenderParams?: RenderParams; textureManagerCallback: null | ((newAtlasData: { atlas: Atlas; textures: ImageData[] }) => void) = null; constructor(gl: WebGLRenderingContext, pickingBuffer: WebGLFramebuffer | null, renderer: Sigma) { @@ -145,9 +144,10 @@ export default function getNodeImageProgram< if (shouldUpgradeShaders) this.upgradeShaders(); this.bindTextures(); - if (this.latestRenderParams) this.render(this.latestRenderParams); - - if (this.renderer && this.renderer.refresh) this.renderer.refresh(); + if (this.renderer?.refresh) + this.renderer.refresh({ + schedule: true, + }); }; textureManager.on(TextureManager.NEW_TEXTURE_EVENT, this.textureManagerCallback); @@ -233,6 +233,10 @@ export default function getNodeImageProgram< array[startIndex++] = color; array[startIndex++] = nodeIndex; + if (this.hasDepth) { + array[startIndex++] = data.depth; + } + // Reference texture: if (imagePosition && typeof imagePosition.textureIndex === "number") { const { width, height } = this.textureImages[imagePosition.textureIndex]; @@ -261,7 +265,6 @@ export default function getNodeImageProgram< u_cameraAngle, u_percentagePadding, } = uniformLocations; - this.latestRenderParams = params; gl.uniform1f(u_correctionRatio, params.correctionRatio); gl.uniform1f(u_sizeRatio, keepWithinCircle ? params.sizeRatio : params.sizeRatio / Math.SQRT2); @@ -274,6 +277,8 @@ export default function getNodeImageProgram< ); gl.uniform1i(u_colorizeImages, drawingMode === "color" ? 1 : 0); gl.uniform1i(u_keepWithinCircle, keepWithinCircle ? 1 : 0); + + if (this.hasDepth) gl.uniform1f(uniformLocations.u_maxZIndex, params.maxNodesDepth); } }; } diff --git a/packages/node-image/src/shader-frag.ts b/packages/node-image/src/shader-frag.ts index 90eeff566..9d9040f1d 100644 --- a/packages/node-image/src/shader-frag.ts +++ b/packages/node-image/src/shader-frag.ts @@ -92,13 +92,21 @@ void main(void) { } else if (dist < v_radius) { gl_FragColor = mix(transparent, color, (v_radius - dist) / border); } + + #ifdef HAS_DEPTH + if (dist >= v_radius) discard; + #endif } // Crop in a square else: else { float squareHalfSize = v_radius * ${Math.SQRT1_2 * Math.cos(Math.PI / 12)}; if (abs(diffVector.x) > squareHalfSize || abs(diffVector.y) > squareHalfSize) { + #ifdef HAS_DEPTH + discard; + #else gl_FragColor = transparent; + #endif } else { gl_FragColor = color; } diff --git a/packages/node-image/src/shader-vert.ts b/packages/node-image/src/shader-vert.ts index 6932a2977..e929dbf14 100644 --- a/packages/node-image/src/shader-vert.ts +++ b/packages/node-image/src/shader-vert.ts @@ -18,6 +18,11 @@ varying float v_radius; varying vec4 v_texture; varying float v_textureIndex; +#ifdef HAS_DEPTH +attribute float a_zIndex; +uniform float u_maxZIndex; +#endif + const float bias = 255.0 / 254.0; const float marginRatio = 1.05; @@ -34,6 +39,10 @@ void main() { v_diffVector = diffVector; v_radius = size / 2.0 / marginRatio; + #ifdef HAS_DEPTH + gl_Position.z = a_zIndex / u_maxZIndex; + #endif + #ifdef PICKING_MODE // For picking mode, we use the ID as the color: v_color = a_id; diff --git a/packages/node-piechart/src/factory.ts b/packages/node-piechart/src/factory.ts index 17c8be7dc..7113b453e 100644 --- a/packages/node-piechart/src/factory.ts +++ b/packages/node-piechart/src/factory.ts @@ -20,21 +20,11 @@ export default function getNodePiechartProgram< }; const { slices, offset } = options; - const UNIFORMS = [ - "u_sizeRatio", - "u_correctionRatio", - "u_cameraAngle", - "u_matrix", - "u_defaultColor", - ...("value" in offset ? ["u_offset"] : []), - ...slices.flatMap(({ color }, i) => ("value" in color ? [`u_sliceColor_${i + 1}`] : [])), - ]; - return class NodeBorderProgram< N extends Attributes = Attributes, E extends Attributes = Attributes, G extends Attributes = Attributes, - > extends NodeProgram<(typeof UNIFORMS)[number], N, E, G> { + > extends NodeProgram { static readonly ANGLE_1 = 0; static readonly ANGLE_2 = (2 * Math.PI) / 3; static readonly ANGLE_3 = (4 * Math.PI) / 3; @@ -45,10 +35,20 @@ export default function getNodePiechartProgram< VERTEX_SHADER_SOURCE: getVertexShader(options), FRAGMENT_SHADER_SOURCE: getFragmentShader(options), METHOD: WebGLRenderingContext.TRIANGLES, - UNIFORMS, + UNIFORMS: [ + "u_sizeRatio", + "u_correctionRatio", + "u_cameraAngle", + "u_matrix", + "u_defaultColor", + ...(this.hasDepth ? ["u_maxZIndex"] : []), + ...("value" in offset ? ["u_offset"] : []), + ...slices.flatMap(({ color }, i) => ("value" in color ? [`u_sliceColor_${i + 1}`] : [])), + ], ATTRIBUTES: [ { name: "a_position", size: 2, type: FLOAT }, { name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true }, + ...(this.hasDepth ? [{ name: "a_zIndex", size: 1, type: FLOAT }] : []), { name: "a_size", size: 1, type: FLOAT }, ...("attribute" in offset ? [{ name: "a_offset", size: 1, type: FLOAT }] : []), ...slices.flatMap(({ color }, i) => @@ -71,6 +71,9 @@ export default function getNodePiechartProgram< array[startIndex++] = data.x; array[startIndex++] = data.y; array[startIndex++] = nodeIndex; + if (this.hasDepth) { + array[startIndex++] = data.depth; + } array[startIndex++] = data.size; if ("attribute" in offset) { array[startIndex++] = data[offset.attribute as "size"] || 0; @@ -94,6 +97,7 @@ export default function getNodePiechartProgram< gl.uniform1f(u_cameraAngle, params.cameraAngle); gl.uniformMatrix3fv(u_matrix, false, params.matrix); + if (this.hasDepth) gl.uniform1f(uniformLocations.u_maxZIndex, params.maxNodesDepth); if ("value" in offset) gl.uniform1f(uniformLocations.u_offset, offset.value); const [r, g, b, a] = colorToArray(options.defaultColor || DEFAULT_COLOR); diff --git a/packages/node-piechart/src/shader-frag.ts b/packages/node-piechart/src/shader-frag.ts index 7b80546ca..04c4a9444 100644 --- a/packages/node-piechart/src/shader-frag.ts +++ b/packages/node-piechart/src/shader-frag.ts @@ -88,6 +88,10 @@ ${slices.map((_, i) => ` float angle_${i + 1} = angle_${i} + sliceValue_${i + gl_FragColor = color; } else if (dist < v_radius) { gl_FragColor = mix(transparent, color, (v_radius - dist) / aaBorder); + } else { + #ifdef HAS_DEPTH + discard; + #endif } #endif } diff --git a/packages/node-piechart/src/shader-vert.ts b/packages/node-piechart/src/shader-vert.ts index 11c73d5a4..e17d6cfa9 100644 --- a/packages/node-piechart/src/shader-vert.ts +++ b/packages/node-piechart/src/shader-vert.ts @@ -33,6 +33,11 @@ ${slices .join("\n")} #endif +#ifdef HAS_DEPTH +attribute float a_zIndex; +uniform float u_maxZIndex; +#endif + const vec4 transparent = vec4(0.0, 0.0, 0.0, 0.0); void main() { @@ -49,6 +54,10 @@ void main() { v_diffVector = diffVector; ${"attribute" in offset ? "v_offset = a_offset;\n" : ""} + #ifdef HAS_DEPTH + gl_Position.z = a_zIndex / u_maxZIndex; + #endif + #ifdef PICKING_MODE v_color = a_id; #else diff --git a/packages/sigma/src/rendering/node.ts b/packages/sigma/src/rendering/node.ts index 8756c1966..0db1a9fd0 100644 --- a/packages/sigma/src/rendering/node.ts +++ b/packages/sigma/src/rendering/node.ts @@ -52,7 +52,7 @@ export abstract class NodeProgram< return this.processVisibleItem(indexToColor(nodeIndex), i, data); } - abstract processVisibleItem(nodeIndex: number, i: number, data: NodeDisplayData): void; + abstract processVisibleItem(nodeId: number, startIndex: number, data: NodeDisplayData): void; } class NodeProgramClass< @@ -123,7 +123,10 @@ export function createNodeCompoundProgram< } process(nodeIndex: number, offset: number, data: NodeDisplayData): void { - this.programs.forEach((program) => program.process(nodeIndex, offset, data)); + const l = this.programs.length; + this.programs.forEach((program, i) => + program.process(nodeIndex, offset, { ...data, depth: data.depth + 1 - i / l }), + ); } render(params: RenderParams): void { diff --git a/packages/sigma/src/rendering/program.ts b/packages/sigma/src/rendering/program.ts index c3e7ba655..879acfaa2 100644 --- a/packages/sigma/src/rendering/program.ts +++ b/packages/sigma/src/rendering/program.ts @@ -22,7 +22,7 @@ import { } from "./utils"; const PICKING_PREFIX = `#define PICKING_MODE\n`; -const Z_INDEXING_PREFIX = `#define Z_INDEXING\n`; +const HAS_DEPTH_PREFIX = `#define HAS_DEPTH\n`; const SIZE_FACTOR_PER_ATTRIBUTE_TYPE: Record = { [WebGL2RenderingContext.BOOL]: 1, @@ -77,7 +77,7 @@ export abstract class Program< pickProgram: ProgramInfo | null; isInstanced: boolean; - isZIndexing: boolean; + hasDepth: boolean; abstract getDefinition(): ProgramDefinition | InstancedProgramDefinition; @@ -87,15 +87,15 @@ export abstract class Program< renderer: Sigma, ) { this.renderer = renderer; - this.isZIndexing = !!renderer.getSetting("zIndex"); + this.hasDepth = !!renderer.getSetting("zIndex"); - const zIndexingPrefix = this.isZIndexing ? Z_INDEXING_PREFIX : ""; + const depthPrefix = this.hasDepth ? HAS_DEPTH_PREFIX : ""; // Reading and caching program definition const def = this.getDefinition(); this.VERTICES = def.VERTICES; - this.VERTEX_SHADER_SOURCE = zIndexingPrefix + def.VERTEX_SHADER_SOURCE; - this.FRAGMENT_SHADER_SOURCE = zIndexingPrefix + def.FRAGMENT_SHADER_SOURCE; + this.VERTEX_SHADER_SOURCE = depthPrefix + def.VERTEX_SHADER_SOURCE; + this.FRAGMENT_SHADER_SOURCE = depthPrefix + def.FRAGMENT_SHADER_SOURCE; this.UNIFORMS = def.UNIFORMS; this.ATTRIBUTES = def.ATTRIBUTES; this.METHOD = def.METHOD; @@ -331,6 +331,12 @@ export abstract class Program< protected renderProgram(params: RenderParams, programInfo: ProgramInfo): void { const { gl, program } = programInfo; + if (this.hasDepth) { + gl.enable(gl.DEPTH_TEST); + } else { + gl.disable(gl.DEPTH_TEST); + } + // With the current fix for #1397, the alpha blending is enabled for the // picking layer: gl.enable(gl.BLEND); diff --git a/packages/sigma/src/rendering/programs/node-circle/frag.glsl.ts b/packages/sigma/src/rendering/programs/node-circle/frag.glsl.ts index 1a5c04529..497bdeaa1 100644 --- a/packages/sigma/src/rendering/programs/node-circle/frag.glsl.ts +++ b/packages/sigma/src/rendering/programs/node-circle/frag.glsl.ts @@ -22,13 +22,15 @@ void main(void) { gl_FragColor = v_color; #else - float t = 0.0; - if (dist > border) - t = 1.0; - else if (dist > 0.0) - t = dist / border; - - gl_FragColor = mix(v_color, transparent, t); + if (dist > border) { + #ifdef HAS_DEPTH + discard; + #endif + } else if (dist > 0.0) { + gl_FragColor = mix(v_color, transparent, dist / border); + } else { + gl_FragColor = v_color; + } #endif } `; diff --git a/packages/sigma/src/rendering/programs/node-circle/index.ts b/packages/sigma/src/rendering/programs/node-circle/index.ts index 5c99821fb..38a179333 100644 --- a/packages/sigma/src/rendering/programs/node-circle/index.ts +++ b/packages/sigma/src/rendering/programs/node-circle/index.ts @@ -19,13 +19,11 @@ import VERTEX_SHADER_SOURCE from "./vert.glsl"; const { UNSIGNED_BYTE, FLOAT } = WebGLRenderingContext; -const UNIFORMS = ["u_sizeRatio", "u_correctionRatio", "u_matrix"] as const; - export default class NodeCircleProgram< N extends Attributes = Attributes, E extends Attributes = Attributes, G extends Attributes = Attributes, -> extends NodeProgram<(typeof UNIFORMS)[number], N, E, G> { +> extends NodeProgram { static readonly ANGLE_1 = 0; static readonly ANGLE_2 = (2 * Math.PI) / 3; static readonly ANGLE_3 = (4 * Math.PI) / 3; @@ -36,12 +34,13 @@ export default class NodeCircleProgram< VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE, METHOD: WebGLRenderingContext.TRIANGLES, - UNIFORMS, + UNIFORMS: ["u_sizeRatio", "u_correctionRatio", "u_matrix", ...(this.hasDepth ? ["u_maxZIndex"] : [])], ATTRIBUTES: [ { name: "a_position", size: 2, type: FLOAT }, { name: "a_size", size: 1, type: FLOAT }, { name: "a_color", size: 4, type: UNSIGNED_BYTE, normalized: true }, { name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true }, + ...(this.hasDepth ? [{ name: "a_zIndex", size: 1, type: FLOAT }] : []), ], CONSTANT_ATTRIBUTES: [{ name: "a_angle", size: 1, type: FLOAT }], CONSTANT_DATA: [[NodeCircleProgram.ANGLE_1], [NodeCircleProgram.ANGLE_2], [NodeCircleProgram.ANGLE_3]], @@ -57,6 +56,9 @@ export default class NodeCircleProgram< array[startIndex++] = data.size; array[startIndex++] = color; array[startIndex++] = nodeIndex; + if (this.hasDepth) { + array[startIndex++] = data.depth; + } } setUniforms(params: RenderParams, { gl, uniformLocations }: ProgramInfo): void { @@ -65,5 +67,7 @@ export default class NodeCircleProgram< gl.uniform1f(u_correctionRatio, params.correctionRatio); gl.uniform1f(u_sizeRatio, params.sizeRatio); gl.uniformMatrix3fv(u_matrix, false, params.matrix); + + if (this.hasDepth) gl.uniform1f(uniformLocations.u_maxZIndex, params.maxNodesDepth); } } diff --git a/packages/sigma/src/rendering/programs/node-circle/vert.glsl.ts b/packages/sigma/src/rendering/programs/node-circle/vert.glsl.ts index 9d720da65..4fd8df503 100644 --- a/packages/sigma/src/rendering/programs/node-circle/vert.glsl.ts +++ b/packages/sigma/src/rendering/programs/node-circle/vert.glsl.ts @@ -6,6 +6,10 @@ attribute vec2 a_position; attribute float a_size; attribute float a_angle; +#ifdef HAS_DEPTH +attribute float a_zIndex; +#endif + uniform mat3 u_matrix; uniform float u_sizeRatio; uniform float u_correctionRatio; @@ -30,6 +34,10 @@ void main() { v_diffVector = diffVector; v_radius = size / 2.0; + #ifdef HAS_DEPTH + gl_Position.z = a_zIndex; + #endif + #ifdef PICKING_MODE // For picking mode, we use the ID as the color: v_color = a_id; diff --git a/packages/sigma/src/rendering/programs/node-point/frag.glsl.ts b/packages/sigma/src/rendering/programs/node-point/frag.glsl.ts index cd5610ecf..df568dc21 100644 --- a/packages/sigma/src/rendering/programs/node-point/frag.glsl.ts +++ b/packages/sigma/src/rendering/programs/node-point/frag.glsl.ts @@ -20,13 +20,15 @@ void main(void) { gl_FragColor = transparent; #else - float t = 0.0; - if (dist > v_border) - t = 1.0; - else if (dist > 0.0) - t = dist / v_border; - - gl_FragColor = mix(transparent, v_color, t); + if (dist > v_border) { + gl_FragColor = v_color; + } else if (dist > 0.0) { + gl_FragColor = mix(transparent, v_color, dist / v_border); + } else { + #ifdef HAS_DEPTH + discard; + #endif + } #endif } `; diff --git a/packages/sigma/src/rendering/programs/node-point/index.ts b/packages/sigma/src/rendering/programs/node-point/index.ts index bb01cda80..2a8ee231c 100644 --- a/packages/sigma/src/rendering/programs/node-point/index.ts +++ b/packages/sigma/src/rendering/programs/node-point/index.ts @@ -18,25 +18,24 @@ import VERTEX_SHADER_SOURCE from "./vert.glsl"; const { UNSIGNED_BYTE, FLOAT } = WebGLRenderingContext; -const UNIFORMS = ["u_sizeRatio", "u_pixelRatio", "u_matrix"] as const; - export default class NodePointProgram< N extends Attributes = Attributes, E extends Attributes = Attributes, G extends Attributes = Attributes, -> extends NodeProgram<(typeof UNIFORMS)[number], N, E, G> { +> extends NodeProgram { getDefinition() { return { VERTICES: 1, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE, METHOD: WebGLRenderingContext.POINTS, - UNIFORMS, + UNIFORMS: ["u_sizeRatio", "u_pixelRatio", "u_matrix", ...(this.hasDepth ? ["u_maxZIndex"] : [])], ATTRIBUTES: [ { name: "a_position", size: 2, type: FLOAT }, { name: "a_size", size: 1, type: FLOAT }, { name: "a_color", size: 4, type: UNSIGNED_BYTE, normalized: true }, { name: "a_id", size: 4, type: UNSIGNED_BYTE, normalized: true }, + ...(this.hasDepth ? [{ name: "a_zIndex", size: 1, type: FLOAT }] : []), ], }; } @@ -49,13 +48,21 @@ export default class NodePointProgram< array[startIndex++] = data.size; array[startIndex++] = floatColor(data.color); array[startIndex++] = nodeIndex; + if (this.hasDepth) { + array[startIndex++] = data.depth; + } } - setUniforms({ sizeRatio, pixelRatio, matrix }: RenderParams, { gl, uniformLocations }: ProgramInfo): void { + setUniforms( + { sizeRatio, pixelRatio, matrix, maxNodesDepth }: RenderParams, + { gl, uniformLocations }: ProgramInfo, + ): void { const { u_sizeRatio, u_pixelRatio, u_matrix } = uniformLocations; gl.uniform1f(u_pixelRatio, pixelRatio); gl.uniform1f(u_sizeRatio, sizeRatio); gl.uniformMatrix3fv(u_matrix, false, matrix); + + if (this.hasDepth) gl.uniform1f(uniformLocations.u_maxZIndex, maxNodesDepth); } } diff --git a/packages/sigma/src/rendering/programs/node-point/vert.glsl.ts b/packages/sigma/src/rendering/programs/node-point/vert.glsl.ts index a79c37ae7..8df274b3d 100644 --- a/packages/sigma/src/rendering/programs/node-point/vert.glsl.ts +++ b/packages/sigma/src/rendering/programs/node-point/vert.glsl.ts @@ -5,6 +5,10 @@ attribute vec4 a_color; attribute vec2 a_position; attribute float a_size; +#ifdef HAS_DEPTH +attribute float a_zIndex; +#endif + uniform float u_sizeRatio; uniform float u_pixelRatio; uniform mat3 u_matrix; @@ -28,6 +32,10 @@ void main() { v_border = (0.5 / a_size) * u_sizeRatio; + #ifdef HAS_DEPTH + gl_Position.z = a_zIndex; + #endif + #ifdef PICKING_MODE // For picking mode, we use the ID as the color: v_color = a_id; diff --git a/packages/sigma/src/sigma.ts b/packages/sigma/src/sigma.ts index 947391176..ba6a5feb1 100644 --- a/packages/sigma/src/sigma.ts +++ b/packages/sigma/src/sigma.ts @@ -143,8 +143,6 @@ export default class Sigma< private nodesWithForcedLabels: Set = new Set(); private edgesWithForcedLabels: Set = new Set(); private nodeExtent: { x: Extent; y: Extent } = { x: [0, 1], y: [0, 1] }; - private nodeDepths: Record = {}; - private edgeDepths: Record = {}; private matrix: Float32Array = identity(); private invMatrix: Float32Array = identity(); @@ -760,14 +758,15 @@ export default class Sigma< } // Order nodes by zIndex before to add them to program - this.nodeDepths = {}; if (this.settings.zIndex) { const sortedNodes = zIndexOrdering( (node: string): number => this.nodeDataCache[node].zIndex, nodes.slice(0), ); - for (let i = 0, l = sortedNodes.length; i < l; i++) this.nodeDepths[sortedNodes[i]] = l - 1 - i; + for (let i = 0, l = sortedNodes.length; i < l; i++) { + this.nodeDataCache[sortedNodes[i]].depth = l - 1 - i; + } } // Add data to programs @@ -797,14 +796,15 @@ export default class Sigma< } // Order edges by zIndex before to add them to program - this.edgeDepths = {}; if (this.settings.zIndex) { const sortedEdges = zIndexOrdering( (edge: string): number => this.edgeDataCache[edge].zIndex, edges.slice(0), ); - for (let i = 0, l = sortedEdges.length; i < l; i++) this.edgeDepths[sortedEdges[i]] = l - 1 - i; + for (let i = 0, l = sortedEdges.length; i < l; i++) { + this.edgeDataCache[sortedEdges[i]].depth = l - 1 - i; + } } for (const type in this.edgePrograms) { @@ -1407,7 +1407,7 @@ export default class Sigma< const data = this.nodeDataCache[node]; const nodeProgram = this.nodePrograms[data.type]; if (!nodeProgram) throw new Error(`Sigma: could not find a suitable program for node type "${data.type}"!`); - nodeProgram.process(fingerprint, position, { ...data, zIndex: this.nodeDepths[node] }); + nodeProgram.process(fingerprint, position, data); // Saving program index this.nodeProgramIndex[node] = position; } @@ -1426,7 +1426,7 @@ export default class Sigma< const extremities = this.graph.extremities(edge), sourceData = this.nodeDataCache[extremities[0]], targetData = this.nodeDataCache[extremities[1]]; - edgeProgram.process(fingerprint, position, sourceData, targetData, { ...data, zIndex: this.edgeDepths[edge] }); + edgeProgram.process(fingerprint, position, sourceData, targetData, data); // Saving program index this.edgeProgramIndex[edge] = position; } diff --git a/packages/sigma/src/types.ts b/packages/sigma/src/types.ts index c4af74a99..2112c8946 100644 --- a/packages/sigma/src/types.ts +++ b/packages/sigma/src/types.ts @@ -64,6 +64,7 @@ export interface DisplayData { hidden: boolean; forceLabel: boolean; zIndex: number; + depth: number; type: string; }