Skip to content

Commit

Permalink
feat: draw wireframe for geometry in lesson5
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Dec 25, 2024
1 parent 8bb780f commit e01cf91
Show file tree
Hide file tree
Showing 31 changed files with 1,049 additions and 609 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ pnpm run dev

- Drawing straight lines using Line Geometry or screen-space techniques.
- Drawing dots grid.
- Drawing wireframe.
- Drawing wireframe for Geometry.

<img src="./screenshots/lesson5.png" width="300" alt="Lesson 5">
<img src="./screenshots/lesson5.png" width="300" alt="Lesson 5 grids">
<img src="./screenshots/lesson5-2.png" width="300" alt="Lesson 5 wireframe">

## Lesson 6 - Event system [🔗](https://infinitecanvas.cc/guide/lesson-006)

Expand Down
5 changes: 3 additions & 2 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,10 @@ pnpm run dev

- 绘制直线网格。使用 Line Geometry 或者屏幕空间技术
- 绘制点网格
- 绘制 wireframe
- 为 Geometry 绘制 wireframe

<img src="./screenshots/lesson5.png" width="300" alt="Lesson 5">
<img src="./screenshots/lesson5.png" width="300" alt="Lesson 5 grids">
<img src="./screenshots/lesson5-2.png" width="300" alt="Lesson 5 wireframe">

## 课程 6 - 事件系统 [🔗](https://infinitecanvas.cc/zh/guide/lesson-006)

Expand Down
Binary file added __tests__/ssr/snapshots/wireframe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions __tests__/ssr/wireframe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import _gl from 'gl';
import { getCanvas } from '../utils';
import '../useSnapshotMatchers';
import { Canvas, Circle } from '../../packages/core/src';

const dir = `${__dirname}/snapshots`;
let $canvas: HTMLCanvasElement;
let canvas: Canvas;

describe('Wireframe', () => {
beforeEach(async () => {
$canvas = getCanvas(200, 200);
canvas = await new Canvas({
canvas: $canvas,
}).initialized;
});

afterEach(() => {
canvas.destroy();
});

it('should render wireframe for circle correctly.', async () => {
const circle = new Circle({
cx: 100,
cy: 100,
r: 50,
fill: 'grey',
wireframe: true,
});
canvas.appendChild(circle);
canvas.render();

expect($canvas.getContext('webgl1')).toMatchWebGLSnapshot(dir, 'wireframe');
});
});
64 changes: 57 additions & 7 deletions packages/core/examples/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,63 @@ const canvas = await new Canvas({
// shaderCompilerPath: '/glsl_wgsl_compiler_bg.wasm',
}).initialized;

const circle = new Circle({
cx: 50,
cy: 50,
r: 4,
fill: '#F67676',
});
canvas.appendChild(circle);
for (let i = 0; i < 2; i++) {
const circle = new Circle({
cx: i * 100,
cy: i * 100,
r: 50,
fill: `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
Math.random() * 255,
)},${Math.floor(Math.random() * 255)})`,
batchable: true,
wireframe: true,
stroke: 'black',
strokeWidth: 10,
strokeOpacity: 0.5,
});
canvas.appendChild(circle);
}

// const rect = new Rect({
// x: 300,
// y: 100,
// width: 100,
// height: 100,
// fill: '#F67676',
// // stroke: 'black',
// // strokeWidth: 10,
// dropShadowBlurRadius: 10,
// dropShadowColor: 'rgba(0, 0, 0, 0.5)',
// dropShadowOffsetX: 10,
// dropShadowOffsetY: 10,
// // batchable: false,
// wireframe: true,
// });
// canvas.appendChild(rect);

// const path = new Path({
// d: 'M 100 100 L 200 200 L 300 100 L 400 200 L 500 100 Z',
// fill: '#F67676',
// batchable: false,
// wireframe: true,
// });
// canvas.appendChild(path);

// const polyline = new Polyline({
// points: [
// [100, 100],
// [200, 200],
// [300, 100],
// ],
// stroke: '#F67676',
// strokeWidth: 20,
// strokeLinecap: 'round',
// strokeLinejoin: 'round',
// // bowing: 2,
// // roughness: 4,
// wireframe: true,
// });
// canvas.appendChild(polyline);

// const text = new Text({
// x: 50,
Expand Down
178 changes: 175 additions & 3 deletions packages/core/src/drawcalls/Drawcall.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import { Buffer, Device, RenderPass } from '@antv/g-device-api';
import {
Buffer,
Device,
InputLayoutBufferDescriptor,
Program,
RenderPass,
RenderPipeline,
InputLayout,
Bindings,
BufferUsage,
BufferFrequencyHint,
VertexStepMode,
Format,
} from '@antv/g-device-api';
import { Shape } from '../shapes';
import { RenderCache } from '../utils/render-cache';
import { uid } from '../utils';
import { isString, uid } from '../utils';
import { Location } from '../shaders/wireframe';

