Skip to content

Commit

Permalink
[wip] Drafts picking implementation
Browse files Browse the repository at this point in the history
Details:
- Breaks renderers API:
  - processVisibleItem takes an additional first argument, nodeId (or
    edgeId for edges), that must be used as a color in the shaders in
    PICKING mode
  - draw becomes setUniforms, and no more has to call the drawGl method.
    Also, it receives a ProgramInfo as an additional input
  - The WebGL method used for drawing must be specified in the
    ProgramDefinition
- Migrates edge.rectangle and node.circle to picking (and partially
  migrates other programs so that they can be built)
- Adds a new WebGL context that draws the picking layer
- Migrates getNodeAtPosition and getEdgeAt to read the new layer

TODO:
- Picking does not fully work yet
- Remaining programs must implement picking in their shaders
- ATM edges are drawn over nodes on the picking layer, and this must be
  fixed
- Picking layer must be hidden
- Picking should probably be done on their respective layer, but on a
  texture
  • Loading branch information
jacomyal committed Sep 29, 2023
1 parent 08a2940 commit c20db82
Show file tree
Hide file tree
Showing 20 changed files with 475 additions and 391 deletions.
24 changes: 12 additions & 12 deletions examples/custom-rendering/node.border.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,44 @@ import { floatColor } from "sigma/utils";
import { NodeProgram } from "sigma/rendering/webgl/programs/common/node";
import VERTEX_SHADER_SOURCE from "!raw-loader!./node.border.vert.glsl";
import FRAGMENT_SHADER_SOURCE from "!raw-loader!./node.border.frag.glsl";
import { ProgramInfo } from "sigma/rendering/webgl/programs/common/program";

const { UNSIGNED_BYTE, FLOAT } = WebGLRenderingContext;

const UNIFORMS = ["u_sizeRatio", "u_pixelRatio", "u_matrix"] as const;

export default class NodeBorderProgram extends NodeProgram<typeof UNIFORMS[number]> {
export default class NodeBorderProgram extends NodeProgram<(typeof UNIFORMS)[number]> {
getDefinition() {
return {
VERTICES: 1,
VERTEX_SHADER_SOURCE,
FRAGMENT_SHADER_SOURCE,
METHOD: WebGLRenderingContext.POINTS,
UNIFORMS,
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 },
],
};
}

