#You need the tolerance for visibility

1 messages · Page 1 of 1 (latest)

white mist
#

correct, which is even worse considering this. it tests tolerance around the center, so for a regular token it tests 25 points around the center, which is less optimal than testing it's bounds in a polygon anyway

#

@dull moth if you want to continue discussing here

pseudo valley
#

25 points? That would be a bug. It should be 9 (or 1 if the tolerance is 0)

white mist
#
 get isVisible() {
    // Only GM users can see hidden tokens
    const gm = game.user.isGM;
    if ( this.document.hidden && !gm ) return false;

    // Some tokens are always visible
    if ( !canvas.effects.visibility.tokenVision ) return true;
    if ( this.controlled ) return true;

    // Otherwise, test visibility against current sight polygons
    if ( canvas.effects.visionSources.has(this.sourceId) ) return true;
    const tolerance = Math.min(this.w, this.h) / 4;
    return canvas.effects.visibility.testVisibility(this.center, {tolerance, object: this});
  }
#

h and w are 100

#

so it's 25

pseudo valley
#

Ah, I thought you meant that it's 25 points that are tested. Yes the tolerance is 25 pixels (assuming 100px grid spaces and 1x1 token). But you need a large tolerance, otherwise you wouldn't be able to so tokens that are partially obscured by a wall

white mist
#

it is 25 points tested

dull moth
#

Yeah, the tolerance of 25 pixels is used to construct a grid of 9 points to test. The original center point, 4 corners of a square, and 4 midpoints of a square.

pseudo valley
#
    // Prepare an array of test points depending on the requested tolerance
    const t = tolerance;
    const offsets = t > 0 ? [[0, 0], [-t, -t], [-t, t], [t, t], [t, -t], [-t, 0], [t, 0], [0, -t], [0, t]] : [[0, 0]];
    const config = {
      object,
      tests: offsets.map(o => ({
        point: new PIXI.Point(point.x + o[0], point.y + o[1]),
        los: new Map()
      }))
    };
white mist
#

ahh right right sorry

dull moth
#

A containment test won't really work for visibility testing because you are testing whether a point on the scene is visible from some other vantage point. I have a whole module devoted to subverting this visibility test in various ways, but the point test remains the fastest in most cases. https://github.com/caewok/fvtt-token-visibility

#

The typical visibility test, then, is going to shoot a ray from the vantage point to the token point. But you could use containment to test if various point of the token are contained within the vision polygon, if you have a vision polygon.

white mist
#

btw, i think i have an issue with my algorithm

pseudo valley
# white mist btw, i think i have an issue with my algorithm

If you're looking for an better containment test, I dug this old thing up I wrote some time ago. It returns 1 if the point is in the interior, 0 if on the boundary, and -1 if outside. It's exact if all coordinates are integers in the range [-2^-24, +2^24]. The important part is that (x - x1) * (y2 - y1) - (x2 - x1) * (y - y1) must be exact. So it can also be floats but you need to limit the number of binary places (any power of 2 scale of the integers [-2^-24, +2^24] is fine).

function contains(points, x, y) {
    let c = 0;
    let x0 = points[points.length - 2];
    let y0 = points[points.length - 1];

    for (let i = 0; i < points.length; i += 2) {
        let x1 = x0;
        let y1 = y0;
        let x2 = x0 = points[i];
        let y2 = y0 = points[i + 1];

        if (y1 > y2 || y1 === y2 && x1 > x2) {
            [x1, y1, x2, y2] = [x2, y2, x1, y1];
        }

        if (y < y1 || y > y2 || x < x1 && x < x2) {
            continue;
        }

        if (y !== y2) {
            const d = (x - x1) * (y2 - y1) - (x2 - x1) * (y - y1);

            if (d === 0) {
                return 0;
            }

            if (d > 0) {
                c++;
            }
        } else if (x === x2 || y1 === y2 && x <= x2) {
            return 0;
        }
    }

    return c & 1 ? 1 : -1;
}
white mist
#

