Merge remote-tracking branch 'origin/webgpu-2' into webgpu-true

This commit is contained in:
Vitaly Turovsky 2024-05-25 03:41:57 +03:00
commit 408df667bb
7 changed files with 236 additions and 695 deletions

View file

@ -4,7 +4,7 @@
@fragment
fn main(
@location(0) fragUV: vec2f,
@location(1) fragPosition: vec4f
@location(1) TextueIndex: f32
) -> @location(0) vec4f {
return textureSample(myTexture, mySampler, fragUV/64);
return textureSample(myTexture, mySampler, fragUV/64.0 + vec2f(TextueIndex%32,TextueIndex/32.0 )/32.0);
}

View file

@ -1,28 +1,24 @@
struct Uniforms {
modelViewProjectionMatrix : mat4x4f,
//modelViewProjectionMatrix : array<mat4x4f, 16>,
//ProjectionMatrix : mat4x4f,
// ViewMatrix: mat4x4f
ViewProjectionMatrix : mat4x4f,
}
@binding(0) @group(0) var<uniform> uniforms : Uniforms;
struct VertexOutput {
@builtin(position) Position : vec4f,
@location(0) fragUV : vec2f,
@location(1) fragPosition: vec4f,
@location(1) TextueIndex: f32
}
@vertex
fn main(
//@builtin(instance_index) instanceIdx : u32,
@location(0) position : vec4f,
@location(1) uv : vec2f
//@location(2) ModelMatrix : mat4x4f,
@location(1) uv : vec2f,
@location(2) ModelMatrix : vec3f,
@location(3) TextureIndex : f32
) -> VertexOutput {
var output : VertexOutput;
output.Position = uniforms.modelViewProjectionMatrix * position;
//output.Position = uniforms.modelViewProjectionMatrix[instanceIdx] * position;
output.Position = uniforms.ViewProjectionMatrix * (position +vec4f(ModelMatrix, 0.0));
output.fragUV = uv;
output.fragPosition = 0.5 * (position + vec4(1.0, 1.0, 1.0, 1.0));
output.TextueIndex = TextureIndex;
return output;
}

View file

@ -0,0 +1,95 @@
export const cubeVertexSize = 4 * 5 // Byte size of one cube vertex.
export const cubePositionOffset = 0
//export const cubeColorOffset = 4 * 3 // Byte offset of cube vertex color attribute.
export const cubeUVOffset = 4 * 3
export const cubeVertexCount = 36
//@ts-format-ignore-region
export const cubeVertexArray = new Float32Array([
-0.5, -0.5, -0.5, 0.0, 0.0, // Bottom-let
0.5, -0.5, -0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
-0.5, 0.5, -0.5, 0.0, 1.0, // top-let
-0.5, -0.5, -0.5, 0.0, 0.0, // bottom-let
// ront ace
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
0.5, 0.5, 0.5, 1.0, 1.0, // top-right
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, 0.5, 1.0, 1.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
-0.5, 0.5, 0.5, 0.0, 1.0, // top-let
// Let ace
-0.5, 0.5, 0.5, 1.0, 0.0, // top-right
-0.5, -0.5, -0.5, 0.0, 1.0, // bottom-let
-0.5, 0.5, -0.5, 1.0, 1.0, // top-let
-0.5, -0.5, -0.5, 0.0, 1.0, // bottom-let
-0.5, 0.5, 0.5, 1.0, 0.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-right
// Right ace
0.5, 0.5, 0.5, 1.0, 0.0, // top-let
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right
0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right
0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
0.5, 0.5, 0.5, 1.0, 0.0, // top-let
// Bottom ace
-0.5, -0.5, -0.5, 0.0, 1.0, // top-right
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-let
0.5, -0.5, -0.5, 1.0, 1.0, // top-let
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-let
-0.5, -0.5, -0.5, 0.0, 1.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-right
// Top ace
-0.5, 0.5, -0.5, 0.0, 1.0, // top-let
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right
-0.5, 0.5, 0.5, 0.0, 0.0, // bottom-let
-0.5, 0.5, -0.5, 0.0, 1.0// top-let˚
]);
export const cubeVertexArraySub = new Float32Array([
-0.5, -0.5, -0.5, 0.0, 0.0, // Bottom-let
0.5, -0.5, -0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
-0.5, 0.5, -0.5, 0.0, 1.0, // top-let
-0.5, -0.5, -0.5, 0.0, 0.0, // bottom-let
// ront ace
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
0.5, 0.5, 0.5, 1.0, 1.0, // top-right
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, 0.5, 1.0, 1.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
-0.5, 0.5, 0.5, 0.0, 1.0, // top-let
// Let ace
-0.5, 0.5, 0.5, 1.0, 0.0, // top-right
-0.5, -0.5, -0.5, 0.0, 1.0, // bottom-let
-0.5, 0.5, -0.5, 1.0, 1.0, // top-let
-0.5, -0.5, -0.5, 0.0, 1.0, // bottom-let
-0.5, 0.5, 0.5, 1.0, 0.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-right
// Right ace
0.5, 0.5, 0.5, 1.0, 0.0, // top-let
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right
0.5, -0.5, -0.5, 0.0, 1.0, // bottom-right
0.5, -0.5, 0.5, 0.0, 0.0, // bottom-let
0.5, 0.5, 0.5, 1.0, 0.0, // top-let
// Bottom ace
-0.5, -0.5, -0.5, 0.0, 1.0, // top-right
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-let
0.5, -0.5, -0.5, 1.0, 1.0, // top-let
0.5, -0.5, 0.5, 1.0, 0.0, // bottom-let
-0.5, -0.5, -0.5, 0.0, 1.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, // bottom-right
// Top ace
-0.5, 0.5, -0.5, 0.0, 1.0, // top-let
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right
0.5, 0.5, 0.5, 1.0, 0.0, // bottom-right
-0.5, 0.5, 0.5, 0.0, 0.0, // bottom-let
-0.5, 0.5, -0.5, 0.0, 1.0, 5.0// top-let˚
]);