processVisibleItem(i: number, data: NodeDisplayData) {
processVisibleItem(nodeIndex: number, startIndex: number, data: NodeDisplayData) {
const array = this.array;

array[i++] = data.x;
array[i++] = data.y;
array[i++] = data.size;
array[i] = floatColor(data.color);
array[startIndex++] = data.x;
array[startIndex++] = data.y;
array[startIndex++] = data.size;
array[startIndex++] = floatColor(data.color);
array[startIndex++] = nodeIndex;
}

draw(params: RenderParams): void {
const gl = this.gl;

const { u_sizeRatio, u_pixelRatio, u_matrix } = this.uniformLocations;
setUniforms(params: RenderParams, { gl, uniformLocations }: ProgramInfo): void {
const { u_sizeRatio, u_pixelRatio, u_matrix } = uniformLocations;

gl.uniform1f(u_sizeRatio, params.sizeRatio);
gl.uniform1f(u_pixelRatio, params.pixelRatio);
gl.uniformMatrix3fv(u_matrix, false, params.matrix);

gl.drawArrays(gl.POINTS, 0, this.verticesCount);
}
}
31 changes: 23 additions & 8 deletions src/rendering/webgl/programs/common/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import Sigma from "../../../../sigma";
import { AbstractProgram, Program } from "./program";
import { NodeDisplayData, EdgeDisplayData, RenderParams } from "../../../../types";
import { indexToColor } from "../../../../utils";

export abstract class AbstractEdgeProgram extends AbstractProgram {
abstract process(
edgeIndex: number,
offset: number,
sourceData: NodeDisplayData,
targetData: NodeDisplayData,
Expand All @@ -21,7 +23,13 @@ export abstract class EdgeProgram<Uniform extends string = string>
extends Program<Uniform>
implements AbstractEdgeProgram
{
process(offset: number, sourceData: NodeDisplayData, targetData: NodeDisplayData, data: EdgeDisplayData): void {
process(
edgeIndex: number,
offset: number,
sourceData: NodeDisplayData,
targetData: NodeDisplayData,
data: EdgeDisplayData,
): void {
let i = offset * this.STRIDE;
// NOTE: dealing with hidden items automatically
if (data.hidden || sourceData.hidden || targetData.hidden) {
Expand All @@ -31,18 +39,19 @@ export abstract class EdgeProgram<Uniform extends string = string>
return;
}

return this.processVisibleItem(i, sourceData, targetData, data);
return this.processVisibleItem(indexToColor(edgeIndex), i, sourceData, targetData, data);
}
abstract processVisibleItem(
i: number,
edgeIndex: number,
startIndex: number,
sourceData: NodeDisplayData,
targetData: NodeDisplayData,
data: EdgeDisplayData,
): void;
}

export interface EdgeProgramConstructor {
new (gl: WebGLRenderingContext, renderer: Sigma): AbstractEdgeProgram;
new (gl: WebGLRenderingContext, pickGl: WebGLRenderingContext, renderer: Sigma): AbstractEdgeProgram;
}

/**
Expand All @@ -57,18 +66,24 @@ export function createEdgeCompoundProgram(programClasses: Array<EdgeProgramConst
return class EdgeCompoundProgram implements AbstractEdgeProgram {
programs: Array<AbstractEdgeProgram>;

constructor(gl: WebGLRenderingContext, renderer: Sigma) {
constructor(gl: WebGLRenderingContext, pickGl: WebGLRenderingContext, renderer: Sigma) {
this.programs = programClasses.map((Program) => {
return new Program(gl, renderer);
return new Program(gl, pickGl, renderer);
});
}

reallocate(capacity: number): void {
this.programs.forEach((program) => program.reallocate(capacity));
}

process(offset: number, sourceData: NodeDisplayData, targetData: NodeDisplayData, data: EdgeDisplayData): void {
this.programs.forEach((program) => program.process(offset, sourceData, targetData, data));
process(
edgeIndex: number,
offset: number,
sourceData: NodeDisplayData,
targetData: NodeDisplayData,
data: EdgeDisplayData,
): void {
this.programs.forEach((program) => program.process(edgeIndex, offset, sourceData, targetData, data));
}

render(params: RenderParams): void {
Expand Down
19 changes: 10 additions & 9 deletions src/rendering/webgl/programs/common/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
import Sigma from "../../../../sigma";
import { AbstractProgram, Program } from "./program";
import { NodeDisplayData, RenderParams } from "../../../../types";
import { indexToColor } from "../../../../utils";

export abstract class AbstractNodeProgram extends AbstractProgram {
abstract process(offset: number, data: NodeDisplayData): void;
abstract process(nodeIndex: number, offset: number, data: NodeDisplayData): void;
}

export abstract class NodeProgram<Uniform extends string = string>
extends Program<Uniform>
implements AbstractNodeProgram
{
process(offset: number, data: NodeDisplayData): void {
process(nodeIndex: number, offset: number, data: NodeDisplayData): void {
let i = offset * this.STRIDE;
// NOTE: dealing with hidden items automatically
if (data.hidden) {
Expand All @@ -26,13 +27,13 @@ export abstract class NodeProgram<Uniform extends string = string>
return;
}

return this.processVisibleItem(i, data);
return this.processVisibleItem(indexToColor(nodeIndex), i, data);
}
abstract processVisibleItem(i: number, data: NodeDisplayData): void;
abstract processVisibleItem(nodeIndex: number, i: number, data: NodeDisplayData): void;
}

export interface NodeProgramConstructor {
new (gl: WebGLRenderingContext, renderer: Sigma): AbstractNodeProgram;
new (gl: WebGLRenderingContext, pickGl: WebGLRenderingContext, renderer: Sigma): AbstractNodeProgram;
}

/**
Expand All @@ -47,18 +48,18 @@ export function createNodeCompoundProgram(programClasses: Array<NodeProgramConst
return class NodeCompoundProgram implements AbstractNodeProgram {
programs: Array<AbstractNodeProgram>;

constructor(gl: WebGLRenderingContext, renderer: Sigma) {
constructor(gl: WebGLRenderingContext, pickGl: WebGLRenderingContext, renderer: Sigma) {
this.programs = programClasses.map((Program) => {
return new Program(gl, renderer);
return new Program(gl, pickGl, renderer);
});
}

reallocate(capacity: number): void {
this.programs.forEach((program) => program.reallocate(capacity));
}

process(offset: number, data: NodeDisplayData): void {
this.programs.forEach((program) => program.process(offset, data));
process(nodeIndex: number, offset: number, data: NodeDisplayData): void {
this.programs.forEach((program) => program.process(nodeIndex, offset, data));
}

render(params: RenderParams): void {
Expand Down
Loading

0 comments on commit c20db82

Please sign in to comment.