thanks, i'll test it too

#

so i'm considering just pulling pixels colors from the polygon texture, not sure if it's faster though

pseudo valley
white mist
#

so correct me if i'm wrong, but this texture gets generated either way

#

and i'm just sampling certain pixels from that, not getting the whole pixel buffer

pseudo valley
white mist
#

maybe i wasn't clear, i'm using the vision.los to test if stuff should be visible or not

#

so i think that texture is getting created either way

pseudo valley
white mist
#

and we should have the fog texture or mask for rendering right?

pseudo valley
white mist
#

i wonder at what point it would get really slow, but i assume 1000 pixels should be in the clear

#

or maybe not

#

i don't really know actually

#

(so actually, you can totally implement it with shaders, and not test for visibility for tokens)

pseudo valley
#

Reading the pixels from the texture plays a part in why in V10 and before the fog saving freezes the canvas noticably. V11 going to fix that though by reading async and base64 in a web worker

white mist
#

(i do that in another shader for some other tests)

#

is there really a reason to send the whole base64 fog, instead of the vertices?

#

and extracting it for that matter?

#

i know we had that in older versions, but why now?

pseudo valley
#

Computing the explored fog as polygon is much more expensive

white mist
#

you have to do that every time you move or select a token right?

pseudo valley
pseudo valley
#

Clipper operations aren't cheap.

white mist
#

i'm probably mixing things again, but when you move a token, the vision (los) polygon gets computed each time, and rendered anyway.

pseudo valley
white mist
#

well, you are basically saving pixel data (deflated {i think it was in png} but in base64) instead of vertices, it's larger by definition no? and don't forget you need to grab it from the rendered gpu texture, you get the whole pixel buffer, and then convert it into an image, and then base64 it.

pseudo valley
# white mist well, you are basically saving pixel data (deflated {i think it was in png} but ...

In a scene with 100 light with each 100 points that's 100 * 100 * 2 * 8 bytes = 160 kB of polygon data. And that's added each frame the token is moved. Of course you can be cleverer about it because the polygons of lights rarely change. But potentially you can exceed the space you need to store the image. And the scene load times increase with each addition, because all these polygons need to be triangulated and rendered (essentially the entire history of the fog exploration would need to be gone through on load)

white mist
#

why 8 bytes? (and thank you btw for indulging me with this)

pseudo valley
#

assuming 64bit floats

white mist
#

foundry uses integers for all position though

pseudo valley
#

Polygon points aren't all integer. The intersection points of the FOV circle and the LOS polygon are fractional. But of course you would round them before saving

white mist
#

and what do we do with the other 2 bytes?

pseudo valley
#

2 coordinates per point

white mist
#

right sorry

#

it's just an svg at this point

#

but yeah, you'll need to do some quantisation for the points at some point

#

it's an interesting problem tbh

pseudo valley
# white mist it's just an svg at this point

That's no really true unless we compute all the unions and intersections. Otherwise it's an array of deltas, where each delta contains all the FOV and LOS polygon of this frame. Then you can reconstruct the fog the applying each delta

pseudo valley
# white mist it's an interesting problem tbh

Polygon fog is probably not impossible but it would require a lot of work and testing. And in the end it might just perform a lot worse than pixels based fog. That's why advised the Foundry devs to not waste their time with this and just switch to use async readPixels and base64 conversion in a worker. That brings the time spent in the main thread for the save from ~250ms down to <3ms

white mist
#

async webgl really changes things

#

do you have access to the canvas from a worker

#

or is it just offscreencanvas?

pseudo valley
#

the worker is isolated, you have to send/transfer the data back and forth

white mist
#

you can have 2 canvas on the main thread, and the worker will be able to access the offscreencanvas

#

would that reduce stuff that needs to be sent back and forth?

pseudo valley
#

the offscreencanvas can be either in the main thread or in a worker, but not in two places at once

#

you can transfer it though, meaning sending it without cloning to the worker

white mist
#

yea