View file

@ -1,51 +0,0 @@
export const cubeVertexSize = 4 * 10 // Byte size of one cube vertex.
export const cubePositionOffset = 0
export const cubeColorOffset = 4 * 4 // Byte offset of cube vertex color attribute.
export const cubeUVOffset = 4 * 8
export const cubeVertexCount = 36
//@ts-format-ignore-region
export const cubeVertexArray = new Float32Array([
// float4 position, float4 color, float2 uv,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, 1, 1, 1, 0, 1, 1, 1, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, -1, 1, 1, 0, 0, 1, 1, 0,
-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,
-1, 1, -1, 1, 0, 1, 0, 1, 0, 0,
-1, 1, 1, 1, 0, 1, 1, 1, 0, 1,
1, 1, -1, 1, 1, 1, 0, 1, 1, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
-1, -1, -1, 1, 0, 0, 0, 1, 0, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
-1, 1, 1, 1, 0, 1, 1, 1, 1, 1,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
-1, -1, 1, 1, 0, 0, 1, 1, 1, 0,
1, -1, 1, 1, 1, 0, 1, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 1,
1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, -1, -1, 1, 0, 0, 0, 1, 1, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
1, 1, -1, 1, 1, 1, 0, 1, 0, 0,
1, -1, -1, 1, 1, 0, 0, 1, 0, 1,
-1, 1, -1, 1, 0, 1, 0, 1, 1, 0,
]);

View file

@ -13,12 +13,16 @@ import { TWEEN_DURATION } from '../viewer/lib/entities'
import { EntityMesh } from '../viewer/lib/entity/EntityMesh'
// import * as Mathgl from 'math.gl'
import { findTextureInBlockStates } from '../../src/playerWindows'
import { initWebgpuRenderer, loadFixtureSides, setAnimationTick } from './webgpuRendererMain'
import { initWebgpuRenderer, loadFixtureSides, setAnimationTick, webgpuChannel } from './webgpuRendererMain'
import { renderToDom } from '@zardoy/react-util'
globalThis.THREE = THREE
//@ts-ignore
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { renderPlayground } from './TouchControls2'
import { WorldRendererWebgpu } from '../viewer/lib/worldrendererWebgpu'
import { TextureAnimation } from './TextureAnimation'
import { BlockType } from './shared'
const gui = new GUI()
@ -182,6 +186,12 @@ async function main () {
}
direction.applyQuaternion(viewer.camera.quaternion)
direction.y = 0
if (pressedKeys.has('ShiftLeft')) {
direction.y *= 2
direction.x *= 2
direction.z *= 2
}
// Add the vector to the camera's position to move the camera
viewer.camera.position.add(direction)
}
@ -242,6 +252,34 @@ async function main () {
viewer.camera.position.set(pos[0], pos[1], pos[2])
}
let blocks: Record<string, BlockType> = {}
let i = 0
for (let x = 0; x < 1; x++) {
blocks = {}
for (let i = 0; i < 1000; i++) {
const max = 100
const pos = new Vec3(Math.floor(Math.random() * max), Math.floor(Math.random() * max), Math.floor(Math.random() * max))
const getFace = (face: number) => {
return {
face,
textureIndex: Math.floor(Math.random() * 512)
}
}
blocks[`${pos.x},${pos.y},${pos.z}`] = {
sides: [
getFace(0),
getFace(1),
getFace(2),
getFace(3),
getFace(4),
getFace(5)
],
}
}
webgpuChannel.addBlocksSection({ blocks }, `0,0,${i++}`)
}
return
// Create viewer

View file

