fenomas / noa

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

able to add shadow to a specific chunk but not a particular face??? #169

Closed levlups closed 1 year ago

levlups commented 2 years ago

in terrainMesher.js i was able to shade a particular chunk at will using the requestedID of chunk but if i use for example for ( var i=0;i<len0 ; i++) if ( i<10 ) shade... it doesnt work ,it breaks down its like the chunk meshes or AOs or... wath else merges together , is their a way to keep those faces separated so we can shade certain faces and not the whole chunk.???

fenomas commented 2 years ago

I'm lost, can you describe specifically what you want to do?

For reference, terrain meshes are merged together by chunk and by material - i.e. all voxel faces in a given chunk that share the same material will be part of the same mesh. So if you want to do something to an individual voxel face, the straightforward way would be to make a separate block material for that face, so it gets its own material. But I may be misunderstanding you.

levlups commented 2 years ago

you understood right , to make realistic shadow like minecraft i need a material per face ... ??? how would I do that ....

levlups commented 2 years ago

heres an example of code I was using id is the requestedID of the chunk , but its not precise

function pushAOColor(colors, ix, baseCol, ao, aoVals, revAoVal, id) {
    var mult = (ao === 0) ? revAoVal : aoVals[ao - 1]

    if (parseInt(id[1]) >= 0) {

        mult = (ao === 0) ? revAoVal : aoVals[ao - 1]

    } else {

        mult = (ao === 0) ? revAoVal : aoVals[ao - 1]
        mult -= 0.2
    }

    colors[ix] = baseCol[0] * mult
    colors[ix + 1] = baseCol[1] * mult
    colors[ix + 2] = baseCol[2] * mult
    colors[ix + 3] = 1
}
levlups commented 2 years ago

fenomas if your still lost , tell me Ill make you a video to explain :)

fenomas commented 2 years ago

Hmm, I'm still a bit lost but I guess you're tweaking the AO vertex colors based on the world position of the voxel face?

If that's right, then the requestedID of each chunk is just an identifier, so the engine and the client know if they're talking about the same chunk or not. If you want a kind of "is this voxel above or below y=0" test, then I guess you'd want to tweak terrain meshing so that the mesher knows each chunk's world x/y/z position, and then it can track the world position of each voxel as it meshes.

Is that what you're trying to do? If so, then look at the greedyMesher.mesh call inside terrain mesher. If that call passed in chunk.x, chunk.y, chunk.z along with its other params, then the mesher would know the world position of the chunk it's meshing, and it could pass internal xyz coords down to the AO logic.

ghost commented 2 years ago

Yes I did that already , but it's not enough , it needs to be more precise I'll make a video of exactly wath i need .Stay tuned lol.

levlups commented 2 years ago

heres the minecraft classic terrainmesher code if your attentive youll see a noa.getbrightness() function

