I am working on a voxel geometry in threejs and everything is right for loading the geometry once or removing faces but when adding new faces the geometry gets empty with [.WebGL-00006C2000754000] GL_INVALID_OPERATION: Vertex buffer is not big enough for the draw call
warning. IDK why but the addface works after removing one face before it.
I use threejs r162
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
//import * as BufferGeometryUtils from "three/addons/utils/BufferGeometryUtils.js";
const palette = [
[0x1d,0x18,0x26],
[0x8b,0x7f,0xb0],
[0xc3,0xbe,0xe5],
[0xff,0xe8,0xe9],
[0x65,0x26,0x4e],
[0xa0,0x1a,0x3d],
[0xde,0x1b,0x45],
[0xf2,0x63,0x7b],
[0x8b,0x3f,0x39],
[0xbb,0x45,0x31],
[0xef,0x5d,0x0e],
[0xff,0x95,0x00],
[0x00,0xa0,0x3d],
[0x12,0xd5,0x00],
[0xb4,0xd8,0x00],
[0xff,0xc3,0x1f],
[0x00,0x6e,0x69],
[0x00,0xae,0x85],
[0x00,0xda,0xa7],
[0x4f,0xd6,0xff],
[0x2b,0x27,0x54],
[0x3c,0x51,0xaf],
[0x18,0x88,0xde],
[0x00,0xa9,0xe1],
[0x59,0x3c,0x97],
[0x89,0x44,0xcf],
[0xb4,0x4a,0xff],
[0xe9,0x59,0xff],
[0xe7,0x87,0x6d],
[0xff,0xba,0x8c],
[0xff,0xef,0x5c],
[0xff,0x9c,0xde]
];
const palette_01 = new Array(32).fill(null).map((_,i)=>palette[i].map(c=>c/256));
class VoxelGeometry {
constructor(sizex, sizey, sizez, palette) {
this.sizex = sizex;
this.sizey = sizey;
this.sizez = sizez;
this.palette = palette;
this.faces = [];
this.geometry = new THREE.BufferGeometry();
this.material = new THREE.MeshLambertMaterial({ vertexColors: true });
this.mesh = new THREE.Mesh(this.geometry, this.material);
this.mesh.position.set(-sizex / 2, -sizey / 2, -sizez / 2);
}
addFace(x, y, z, side, color) {
const face = {x,y,z,side,color};
this.faces.push(face);
}
removeFace(x, y, z, side) {
const faceIndex = this.faces.findIndex(f => f.x === x && f.y === y && f.z === z && f.side === side);
if (faceIndex !== -1) {
this.faces.splice(faceIndex, 1);
}
}
getFaceVertices(x, y, z, side) {
switch (side) {
case "left": return [[x, y + 1, z + 1], [x, y + 1, z], [x, y, z], [x, y, z + 1]];
case "right": return [[x + 1, y + 1, z + 1], [x + 1, y, z + 1], [x + 1, y, z], [x + 1, y + 1, z]];
case "bottom": return [[x, y, z], [x + 1, y, z], [x + 1, y, z + 1], [x, y, z + 1]];
case "top": return [[x, y + 1, z], [x, y + 1, z + 1], [x + 1, y + 1, z + 1], [x + 1, y + 1, z]];
case "back": return [[x, y, z], [x, y + 1, z], [x + 1, y + 1, z], [x + 1, y, z]];
case "front": return [[x, y, z + 1], [x + 1, y, z + 1], [x + 1, y + 1, z + 1], [x, y + 1, z + 1]];
}
}
updateGeometry() {
const positions = [];
const colors = [];
const indices = [];
this.faces.forEach((face, index) => {
const vertexIndex = index * 4;
const vertices = this.getFaceVertices(face.x, face.y, face.z, face.side);
const [v0,v1,v2,v3] = vertices;
positions.push(v0[0],v0[1],v0[2],v1[0],v1[1],v1[2],v2[0],v2[1],v2[2],v3[0],v3[1],v3[2]);
const color = this.palette[face.color&31];
colors.push(color[0],color[1],color[2],color[0],color[1],color[2],color[0],color[1],color[2],color[0],color[1],color[2]);
indices.push(
vertexIndex, vertexIndex + 1, vertexIndex + 2,
vertexIndex, vertexIndex + 2, vertexIndex + 3
);
});
this.geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
this.geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
this.geometry.setIndex(indices);
this.geometry.computeVertexNormals();
this.geometry.attributes.position.needsUpdate = true;
this.geometry.attributes.color.needsUpdate = true;
this.geometry.index.needsUpdate = true;
}
createVoxelMesh(voxels) {
for (let x = 0; x < this.sizex; x++) {
for (let y = 0; y < this.sizey; y++) {
for (let z = 0; z < this.sizez; z++) {
const voxel = voxels[x + y * this.sizex + z * this.sizex * this.sizey];
if (voxel != -1) {
if (voxels[(x - 1) + y * this.sizex + z * this.sizex * this.sizey] == -1 || x == 0) {
this.addFace(x, y, z,'left',voxel);
}
if (voxels[(x + 1) + y * this.sizex + z * this.sizex * this.sizey] == -1 || x == this.sizex - 1) {
this.addFace(x, y, z,'right',voxel);
}
if (voxels[x + (y - 1) * this.sizex + z * this.sizex * this.sizey] == -1 || y == 0) {
this.addFace(x, y, z,'bottom',voxel);
}
if (voxels[x + (y + 1) * this.sizex + z * this.sizex * this.sizey] == -1 || y == this.sizey - 1) {
this.addFace(x, y, z,'top',voxel);
}
if (voxels[x + y * this.sizex + (z - 1) * this.sizex * this.sizey] == -1 || z == 0) {
this.addFace(x, y, z, 'back',voxel);
}
if (voxels[x + y * this.sizex + (z + 1) * this.sizex * this.sizey] == -1 || z == this.sizez - 1) {
this.addFace(x, y, z, 'front',voxel);
}
}
}
}
}
this.updateGeometry();
return this.mesh;
}
}
const floor = Math.floor;
const round = Math.round;
const abs = Math.abs;
const min = Math.min;
const max = Math.max;
const voxel_map = [];
const size = 8;
const c = size/2-0.5;
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
for (let z = 0; z < size; z++) {
let d = max(max(abs(x-c),abs(y-c)),abs(z-c));
let dm = min(min(abs(x-c),abs(y-c)),abs(z-c));
if ((x^y^z)==0) {
voxel_map[x+y*size+z*size**2] = d&31;
} else {
voxel_map[x+y*size+z*size**2] = -1;
}
}
}
}
const context = canvas.getContext("webgl",{antialias:false});
const renderer = new THREE.WebGLRenderer({canvas,context});
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio,2));
renderer.outputColorSpace = THREE.LinearSRGBColorSpace;
window.addEventListener("resize",() => {
camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth,window.innerHeight);
});
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.01,2048);
camera.position.z = size*2;
const controls = new OrbitControls(camera, renderer.domElement);
//--light--//
const light = new THREE.DirectionalLight(0xffffff,Math.PI);
light.position.set(0.75*size/2,size/2,0.5*size/2);
scene.add(light);
const light2 = new THREE.DirectionalLight(0xffffff,Math.PI);
light2.position.set(-0.75*size/2,-size/2,-0.5*size/2);
scene.add(light2);
const hemiLight = new THREE.HemisphereLight(0xffffff,0xffffff,Math.PI*0.35);
hemiLight.position.set(0,1,0);
scene.add(hemiLight);
//--voxels--//
const g = new VoxelGeometry(size,size,size,palette_01);
window.onclick = () => {
g.addFace(0,0,0,"front",6);
g.updateGeometry();
}
const mesh = g.createVoxelMesh(voxel_map);
scene.add(mesh);
//--cube--//
const geometry = new THREE.BoxGeometry(size,size,size);
const material = new THREE.MeshLambertMaterial({color:0xfffffff,side:THREE.BackSide});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
//--update--//
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();