fenomas / noa

Experimental voxel game engine.
MIT License
611 stars 87 forks source link

Textures with partial transparency #116

Closed Patbox closed 4 years ago

Patbox commented 4 years ago

Hello. I've tried to add blocks with textures with partial transparency and it looks like it doesn't render it properly. Blocks have 'opaque' set to false.

image

fenomas commented 4 years ago

Hi, did you set texHasAlpha when registering the block material?

Basically, texture transparency comes down to the settings on the Babylon material applied to the mesh - noa makes one for you by default, but you can also make your own and pass it in.

Note: the [opaque](https://github.com/andyhall/noa/blob/master/src/lib/registry.js#L99) flag for blocks (not block materials) is separate - that determines whether the block itself occludes block faces adjacent to it. E.g. a minecraft "tree leaves" block would need to be non-opaque, since you can see adjacent blocks through it, even though the texture doesn't have alpha.

Patbox commented 4 years ago

Yes, I've set texHasAlpha to true. The code for setting up blocks looks like this: https://github.com/Patbox/voxelsrv/blob/master/src/registry.js#L27

createBlock(_id++, color + '_stained_glass', 0, ['block/' + color + '_stained_glass' ] , {opaque: false}, {drop: color + '_stained_glass', hardness: 1, tool: 'pickaxe', material: 'glass'})

fenomas commented 4 years ago

Okay, it's a few years since I touched these bits of code but I think I see what's going on now.

Noa's texHasAlpha flag maps to Babylon's hasAlpha property on textures, which only enables the texture to have fully on/off pixels (like minecraft tree leaf textures). To get partial transparency one has to also define an opacityTexture.

To see why the engine doesn't support this, you can try it out by doing it on a custom block texture:

    var tmat = noa.rendering.makeStandardMaterial('')
    tmat.diffuseTexture = new Texture('textures/window.png', scene)
    tmat.opacityTexture = tmat.diffuseTexture
    noa.registry.registerMaterial('window', null, null, false, tmat)

    blockIDs.windowID = noa.registry.registerBlock(_id++, {
        material: 'window',
        opaque: false,
    })
Screen Shot 2020-06-22 at 16 05 30

As you can see the composite order gets messed up, (I assume) because noa merges terrain into big per-chunk meshes, and then Babylon treats that whole chunk as a single thing with respect to drawing transparent textures in the right order.

So, long story short, I think the right way to do what you're looking for is to treat each block with partial transparency as a custom block and make your own mesh for it, with whatever settings you need (opacity, backfaceCulling, etc). And generally assume that all terrain (i.e. blocks that don't have custom meshes) textures cannot be partially transparent.

I should add that I'm basically a beginner at this stuff, so do let me know if you see a better approach.

fenomas commented 4 years ago

By the way: your project looks really nice! Would you like it added to the list of projects at the top of noa's readme?

Patbox commented 4 years ago

Okay so I will do it with custom blocks.

And thanks! Sure, you can add it.

Patbox commented 4 years ago

image The final result of using custom meshes. Here's code I use:

var mat = noa.rendering.makeStandardMaterial(name)

var tex = new BABYLON.Texture('textures/' + texture[0] + '.png', scene, true, true,
    BABYLON.Texture.NEAREST_SAMPLINGMODE)

mat.diffuseTexture = tex
mat.opacityTexture = mat.diffuseTexture
mat.backFaceCulling = true

var mesh = BABYLON.MeshBuilder.CreateBox(name, {size: 1}, noa.rendering.getScene())
mesh.material = mat
mesh.bakeTransformIntoVertices( ( new BABYLON.Matrix.Scaling(1, 1, 1) ).setTranslation ( new BABYLON.Vector3(0, 0.5, 0) ) )
mesh.material.needDepthPrePass = true

var finOpts = options
finOpts.blockMesh = mesh
noa.registry.registerBlock(id, finOpts)
fenomas commented 4 years ago

LGTM 👍

I'm afraid it will get pretty hairy if you want to do things like remove unneeded block faces between two such custom blocks, or between a stained glass block and adjacent solid blocks. But I don't think there's any good solution for that, besides managing it all manually.

PS: congratulations on having a web-based minecraft client that's better than Mojang's 😄

fenomas commented 4 years ago

Guessing this is okay to close, reopen if you have questions.