function o() {
    var e = i(188),
        t = e.ID_MASK,
        r = (e.VAR_MASK, e.SOLID_BIT),
        o = e.OPAQUE_BIT,
        s = e.OBJECT_BIT,
        c = new Int16Array(256),
        u = new Uint16Array(256);

    function l(e, i, n, r, o, a) {
        for (var s = n.shape[1], l = c, h = u, d = 0, p = n.data, m = n.index(e - 1, 0, 0), g = n.stride[0], _ = n.stride[1], v = n.stride[2], y = 0; y < s; ++y) {
            var b = m;
            m += v;
            for (var E = 0; E < s; E++, d++, b += _) {
                var T = p[b],
                    A = p[b + g],
                    x = f(T, A, E, y);
                if (x) {
                    if (l[d] = x > 0 ? r(T & t, 2 * i) : -r(A & t, 2 * i + 1), 0 != l[d]) {
                        var S = a[0] + e + x,
                            C = a[1] + E,
                            P = a[2] + y;
                        1 == i && (S = a[0] + y, C = a[1] + e + x, P = a[2] + E), 2 == i && (S = a[0] + E, C = a[1] + y, P = a[2] + e + x), 0 == i && x > 0 && (S -= 1), 1 == i && x > 0 && (C -= 1), 2 == i && x > 0 && (P -= 1), noa.getBrightness(S, C, P) < 1 && (l[d] += 512 * x)
                    }
                    if (o) {
                        var R = x > 0 ? e : e - 1,
                            M = x > 0 ? e - 1 : e;
                        h[d] = o(n, R, M, E, y)
                    }
                }
            }
        }
    }
    this.mesh = function(e, t, i, n, r, o, s) {
        var h, f = [],
            p = n && o === r[0];
        n && (h = p ? v : y);
        for (var m = 0; m < 3; ++m) {
            var g = (m + 1) % 3,
                _ = (m + 2) % 3,
                b = e.transpose(m, g, _).lo(1, 1, 1).hi(e.shape[m] - 2, e.shape[g] - 2, e.shape[_] - 2),
                E = b.shape[0] - 1,
                T = b.shape[1],
                A = b.shape[2];
            c.length < T * A && (c = new Int16Array(T * A), u = new Uint16Array(T * A));
            for (var x = 0; x <= E; ++x) l(x, m, b, t, h, s), a("built masks"), d(x, m, g, _, T, A, n, f, i, r, o, s), a("build submeshes")
        }
        return f
    };
levlups commented 2 years ago

heres wath happen when you remove a block in minecraft classic

}), this.getBrightness = function(e, t, i) {
                        return e < 0 || t < 0 || i < 0 || e >= _ - 1 || i >= _ - 1 ? t >= v ? 1 : .6 : t >= T.lightDepths[e][i] + 1 ? 1 : .6
                    }, k.getBrightness = this.getBrightness, this.addBlockCheckLit = function(e, t, i) {
                        var n = this.lightDepths[e][i];
                        if (t >= n) {
                            if (Math.floor(t / g) != Math.floor(n / g)) {
                                var r = k.world.getBlockID(e, n, i);
                                k.world.setBlockID(0, e, n, i), k.world.setBlockID(r, e, n, i)
                            }
                            this.lightDepths[e][i] = t
                        }
                    }
levlups commented 2 years ago

heres the youtube video explanation

https://www.youtube.com/watch?v=62YCEIXB_NA

fenomas commented 2 years ago

Hi, thanks for the video (and the kind words!). I understand the issue now.

First to explain, the core function of the terrain mesher is to merge together related terrain faces, to reduce the number of vertices in the overall world. You can see this in action if you compare what pieces of terrain look like in wireframe mode:

Screen Shot 2021-10-25 at 14 08 39

Normally this merging step happens after voxel materials and AO values (which are turned into vertex colors) have already been processed, so the terrain mesher only merges together faces where the corners all have the same values, so the merging isn't visible. But in your demo you're randomizing colors after the merging step, so that's why you see unpredictable changes.

Basically the point of all this is that for the engine to support lighting (in its current architecture, at least) lighting would need to be handled earlier in the meshing process - before or together with AO, so that later on the mesher only merges together voxels that have the same light level.

As for lighting itself, it's a big topic that basically hasn't been tackled yet - I think this has been discussed in other issues, but maybe I should make one big tracking issue for it. The simplest approach, which you could do immediately without waiting for any engine changes, would be to define two versions of your terrain blocks, a lighted version and an unlighted version. Then you'd just make the underground terrain out of unlit_dirt voxels, and surface blocks out of lit_dirt, and it would be game logic's job to know which one is correct in a given spot. The problem with this is that in real terrain you'd want some dirt blocks to be lit on one side and unlit on the other side, and it would be tedious to define lots of different variations.

For "real" engine-supported lighting, the short answer is that there are several possible approaches and I haven't currently started any of them yet.

I hope this clears stuff up!