@ -1,593 +0,0 @@
import * as THREE from 'three'
//@ts-ignore
import VertShader from './_VertexShader.vert'
//@ts-ignore
import FragShader from './_FragmentShader.frag'
import { BlockFaceType, BlockType } from './shared'
import * as tweenJs from '@tweenjs/tween.js'
let allSides = [] as ([number, number, number, BlockFaceType] | undefined)[]
let allSidesAdded = 0
let needsSidesUpdate = false
let chunksArrIndexes = {}
let freeArrayIndexes = [] as [number, number][]
let rendering = true
let sidePositions
let updateCubes: (startIndex: any, forceUpdate?) => void
let lastNotUpdatedIndex
let lastNotUpdatedArrSize
let animationTick = 0;
const updateCubesWhenAvailable = (pos) => {
if (updateCubes) {
updateCubes(pos)
} else {
setTimeout(updateCubesWhenAvailable, 100)
}
}
const camera = new THREE.PerspectiveCamera(75, 1 / 1, 0.1, 1000)
let renderedFrames = 0
setInterval(() => {
// console.log('FPS:', renderedFrames)
postMessage({ type: 'fps', fps: renderedFrames })
renderedFrames = 0
}, 1000)
const updateSize = (width, height) => {
camera.aspect = width / height
camera.updateProjectionMatrix()
}
export const initWebglRenderer = async (canvas: HTMLCanvasElement, imageBlob: ImageBitmapSource, isPlayground: boolean, FragShaderOverride?) => {
// isPlayground = false
// blockStates = blockStatesJson
const textureBitmap = await createImageBitmap(imageBlob)
const textureWidth = textureBitmap.width
const textureHeight = textureBitmap.height
const gl = canvas.getContext('webgl2')!
const program = createProgram(gl, VertShader, FragShaderOverride || FragShader)
let CubeMesh = new Float32Array([
-0.5, -0.5, -0.5, 0.0, 0.0, 0.0, // Bottom-let
0.5, -0.5, -0.5, 1.0, 0.0, 0.0, // bottom-right
0.5, 0.5, -0.5, 1.0, 1.0, 0.0, // top-right
0.5, 0.5, -0.5, 1.0, 1.0, 0.0, // top-right
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0, // top-let
-0.5, -0.5, -0.5, 0.0, 0.0, 0.0, // bottom-let
// ront ace
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0, // bottom-let
0.5, 0.5, 0.5, 1.0, 1.0, 1.0, // top-right
0.5, -0.5, 0.5, 1.0, 0.0, 1.0, // bottom-right
0.5, 0.5, 0.5, 1.0, 1.0, 1.0,// top-right
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,// bottom-let
-0.5, 0.5, 0.5, 0.0, 1.0, 1.0,// top-let
// Let ace
-0.5, 0.5, 0.5, 1.0, 0.0, 2.0,// top-right
-0.5, -0.5, -0.5, 0.0, 1.0, 2.0,// bottom-let
-0.5, 0.5, -0.5, 1.0, 1.0, 2.0,// top-let
-0.5, -0.5, -0.5, 0.0, 1.0, 2.0,// bottom-let
-0.5, 0.5, 0.5, 1.0, 0.0, 2.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, 2.0,// bottom-right
// Right ace
0.5, 0.5, 0.5, 1.0, 0.0, 3.0,// top-let
0.5, 0.5, -0.5, 1.0, 1.0, 3.0,// top-right
0.5, -0.5, -0.5, 0.0, 1.0, 3.0,// bottom-right
0.5, -0.5, -0.5, 0.0, 1.0, 3.0,// bottom-right
0.5, -0.5, 0.5, 0.0, 0.0, 3.0,// bottom-let
0.5, 0.5, 0.5, 1.0, 0.0, 3.0, // top-let
// Bottom ace
-0.5, -0.5, -0.5, 0.0, 1.0, 4.0,// top-right
0.5, -0.5, 0.5, 1.0, 0.0, 4.0,// bottom-let
0.5, -0.5, -0.5, 1.0, 1.0, 4.0,// top-let
0.5, -0.5, 0.5, 1.0, 0.0, 4.0, // bottom-let
-0.5, -0.5, -0.5, 0.0, 1.0, 4.0, // top-right
-0.5, -0.5, 0.5, 0.0, 0.0, 4.0, // bottom-right
// Top ace
-0.5, 0.5, -0.5, 0.0, 1.0, 5.0,// top-let
0.5, 0.5, -0.5, 1.0, 1.0, 5.0,// top-right
0.5, 0.5, 0.5, 1.0, 0.0, 5.0,// bottom-right
0.5, 0.5, 0.5, 1.0, 0.0, 5.0,// bottom-right
-0.5, 0.5, 0.5, 0.0, 0.0, 5.0,// bottom-let
-0.5, 0.5, -0.5, 0.0, 1.0, 5.0// top-let
])
let SideMesh = new Float32Array([
-0.5, -0.5, -0.5, 0.0, 0.0, // Bottom-let
0.5, -0.5, -0.5, 1.0, 0.0, // bottom-right
-0.5, 0.5, -0.5, 0.0, 1.0, // top-let
0.5, 0.5, -0.5, 1.0, 1.0, // top-right
// 0.5, 0.5, -0.5, 1.0, 1.0, // top-right
// -0.5, -0.5, -0.5, 0.0, 0.0, // bottom-let
// ront ace
])
let NumberOfCube = isPlayground ? 10_000 : 16_000_000
sidePositions = new Float32Array(NumberOfCube * 3 * 6)
let sideTextureIndices = new Float32Array(NumberOfCube * 1 * 6);
let sideIndexes = new Float32Array(NumberOfCube * 1 * 6);
let sideBiomeColor = new Float32Array(NumberOfCube * 3 * 6);
// write random coordinates to cube positions xyz ten cubes;
if (isPlayground) {
for (let i = 0; i < NumberOfCube * 18; i += 18) {
sidePositions[i] = Math.floor(Math.random() * 1000) - 500;
sidePositions[i + 1] = Math.floor(Math.random() * 1000) - 500;
sidePositions[i + 2] = Math.floor(Math.random() * 100) - 100;
sideBiomeColor[i] = (Math.random());
sideBiomeColor[i + 1] = (Math.random());
sideBiomeColor[i + 2] = (Math.random());
for (let j = 1; j <= 6; j++) {
if (j != 6) {
sidePositions[j * 3 + i] = sidePositions[i]
sidePositions[j * 3 + i + 1] = sidePositions[i + 1]
sidePositions[j * 3 + i + 2] = sidePositions[i + 2]
sideBiomeColor[j * 3 + i] = sideBiomeColor[i]
sideBiomeColor[j * 3 + i + 1] = sideBiomeColor[i + 1]
sideBiomeColor[j * 3 + i + 2] = sideBiomeColor[i + 2]
}
sideIndexes[i / 3 + j - 1] = j - 1;
sideTextureIndices[i / 3 + j - 1] = Math.floor(Math.random() * 800);
//sideTextureIndices[i / 3 + j - 1] = 1;
}
}
}
let VAO = gl.createVertexArray();
let instanceVBO = gl.createBuffer();
let instanceTextureID = gl.createBuffer();
let instanceBiomeColor = gl.createBuffer();
let instanceCubeSide = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, instanceVBO);
gl.bufferData(gl.ARRAY_BUFFER, sidePositions, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceTextureID);
gl.bufferData(gl.ARRAY_BUFFER, sideTextureIndices, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceBiomeColor);
gl.bufferData(gl.ARRAY_BUFFER, sideBiomeColor, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceCubeSide);
gl.bufferData(gl.ARRAY_BUFFER, sideIndexes, gl.DYNAMIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
VAO = gl.createVertexArray();
let VBO = gl.createBuffer();
// let VBO_sides = gl.createBuffer();
//EBO = gl.createBuffer();
gl.bindVertexArray(VAO);
gl.bindBuffer(gl.ARRAY_BUFFER, VBO)
// gl.bindBuffer(gl.ARRAY_BUFFER, VBO_sides)
gl.bufferData(gl.ARRAY_BUFFER, SideMesh, gl.STATIC_DRAW)
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 5 * 4, 0)
gl.enableVertexAttribArray(0)
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 5 * 4, 3 * 4)
gl.enableVertexAttribArray(1)
gl.enableVertexAttribArray(2);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceCubeSide);
gl.vertexAttribPointer(2, 1, gl.FLOAT, false, 4, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.vertexAttribDivisor(2, 1);
gl.enableVertexAttribArray(3);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceVBO);
gl.vertexAttribPointer(3, 3, gl.FLOAT, false, 3 * 4, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.vertexAttribDivisor(3, 1);
gl.enableVertexAttribArray(4);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceTextureID);
gl.vertexAttribPointer(4, 1, gl.FLOAT, false, 4 * 1, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.vertexAttribDivisor(4, 1);
gl.enableVertexAttribArray(6);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceBiomeColor);
gl.vertexAttribPointer(6, 3, gl.FLOAT, false, 3 * 4, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.vertexAttribDivisor(6, 1);
updateCubes = (startIndex, forceUpdate) => {
// up2
const newSides = allSides.slice(startIndex, lastNotUpdatedArrSize ? startIndex + lastNotUpdatedArrSize : undefined)
newSides.sort((a, b) => {
if (!a || !b) return 0
const getScore = (b: BlockFaceType) => b.isTransparent ? 1 : 0
return getScore(a[3]) - getScore(b[3])
})
globalThis.allSidesSize = allSides.length
sidePositions = new Float32Array(newSides.length * 3)
sideTextureIndices = new Float32Array(newSides.length * 1);
sideIndexes = new Float32Array(newSides.length * 1);
sideBiomeColor = new Float32Array(newSides.length * 3);
for (let i = 0; i < newSides.length * 3; i += 3) {
const newSide = newSides[i / 3];
if (!newSide) continue
sidePositions[i] = newSide[0]
sidePositions[i + 1] = newSide[1]
sidePositions[i + 2] = newSide[2]
const block = newSide[3] as BlockFaceType
if (block.tint) {
const [r, g, b] = block.tint
sideBiomeColor[i] = r
sideBiomeColor[i + 1] = g
sideBiomeColor[i + 2] = b
} else {
sideBiomeColor[i] = 1
sideBiomeColor[i + 1] = 1
sideBiomeColor[i + 2] = 1
}
sideTextureIndices[i / 3] = block.textureIndex
sideIndexes[i / 3] = block.face
}
// startIndex = 0 // TODO!
console.log('startIndex', startIndex, sidePositions.length, allSides.length)
const prepareBuffersUpdate = allSides.length > NumberOfCube || globalThis.testUpdate
globalThis.testUpdate = false
if (prepareBuffersUpdate) {
NumberOfCube += 1_000_000
updateCubes(0, true)
return
}
globalThis.NumberOfCube = NumberOfCube
const supplyData = (data, step) => {
if (forceUpdate) {
globalThis.updatedBufferSize = NumberOfCube
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(NumberOfCube * step), gl.STATIC_DRAW);
}
gl.bufferSubData(gl.ARRAY_BUFFER, startIndex * 4 * step, data); // update buffer content
const error = gl.getError()
if (error) throw new Error("SUBDATA ERROR")
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
gl.bindBuffer(gl.ARRAY_BUFFER, instanceVBO);
supplyData(sidePositions, 3)
gl.bindBuffer(gl.ARRAY_BUFFER, instanceTextureID);
supplyData(sideTextureIndices, 1)
gl.bindBuffer(gl.ARRAY_BUFFER, instanceBiomeColor);
supplyData(sideBiomeColor, 3)
gl.bindBuffer(gl.ARRAY_BUFFER, instanceCubeSide);
supplyData(sideIndexes, 1)
allSidesAdded = allSides.length
needsSidesUpdate = true
lastNotUpdatedArrSize = undefined
}
globalThis.updateCubes = updateCubes
globalThis.resetHalfScene = () => {
for (let i = 0; i < allSides.length / 2; i++) {
allSides[i] = undefined
}
lastNotUpdatedIndex = 0
lastNotUpdatedArrSize = allSides.length / 2
updateCubes(0)
}
const cleanupFirstChunks = () => {
allSides = []
gl.bindBuffer(gl.ARRAY_BUFFER, instanceVBO);
// empty the buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(NumberOfCube * 3), gl.STATIC_DRAW); // todo
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ARRAY_BUFFER, instanceTextureID);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(NumberOfCube * 3), gl.STATIC_DRAW); // todo
// gl.bufferSubData(gl.ARRAY_BUFFER, startIndex * 4, cubeTextureIndices); // update buffer content
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
fullReset = () => {
cleanupFirstChunks()
lastNotUpdatedIndex = undefined
lastNotUpdatedArrSize = undefined
}
//gl.bindBuffer(gl.ARRAY_BUFFER, null);
//gl.bindVertexArray(null)
// viewer.world.updateTexturesData()
// await new Promise(resolve => {
// // console.log('viewer.world.material.map!.image', viewer.world.material.map!.image)
// // viewer.world.material.map!.image.onload = () => {
// // console.log(this.material.map!.image)
// // resolve()
// // }
// viewer.world.renderUpdateEmitter.once('blockStatesDownloaded', resolve)
// })
// const names = Object.keys(viewer.world.downloadedBlockStatesData)
let texture1 = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureWidth, textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, textureBitmap);
gl.useProgram(program)
gl.uniform1i(gl.getUniformLocation(program, "texture1"), 0);
gl.uniform1i(gl.getUniformLocation(program, "texture2"), 1);
gl.enable(gl.DEPTH_TEST)
gl.frontFace(gl.CCW)
gl.enable(gl.CULL_FACE)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
//gl.generateMipmap()
//gl.enable(gl)
//gl.clearColor(0, 0, 0, 1)
//gl.clear(gl.COLOR_BUFFER_BIT)
camera.up = new THREE.Vector3(0, 1, 0)
let ViewUniform = gl.getUniformLocation(program, "view")
let ProjectionUniform = gl.getUniformLocation(program, "projection")
let TickUniform = gl.getUniformLocation(program, "tick")
gl.cullFace(gl.FRONT)
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.bindVertexArray(VAO)
gl.useProgram(program)
updateSize(gl.canvas.width, gl.canvas.height)
const renderLoop = (performance) => {
requestAnimationFrame(renderLoop)
if (!rendering && !needsSidesUpdate) return
// gl.canvas.width = window.innerWidth * window.devicePixelRatio
// gl.canvas.height = window.innerHeight * window.devicePixelRatio
if (newWidth || newHeight) {
gl.canvas.width = newWidth ?? gl.canvas.width
gl.canvas.height = newHeight ?? gl.canvas.height
newWidth = undefined
newHeight = undefined
updateSize(gl.canvas.width, gl.canvas.height)
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height)
}
gl.clearColor(0.6784313725490196, 0.8470588235294118, 0.9019607843137255, 1);
gl.clear(gl.COLOR_BUFFER_BIT)
gl.clear(gl.DEPTH_BUFFER_BIT)
tweenJs.update()
camera.updateMatrix()
gl.uniformMatrix4fv(ViewUniform, false, camera.matrix.invert().elements);
gl.uniformMatrix4fv(ProjectionUniform, false, camera.projectionMatrix.elements);
gl.uniform1i(TickUniform, animationTick);
if (!globalThis.stopRendering) {
gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, (isPlayground ? NumberOfCube * 6 : allSidesAdded));
needsSidesUpdate = false
}
renderedFrames++
}
requestAnimationFrame(renderLoop)
// gl.deleteVertexArray(VAO);
// gl.deleteBuffer(VBO)
// gl.deleteBuffer(EBO)
// gl.deleteProgram(program)
return canvas
}
let fullReset
const createProgram = (gl: WebGL2RenderingContext, vertexShader: string, fragmentShader: string) => {
const createShader = (gl: WebGL2RenderingContext, type: number, source: string) => {
const shaderName = type === gl.VERTEX_SHADER ? 'vertex' : 'fragment'
const shader = gl.createShader(type)!
gl.shaderSource(shader, source)
gl.compileShader(shader)
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS)
if (!success) {
const info = gl.getShaderInfoLog(shader)
gl.deleteShader(shader)
throw new Error(`Shader ${shaderName} compile error: ` + info)
}
return shader
}
const program = gl.createProgram()!
gl.attachShader(program, createShader(gl, gl.VERTEX_SHADER, vertexShader)!)
gl.attachShader(program, createShader(gl, gl.FRAGMENT_SHADER, fragmentShader)!)
gl.linkProgram(program)
const linkSuccess = gl.getProgramParameter(program, gl.LINK_STATUS)
if (!linkSuccess) {
const info = gl.getProgramInfoLog(program)
gl.deleteProgram(program)
throw new Error('Program link error: ' + info)
}
return program
}
let started = false
let newWidth: number | undefined
let newHeight: number | undefined
let autoTickUpdate = undefined as number | undefined
onmessage = function (e) {
if (!started) {
started = true
initWebglRenderer(e.data.canvas, e.data.imageBlob, e.data.isPlayground, e.data.FragShaderOverride)
return
}
if (e.data.type === 'startRender') {
rendering = true
}
if (e.data.type === 'stopRender') {
rendering = false
}
if (e.data.type === 'resize') {
newWidth = e.data.newWidth
newHeight = e.data.newHeight
}
if (e.data.type === 'addBlocksSection') {
const currentLength = allSides.length;
// in: object - name, out: [x, y, z, name]
const newData = Object.entries(e.data.data.blocks).flatMap(([key, value]) => {
const [x, y, z] = key.split(',').map(Number)
const block = value as BlockType
return block.sides.map((side) => {
return [x, y, z, side] as [number, number, number, BlockFaceType]
})
})
// find freeIndexes if possible
const freeArea = freeArrayIndexes.find(([startIndex, endIndex]) => endIndex - startIndex >= newData.length)
if (freeArea) {
const [startIndex, endIndex] = freeArea
allSides.splice(startIndex, newData.length, ...newData)
lastNotUpdatedIndex ??= startIndex
const freeAreaIndex = freeArrayIndexes.indexOf(freeArea)
freeArrayIndexes[freeAreaIndex] = [startIndex + newData.length, endIndex]
if (freeArrayIndexes[freeAreaIndex][0] >= freeArrayIndexes[freeAreaIndex][1]) {
freeArrayIndexes.splice(freeAreaIndex, 1)
// todo merge
}
lastNotUpdatedArrSize = newData.length
console.log('using free area', freeArea)
}
chunksArrIndexes[e.data.key] = [currentLength, currentLength + newData.length]
allSides.push(...newData)
lastNotUpdatedIndex ??= currentLength
// updateCubes?.(currentLength)
}
if (e.data.type === 'addBlocksSectionDone') {
updateCubesWhenAvailable(lastNotUpdatedIndex)
lastNotUpdatedIndex = undefined
lastNotUpdatedArrSize = undefined
}
if (e.data.type === 'removeBlocksSection') {
// fill data with 0
const [startIndex, endIndex] = chunksArrIndexes[e.data.key]
for (let i = startIndex; i < endIndex; i++) {
allSides[i] = undefined
}
lastNotUpdatedArrSize = endIndex - startIndex
updateCubes(startIndex)
// freeArrayIndexes.push([startIndex, endIndex])
// // merge freeArrayIndexes TODO
// if (freeArrayIndexes.at(-1)[0] === freeArrayIndexes.at(-2)?.[1]) {
// const [startIndex, endIndex] = freeArrayIndexes.pop()!
// const [startIndex2, endIndex2] = freeArrayIndexes.pop()!
// freeArrayIndexes.push([startIndex2, endIndex])
// }
}
if (e.data.type === 'camera') {
camera.rotation.set(e.data.camera.rotation.x, e.data.camera.rotation.y, e.data.camera.rotation.z, 'ZYX')
// camera.position.set(e.data.camera.position.x, e.data.camera.position.y, e.data.camera.position.z)
if (camera.position.x === 0 && camera.position.y === 0 && camera.position.z === 0) {
// initial camera position
camera.position.set(e.data.camera.position.x, e.data.camera.position.y, e.data.camera.position.z)
} else {
new tweenJs.Tween(camera.position).to({ x: e.data.camera.position.x, y: e.data.camera.position.y, z: e.data.camera.position.z }, 50).start()
}
}
if (e.data.type === 'animationTick') {
if (e.data.frames <= 0) {
autoTickUpdate = undefined
animationTick = 0
return
}
if (e.data.tick === -1) {
autoTickUpdate = e.data.frames
} else {
autoTickUpdate = undefined
animationTick = e.data.tick % 20 // todo update automatically in worker
}
}
if (e.data.type === 'fullReset') {
fullReset()
}
if (e.data.type === 'exportData') {
const exported = exportData();
postMessage({ type: 'exportData', data: exported }, undefined, [exported.sides.buffer])
}
if (e.data.type === 'loadFixture') {
// allSides = e.data.json.map(([x, y, z, face, textureIndex]) => {
// return [x, y, z, { face, textureIndex }] as [number, number, number, BlockFaceType]
// })
const dataSize = e.data.json.length / 5
for (let i = 0; i < e.data.json.length; i += 5) {
allSides.push([e.data.json[i], e.data.json[i + 1], e.data.json[i + 2], { face: e.data.json[i + 3], textureIndex: e.data.json[i + 4] }])
}
updateCubesWhenAvailable(0)
}
}
globalThis.testDuplicates = () => {
const duplicates = allSides.filter((value, index, self) => self.indexOf(value) !== index)
console.log('duplicates', duplicates)
}
const exportData = () => {
// Calculate the total length of the final array
const totalLength = allSides.length * 5;
// Create a new Int16Array with the total length
const flatData = new Int16Array(totalLength);
// Fill the flatData array
for (let i = 0; i < allSides.length; i++) {
const [x, y, z, side] = allSides[i];
flatData.set([x, y, z, side.face, side.textureIndex], i * 5);
}
return { sides: flatData };
}
setInterval(() => {
if (autoTickUpdate) {
animationTick = (animationTick + 1) % autoTickUpdate;
}
}, 1000 / 20)

