Skip to content

Commit

Permalink
feat(Graphic3D): add wireframe graphics to draw (Orillusion#42)
Browse files Browse the repository at this point in the history
add Graphic3D
add Graphics3DShape
add Graphic3DBatchRenderer
add Graphic3DFillRenderer
add Graphic3DLineBatchRenderer
  • Loading branch information
Codeboy-cn authored Apr 24, 2023
1 parent 3f46e1b commit 0fdc1e9
Show file tree
Hide file tree
Showing 7 changed files with 961 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { RenderNode } from "../../../../components/renderer/RenderNode";
import { BoundingBox } from "../../../../core/bound/BoundingBox";
import { View3D } from "../../../../core/View3D";
import { Color } from "../../../../math/Color";
import { Vector3 } from "../../../../math/Vector3";
import { ClusterLightingRender } from "../cluster/ClusterLightingRender";
import { RendererMask } from "../state/RendererMask";
import { RendererPassState } from "../state/RendererPassState";
import { RendererType } from "../state/RendererType";
import { Graphic3DFixedRenderPipeline } from "./Graphic3DFixedRenderPipeline";
import { GraphicConfig } from "./GraphicConfig";
import { Graphics3DShape } from "./Graphics3DShape";

/**
* @internal
*/
export class Graphic3DBatchRenderer extends RenderNode {
public shapes: Map<string, Graphics3DShape>;
protected mDirtyData: boolean = false;
protected mMinIndexCount: number;
protected mGPUPrimitiveTopology: GPUPrimitiveTopology;
protected mRenderPipeline: Graphic3DFixedRenderPipeline;

constructor(minIndexCount: number, topology: GPUPrimitiveTopology) {
super();
this.alwaysRender = true;
this.mMinIndexCount = minIndexCount;
this.mGPUPrimitiveTopology = topology;
this.shapes = new Map<string, Graphics3DShape>();
this.addRendererMask(RendererMask.Particle);
}

public fillShapData(uuid: string, type: string, color: Color, points: Vector3[]) {
this.mDirtyData = true;
var data: Graphics3DShape;

if (this.shapes.has(uuid)) {
data = this.shapes.get(uuid);
if (data.shapeData.length < GraphicConfig.ShapeVertexSize * points.length) {
data.shapeData = new Float32Array(GraphicConfig.ShapeVertexSize * points.length);
}
} else {
data = new Graphics3DShape(this.transform._worldMatrix.index);
data.type = type;
data.color = color;
data.shapeData = new Float32Array(GraphicConfig.ShapeVertexSize * points.length);
}

const shapeData = data.shapeData;
const transformIndex = this.transform._worldMatrix.index;
for (let i = 0, index = 0; i < points.length; ++i) {
const point = points[i];
shapeData[index++] = point.x;
shapeData[index++] = point.y;
shapeData[index++] = point.z;
shapeData[index++] = transformIndex;
shapeData[index++] = color.r;
shapeData[index++] = color.g;
shapeData[index++] = color.b;
shapeData[index++] = color.a;
}
this.shapes.set(uuid, data);
}

protected init() {
super.init();
this.castGI = false;
this.castShadow = false;
this.mRenderPipeline = new Graphic3DFixedRenderPipeline(this.mMinIndexCount, this.mGPUPrimitiveTopology);
}

public removeShape(uuid: string) {
if (this.shapes.has(uuid)) {
this.mDirtyData = true;
this.shapes.delete(uuid);
}
}

protected initPipeline() {
this.object3D.bound = new BoundingBox(Vector3.ZERO, Vector3.MAX);
this._readyPipeline = true;
}

public nodeUpdate(view: View3D, passType: RendererType, renderPassState: RendererPassState, clusterLightingRender?: ClusterLightingRender) {
// if(!this.enable || passType != RendererType.COLOR ) return ;
if (this.mDirtyData) {
this.mRenderPipeline.reset();
this.shapes.forEach((shape, uuid) => {
this.mRenderPipeline.addShapeData(shape);
});
this.mDirtyData = false;
}
return;
}

public renderPass2(view: View3D, passType: RendererType, rendererPassState: RendererPassState, clusterLightingRender: ClusterLightingRender, encoder: GPURenderPassEncoder, useBundle: boolean = false) {
// if(!this.enable || passType != RendererType.COLOR ) return ;
this.mRenderPipeline.render(rendererPassState, encoder);
}

public allocGraphics3DShape(uuid: string, transformIndex: number) {
let shape: Graphics3DShape;

if (this.shapes.has(uuid)) {
shape = this.shapes.get(uuid);
shape.reset();
} else {
shape = new Graphics3DShape(transformIndex);
shape.uuid = uuid;
shape.type = 'line';
shape.color = Color.COLOR_WHITE;
this.shapes.set(shape.uuid, shape);
}

this.mDirtyData = true;
return shape;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GPUPrimitiveTopology } from "../../../../..";
import { Graphic3DBatchRenderer } from "./Graphic3DBatchRenderer";

/**
* @internal
*/
export class Graphic3DFillRenderer extends Graphic3DBatchRenderer {
constructor() {
super(3, GPUPrimitiveTopology.triangle_list);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { GPUContext, Preprocessor } from "../../../../..";
import { BlendFactor, BlendMode } from "../../../../materials/BlendMode";
import { GlobalBindGroupLayout } from "../../../graphics/webGpu/core/bindGroups/GlobalBindGroupLayout";
import { GPUCullMode, GPUCompareFunction } from "../../../graphics/webGpu/WebGPUConst";
import { webGPUContext } from "../../../graphics/webGpu/Context3D";
import { RendererPassState } from "../state/RendererPassState";
import { Graphics3DShape } from "./Graphics3DShape";
import Graphic3DShader_vs from "../../../../assets/shader/graphic/Graphic3DShader_vs.wgsl?raw"
import Graphic3DShader_fs from "../../../../assets/shader/graphic/Graphic3DShader_fs.wgsl?raw"

/**
* @internal
*/
export class Graphic3DFixedRenderPipeline {

protected mCount: number;
protected mBatchSize: number;
protected mBatchCount: number;
protected mMinIndexCount: number;
protected mOffset: number;
protected mIndexBuffer: GPUBuffer;
protected mDataBuffer: Float32Array;
protected mBatchBuffers: GPUBuffer[];
protected mVertexShader: GPUShaderModule;
protected mFragmentShader: GPUShaderModule;
protected mRenderPipeline: GPURenderPipeline;
protected mRenderPipelineLayout: GPUPipelineLayout;
protected mVertexBufferLayout: GPUVertexBufferLayout;
protected mGPUPrimitiveTopology: GPUPrimitiveTopology;

constructor(minIndexCount: number, topology: GPUPrimitiveTopology) {
this.mMinIndexCount = minIndexCount;
this.mGPUPrimitiveTopology = topology;
this.mBatchSize = Math.trunc(65536 / this.mMinIndexCount);
this.init();
}

public reset() {
this.mCount = 0;
this.mOffset = 0;
this.mBatchCount = 0;
}

public addShapeData(shape: Graphics3DShape) {
let data = shape.shapeData;
while (data.length > 0) {
if (this.mOffset >= this.mDataBuffer.length) {
this.flush();
}

if (this.mOffset + data.length <= this.mDataBuffer.length) {
this.mDataBuffer.set(data, this.mOffset);
this.mOffset += data.length;
break;
}

let remainLength = this.mDataBuffer.length - this.mOffset;
this.mDataBuffer.set(data.slice(0, remainLength), this.mOffset);
this.mOffset += remainLength;
data = data.slice(remainLength);
}
}

protected flush() {
if (this.mOffset > 0) {
let vertexBuffer: GPUBuffer;
if (this.mBatchCount < this.mBatchBuffers.length) {
vertexBuffer = this.mBatchBuffers[this.mBatchCount];
} else {
vertexBuffer = webGPUContext.device.createBuffer({
size: this.mDataBuffer.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
})
this.mBatchBuffers.push(vertexBuffer);
}
// console.log(`writeBuffer(${this.mBatchCount}, ${this.mOffset})`);
webGPUContext.device.queue.writeBuffer(vertexBuffer, 0, this.mDataBuffer, 0, this.mOffset);
this.mCount += this.mOffset / 8;
this.mBatchCount++;
this.mOffset = 0;
}
}

public render(rendererPassState: RendererPassState, encoder: GPURenderPassEncoder) {
const device = webGPUContext.device;

if (!this.mRenderPipeline) {
let targets = rendererPassState.outAttachments;
if (rendererPassState.outColor != -1) {
let target = targets[rendererPassState.outColor];
target.blend = BlendFactor.getBlend(BlendMode.NONE);
}

this.mRenderPipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [GlobalBindGroupLayout.getGlobalDataBindGroupLayout()],
});

let descriptor: GPURenderPipelineDescriptor = {
label: 'Graphic3DFixedRenderPipeline',
layout: this.mRenderPipelineLayout,
vertex: {
module: this.mVertexShader,
entryPoint: 'main',
buffers: [this.mVertexBufferLayout],
},
fragment: {
module: this.mFragmentShader,
entryPoint: 'main',
targets: targets,
},
primitive: {
topology: this.mGPUPrimitiveTopology,
cullMode: GPUCullMode.back,
frontFace: 'ccw',
},
};

if (rendererPassState.depthTexture) {
descriptor.depthStencil = {
depthWriteEnabled: true,
depthCompare: GPUCompareFunction.less_equal,
format: rendererPassState.depthTexture.format,
};
}

this.mRenderPipeline = GPUContext.createPipeline(descriptor);
}

this.flush();
if (this.mBatchCount > 0) {
// console.log(`[${this.mCount} / ${this.mBatchBuffers.length}]`);
encoder.setPipeline(this.mRenderPipeline);
encoder.setIndexBuffer(this.mIndexBuffer, `uint16`);

let count = this.mCount / this.mMinIndexCount;
for (let i = Math.trunc(count / this.mBatchSize) - 1; i >= 0; i--) {
encoder.setVertexBuffer(0, this.mBatchBuffers[i]);
encoder.drawIndexed(this.mMinIndexCount * this.mBatchSize, 1, 0, 0, 0);
}

count = count % this.mBatchSize;
if (count != 0) {
encoder.setVertexBuffer(0, this.mBatchBuffers[this.mBatchCount - 1]);
encoder.drawIndexed(this.mMinIndexCount * count, 1, 0, 0, 0);
}
}
}

protected init() {
const device = webGPUContext.device;

let indexData = new Uint16Array((Math.trunc(this.mMinIndexCount * this.mBatchSize / 4) + 1) * 4);
for (let i = 0; i < indexData.length; i++) {
indexData[i] = i;
}
this.mIndexBuffer = device.createBuffer({
size: indexData.byteLength,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(this.mIndexBuffer, 0, indexData);

this.mVertexBufferLayout = {
arrayStride: (4 + 4) * 4,
stepMode: 'vertex',
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x4' },
{ shaderLocation: 1, offset: 16, format: 'float32x4' },
],
};

this.mBatchBuffers = [];
this.mDataBuffer = new Float32Array((4 + 4) * indexData.length);
this.mBatchBuffers.push(device.createBuffer({
size: this.mDataBuffer.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
}));

this.mVertexShader = this.createShaderModule(
'Graphic3DFixedRenderPipeline.vs',
Preprocessor.parse(Graphic3DShader_vs, {})
);
this.mFragmentShader = this.createShaderModule(
'Graphic3DFixedRenderPipeline.fs',
Preprocessor.parse(Graphic3DShader_fs, {})
);

this.reset();
}

protected createShaderModule(label: string, code: string): GPUShaderModule {
let shaderModule = webGPUContext.device.createShaderModule({
label: label,
code: code,
});
shaderModule.getCompilationInfo().then((e) => {
if (e.messages.length > 0) {
console.log(code);
console.log(e);
}
});
return shaderModule;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { GPUPrimitiveTopology } from "../../../../..";
import { Graphic3DBatchRenderer } from "./Graphic3DBatchRenderer";

/**
* @internal
*/
export class Graphic3DLineBatchRenderer extends Graphic3DBatchRenderer {
constructor() {
super(2, GPUPrimitiveTopology.line_list);
}
}
Loading

0 comments on commit 0fdc1e9

Please sign in to comment.