// TODO: Use a more efficient way to manage Z index.
export const ZINDEX_FACTOR = 100000;
Expand All @@ -20,6 +34,21 @@ export abstract class Drawcall {
materialDirty = true;
destroyed = false;

protected program: Program;
protected pipeline: RenderPipeline;
protected inputLayout: InputLayout;
protected bindings: Bindings;

protected indexBuffer: Buffer;
protected indexBufferData: Uint32Array;

protected vertexBuffers: Buffer[] = [];
protected vertexBufferDatas: Float32Array[] = [];
protected vertexBufferOffsets: number[] = [];

protected vertexBufferDescriptors: InputLayoutBufferDescriptor[];
protected barycentricBufferIndex = -1;

constructor(
protected device: Device,
protected renderCache: RenderCache,
Expand All @@ -35,10 +64,21 @@ export abstract class Drawcall {
): void;

destroy() {
if (this.program) {
this.indexBuffer?.destroy();
this.vertexBuffers.forEach((buffer) => buffer.destroy());
this.vertexBuffers = [];
this.vertexBufferDatas = [];
this.vertexBufferDescriptors = [];
}
this.destroyed = true;
}

validate(_: Shape) {
validate(shape: Shape) {
if (this.shapes[0]?.wireframe !== shape.wireframe) {
return false;
}

return this.count() <= this.maxInstances - 1;
}

Expand All @@ -49,6 +89,10 @@ export abstract class Drawcall {
) {
if (this.geometryDirty) {
this.createGeometry();

if (this.useWireframe) {
this.generateWireframeVertexBufferDescriptors();
}
}

if (this.materialDirty) {
Expand Down Expand Up @@ -84,4 +128,132 @@ export abstract class Drawcall {
count() {
return this.shapes.length;
}

protected get useWireframe() {
return this.shapes[0]?.wireframe;
}

protected get useFillImage() {
const { fill } = this.shapes[0];
return !isString(fill);
// && (isBrowser ? isImageBitmapOrCanvases(fill) : true)
}

protected generateWireframeVertexBufferDescriptors() {
const barycentricBufferDescriptor = {
arrayStride: 4 * 3,
stepMode: VertexStepMode.VERTEX,
attributes: [
{
shaderLocation: Location.BARYCENTRIC, // a_Barycentric
offset: 0,
format: Format.F32_RGB,
},
],
};

if (this.barycentricBufferIndex === -1) {
this.vertexBufferDescriptors.push(barycentricBufferDescriptor);
this.barycentricBufferIndex = this.vertexBufferDescriptors.length - 1;
} else {
this.vertexBufferDescriptors[this.barycentricBufferIndex] =
barycentricBufferDescriptor;
}
}

protected generateWireframe() {
const indiceNum = this.indexBufferData.length;
const originalVertexBuffers = this.vertexBufferDatas.map((buffer) => {
return buffer.slice();
});

for (let i = 0; i < this.vertexBufferDatas.length; i++) {
if (i === this.barycentricBufferIndex) {
continue;
}

const { arrayStride, stepMode } = this.vertexBufferDescriptors[i];
if (stepMode === VertexStepMode.VERTEX) {
this.vertexBufferDatas[i] = new Float32Array(
(arrayStride / Float32Array.BYTES_PER_ELEMENT) * indiceNum,
);
} else {
this.vertexBufferDatas[i] = originalVertexBuffers[i];
}
}

// reallocate attribute data
let cursor = 0;
const uniqueIndices = new Uint32Array(indiceNum);
for (let i = 0; i < indiceNum; i++) {
const ii = this.indexBufferData[i];
for (let j = 0; j < this.vertexBufferDatas.length; j++) {
if (j === this.barycentricBufferIndex) {
continue;
}

const { arrayStride, stepMode } = this.vertexBufferDescriptors[j];

if (stepMode === VertexStepMode.VERTEX) {
const size = arrayStride / Float32Array.BYTES_PER_ELEMENT;
for (let k = 0; k < size; k++) {
this.vertexBufferDatas[j][cursor * size + k] =
originalVertexBuffers[j][ii * size + k];
}
}
}
uniqueIndices[i] = cursor;
cursor++;
}

for (let i = 0; i < this.vertexBuffers.length; i++) {
if (i === this.barycentricBufferIndex) {
continue;
}

this.vertexBuffers[i].destroy();
this.vertexBuffers[i] = this.device.createBuffer({
viewOrSize: this.vertexBufferDatas[i],
usage: BufferUsage.VERTEX,
hint: BufferFrequencyHint.DYNAMIC,
});
}

// create barycentric attributes
const barycentricBufferData = new Float32Array(indiceNum * 3);
for (let i = 0; i < indiceNum; ) {
for (let j = 0; j < 3; j++) {
const ii = uniqueIndices[i++];
barycentricBufferData[ii * 3 + j] = 1;
}
}

const barycentricBuffer = this.device.createBuffer({
viewOrSize: barycentricBufferData,
usage: BufferUsage.VERTEX,
hint: BufferFrequencyHint.DYNAMIC,
});

if (this.indexBuffer) {
this.indexBuffer.destroy();
}
this.indexBuffer = this.device.createBuffer({
viewOrSize: uniqueIndices,
usage: BufferUsage.INDEX,
hint: BufferFrequencyHint.STATIC,
});

if (this.barycentricBufferIndex === -1) {
this.barycentricBufferIndex = this.vertexBufferDescriptors.length - 1;
this.vertexBuffers.push(barycentricBuffer);
this.vertexBufferDatas.push(barycentricBufferData);
} else {
this.vertexBuffers[this.barycentricBufferIndex]?.destroy();
this.vertexBuffers[this.barycentricBufferIndex] = barycentricBuffer;
this.vertexBufferDatas[this.barycentricBufferIndex] =
barycentricBufferData;
}

console.log(originalVertexBuffers, this.vertexBufferDatas);
}
}
Loading

0 comments on commit e01cf91

Please sign in to comment.