View file

@ -2,7 +2,7 @@
import * as THREE from 'three'
import { BlockFaceType, BlockType } from './shared'
import * as tweenJs from '@tweenjs/tween.js'
import { cubePositionOffset, cubeUVOffset, cubeVertexArray, cubeVertexCount, cubeVertexSize } from './cube'
import { cubePositionOffset, cubeUVOffset, cubeVertexArray, cubeVertexCount, cubeVertexSize } from './CubeDef'
//@ts-ignore
import VertShader from './Cube.vert.wgsl'
//@ts-ignore
@ -17,19 +17,10 @@ let chunksArrIndexes = {}
let freeArrayIndexes = [] as [number, number][]
let rendering = true
let sidePositions
let updateCubes: (startIndex: any, forceUpdate?) => void
let lastNotUpdatedIndex
let lastNotUpdatedArrSize
let animationTick = 0
const updateCubesWhenAvailable = (pos) => {
if (updateCubes) {
updateCubes(pos)
} else {
setTimeout(updateCubesWhenAvailable, 100)
}
}
const camera = new THREE.PerspectiveCamera(75, 1 / 1, 0.1, 1000)
globalThis.camera = camera
@ -47,6 +38,10 @@ const updateSize = (width, height) => {
class WebgpuRendererWorker {
NUMBER_OF_CUBES = 1000
ready = false
device: GPUDevice
renderPassDescriptor: GPURenderPassDescriptor
uniformBindGroup: GPUBindGroup
@ -57,6 +52,7 @@ class WebgpuRendererWorker {
verticesBuffer: GPUBuffer
InstancedModelBuffer: GPUBuffer
pipeline: GPURenderPipeline
InstancedTextureIndexBuffer: GPUBuffer
constructor(public canvas: HTMLCanvasElement, public imageBlob: ImageBitmapSource, public isPlayground: boolean, public FragShaderOverride?) {
this.init()
@ -88,6 +84,7 @@ class WebgpuRendererWorker {
alphaMode: 'premultiplied',
})
const verticesBuffer = device.createBuffer({
size: cubeVertexArray.byteLength,
usage: GPUBufferUsage.VERTEX,
@ -96,16 +93,22 @@ class WebgpuRendererWorker {
this.verticesBuffer = verticesBuffer
new Float32Array(verticesBuffer.getMappedRange()).set(cubeVertexArray)
verticesBuffer.unmap()
let ModelMatrix = new THREE.Matrix4()
const InstancedModelBuffer = device.createBuffer({
size: 4 * 4 * 4,
usage: GPUBufferUsage.VERTEX,
this.InstancedModelBuffer = device.createBuffer({
size: this.NUMBER_OF_CUBES * 4 * 3,
usage: GPUBufferUsage.VERTEX || GPUBufferUsage.MAP_WRITE,
mappedAtCreation: true,
})
this.InstancedModelBuffer = InstancedModelBuffer
new Float32Array(InstancedModelBuffer.getMappedRange()).set(ModelMatrix.elements)
InstancedModelBuffer.unmap()
this.InstancedTextureIndexBuffer = device.createBuffer({
size: this.NUMBER_OF_CUBES * 4 * 1,
usage: GPUBufferUsage.VERTEX || GPUBufferUsage.MAP_WRITE,
mappedAtCreation: true,
})
//device.StepM
const vertexCode = VertShader
const fragmentCode = FragShader
@ -124,7 +127,7 @@ class WebgpuRendererWorker {
// position
shaderLocation: 0,
offset: cubePositionOffset,
format: 'float32x4',
format: 'float32x3',
},
{
// uv
@ -135,17 +138,30 @@ class WebgpuRendererWorker {
],
},
{
arrayStride: 4 * 4 * 4,
arrayStride: 3 * 4,
attributes: [
{
// ModelMatrix
shaderLocation: 2,
offset: 0,
format: 'float32x4',
format: 'float32x3',
}
],
stepMode: 'instance',
},
{
arrayStride: 1 * 4,
attributes: [
{
// ModelMatrix
shaderLocation: 3,
offset: 0,
format: 'float32',
}
],
stepMode: 'instance',
}
],
},
fragment: {
@ -172,7 +188,7 @@ class WebgpuRendererWorker {
},
primitive: {
topology: 'triangle-list',
//cullMode: 'back',
cullMode: 'front',
},
depthStencil: {
@ -257,11 +273,39 @@ class WebgpuRendererWorker {
},
}
this.loop()
// always last!
rendering = false
this.loop()
this.ready = true
return canvas
}
updateSides (start) {
rendering = true
const positions = [] as number[]
let textureIndexes = [] as number[]
for (let i = 0; i < allSides.length / 6; i++) {
const side = allSides[i * 6]!
positions.push(...[side[0], side[1], side[2]])
textureIndexes.push(side[3].textureIndex)
}
//Todo: make this dynamic
const ModelMatrix = new Float32Array(positions)
new Float32Array(this.InstancedModelBuffer.getMappedRange()).set(ModelMatrix)
this.InstancedModelBuffer.unmap()
// same index with length = allSides.length / 6
new Float32Array(this.InstancedTextureIndexBuffer.getMappedRange()).set(new Float32Array(textureIndexes))
this.InstancedTextureIndexBuffer.unmap()
// this.NUMBER_OF_CUBES = positions.length
}
lastCall = performance.now()
logged = false
@ -276,31 +320,36 @@ class WebgpuRendererWorker {
const now = Date.now()
tweenJs.update()
const modelViewProjectionMat4 = new THREE.Matrix4()
const ViewProjectionMat4 = new THREE.Matrix4()
camera.updateMatrix()
const projectionMatrix = camera.projectionMatrix
modelViewProjectionMat4.multiplyMatrices(projectionMatrix, camera.matrix.invert())
const modelViewProjection = new Float32Array(modelViewProjectionMat4.elements)
// globalThis.modelViewProjection = modelViewProjection
ViewProjectionMat4.multiplyMatrices(projectionMatrix, camera.matrix.invert())
const ViewProjection = new Float32Array(ViewProjectionMat4.elements)
// globalThis.ViewProjection = ViewProjection
device.queue.writeBuffer(
uniformBuffer,
0,
modelViewProjection.buffer,
modelViewProjection.byteOffset,
modelViewProjection.byteLength
ViewProjection.buffer,
ViewProjection.byteOffset,
ViewProjection.byteLength
)
renderPassDescriptor.colorAttachments[0].view = ctx
.getCurrentTexture()
.createView()
const commandEncoder = device.createCommandEncoder()
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor)
const passEncoder = commandEncoder.beginRenderPass(this.renderPassDescriptor)
passEncoder.setPipeline(pipeline)
passEncoder.setBindGroup(0, uniformBindGroup)
passEncoder.setBindGroup(0, this.uniformBindGroup)
passEncoder.setVertexBuffer(0, verticesBuffer)
passEncoder.setVertexBuffer(1, this.InstancedModelBuffer)
passEncoder.draw(cubeVertexCount, 1)
passEncoder.setVertexBuffer(2, this.InstancedTextureIndexBuffer)
passEncoder.draw(cubeVertexCount, this.NUMBER_OF_CUBES)
passEncoder.end()
device.queue.submit([commandEncoder.finish()])
@ -316,6 +365,13 @@ let fullReset
let webglRendererWorker: WebgpuRendererWorker | undefined
const updateCubesWhenAvailable = (pos) => {
if (webglRendererWorker?.ready) {
webglRendererWorker.updateSides(pos)
} else {
setTimeout(updateCubesWhenAvailable, 100)
}
}
let started = false
let newWidth: number | undefined
@ -337,7 +393,7 @@ export const workerProxyType = createWorkerProxy({
newHeight = newHeight
updateSize(newWidth, newHeight)
},
addBlocksSection (data, key) {
addBlocksSection (data: { blocks: Record<string, BlockType> }, key: string) {
const currentLength = allSides.length
// in: object - name, out: [x, y, z, name]
const newData = Object.entries(data.blocks).flatMap(([key, value]) => {
@ -364,9 +420,9 @@ export const workerProxyType = createWorkerProxy({
}
chunksArrIndexes[key] = [currentLength, currentLength + newData.length]
allSides.push(...newData)
allSides.splice(currentLength, 0, ...newData)
lastNotUpdatedIndex ??= currentLength
// updateCubes?.(currentLength)
updateCubesWhenAvailable(currentLength)
},
addBlocksSectionDone () {
updateCubesWhenAvailable(lastNotUpdatedIndex)
@ -380,7 +436,7 @@ export const workerProxyType = createWorkerProxy({
allSides[i] = undefined
}
lastNotUpdatedArrSize = endIndex - startIndex
updateCubes(startIndex)
updateCubesWhenAvailable(startIndex)
// freeArrayIndexes.push([startIndex, endIndex])