ok let me help you with the algorithm parts.
first you need to divide your script into two. the first script should create the matrix and STORE it in a dictionary (creating variables takes time, you should not do it every frame). the sencond one, “update” script has to run every frame, so it has to be simpler. it has to load the dictionary and read/update it, as well as creating the geometry.
i got experience from making a 2d map using bitmask, checking all the neightbour points to set a single “block”, the same way as a gpu shader, one by one.
the way i did it was by having parallel matrixes, one matrix has the data on what the voxel is made of: say one point has dirt, that would be
, air would be
so it would be immediately ignored, etc. with blocks you don’t need borders, but you can benefit from this technology by detecting the border, if a block is surrounded by blocks everywhere, it is ignored, so in one of the parallel matrixes that same position is
, representing an ABSOLUTELY hidden block, while the borders would always be
, and air would also be
then, with that parallel matrix, you go to the raytracing step, you skip all the blocks that are air (
), and create a new parallel matrix, with the VISIBLE blocks from the POSITION of the player in the voxel, not the frame. that should make it faster. so you only update when the player moves.
then you go to the “render” step using the raytrace matrix (or you can use the “border” matrix, since it will be already few enough blocks), you look at the parallel, if it is
, you look at the first voxel (what it’s made of) and use it to create a block. if it’s
, you skip.
after that, you create the batch. with big enough blocks (minecraft) you should get good framerate, with a not very powerful computer (i used to get good python speed in an athlon 64 x2, any modern computer is much more powerful than that).
thinking again, you should do the border detection in the same script where you define variables, so that the render and raytrace becomes faster. a third script can be used to recalculate “material voxel” and “border” voxel, when a block is changed or a new chunk is loaded. you should make big chunks to get better performance. use the 16x16 for testing, but think to increase as the script gets more complex.