fenomas / noa

Experimental voxel game engine.
MIT License
608 stars 86 forks source link

Meshing with non-opaque blocks is incredibly slow + block textures with alpha dont work #184

Closed MCArth closed 1 year ago

MCArth commented 1 year ago

Hi, I've been doing more testing and found an incredibly slow case meshing case with non-opaque blocks

image

To make the stress a bit more stressy, I modified the worldgen:

noa.registry.registerMaterial('grass', { textureURL: atlasURL, atlasIndex: 0 })
noa.registry.registerMaterial('g_dirt', { textureURL: atlasURL, atlasIndex: 1 })
noa.registry.registerMaterial('dirt', { textureURL: atlasURL, atlasIndex: 2 })
noa.registry.registerMaterial('stone', { textureURL: atlasURL, atlasIndex: 3 })
noa.registry.registerMaterial('stone2', { textureURL: atlasURL, atlasIndex: 4 })
noa.registry.registerMaterial('cloud', { textureURL: atlasURL, atlasIndex: 5 })
// noa.registry.registerMaterial('seethrough', { textureURL: atlasURL, atlasIndex: 6, texHasAlpha: true })

var id = 1
var dirt = noa.registry.registerBlock(id++, { material: 'dirt' })
var grass = noa.registry.registerBlock(id++, { material: 'grass' })
var stone = noa.registry.registerBlock(id++, { material: 'stone' })
var stone2 = noa.registry.registerBlock(id++, { material: 'stone2' })
var cloud = noa.registry.registerBlock(id++, { material: 'cloud', opaque: false })
// var seethrough = noa.registry.registerBlock(id++, { material: 'seethrough', opaque: false })

// worldgen
var decideVoxel = (x, y, z, ht, clo, chi, pillar) => {
    const id = 1+Math.floor(Math.random()*5)
    if (y < ht) {
        return id
    }
    if (y < ht + pillar) return id
    if (y > clo && y < chi) return id
    return 0
}
noa.world.on('worldDataNeeded', (requestID, data, cx, cy, cz) => {
    for (var i = 0; i < data.shape[0]; i++) {
        var x = cx + i
        for (var k = 0; k < data.shape[2]; k++) {
            var z = cz + k
            var ht = Math.sqrt(x * x + z * z) / 100
            var a = noise(x, 150)
            var b = noise(z + 50, 140)
            var c = noise(x - z - 50, 120)
            ht += 2 * a + b + c
            var pillar = (Math.random() < 0.002) ? 5 : 0
            var clo = 39 + 2 * (b - c)
            var chi = 35 + 2 * (a - b)
            for (var j = 0; j < data.shape[1]; j++) {
                var y = cy + j
                var id = decideVoxel(x, y, z, ht, clo, chi, pillar)
                data.set(i, j, k, id)
            }
        }
    }
    // tell noa the chunk's terrain data is now set
    noa.world.setChunkData(requestID, data)
})

It seems likely the meshing functions are being de-opted somehow (if you remove the cloud opaque: false, this doesn't happen)

R.e. the alpha block textures not working, replace terrain_atlas with the below and uncomment the seethrough material above, including it in the worldgen - you'll see the middle of the block should be transparent but is black

terrain_atlas

image

R.e. #183 I'll see if I can get a repro in examples

fenomas commented 1 year ago

Hi, thanks a ton for checking this!

The transparency thing is something I forgot about, I've pushed a fix that ought to resolve it. I'll have a look at the performance issue now.

By the way, if you find multiple bugs do feel free to submit multiple separate issues, so they can track independently etc. Thanks as always!

MCArth commented 1 year ago

Out of interest, what's the surface area usage of noa in your game - do you use most of it? are there any notable parts you don't use? (e.g. alpha-tested/blended blocks like here or perhaps some other features)

If there are I can be more careful pulling in changes that might affect them in the future

fenomas commented 1 year ago

About the perf issue, I think all that's happening is the amount of meshing going on. You're returning non-opaque voxels (the cloud id) all throughout the terrain, so the engine is meshing voxel faces around them even if the non-opaque voxel is deep underground. (noa doesn't currently do any kind of line-of-sight checks when meshing)

I think what you probably want to do is assign random voxel IDs only at the surface level, and but return only solid voxels below that.

About surface area usage: I'm using almost everything, but transparent terrain blocks are one exception. Getting them to compose correctly even after chunking is a general problem I haven't had time to look closely at...

MCArth commented 1 year ago

That makes sense, thanks for checking