#You need the tolerance for visibility
1 messages · Page 1 of 1 (latest)
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
25 points? That would be a bug. It should be 9 (or 1 if the tolerance is 0)
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
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
it is 25 points tested
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.
// 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()
}))
};
ahh right right sorry
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.
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;
}
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
Something like pixels[width * round(y) + round(x)] is definitely faster to check containment for polygons with a large number of points, but the time to generate this pixel array isn't nothing. Also there's a larger memory overhead to store this array of course.
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
You create the texture from the pixels data (array)?
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
There's the screen-sized visibility texture, but nothing canvas sized (which you need for visibility testing). Even if there were one, the texture is only in GPU memory. You'd need to read the pixels from the texture, which is very expensive, especially if you do it synchronously it can easily block the main thread for 50ms.
interesting. i implemented a pixel perfect selector that does just that, albeit for only 1 pixel, didn't experience the 50ms delay, but i assume it can happen
and we should have the fog texture or mask for rendering right?
It depends on the amount of pixels that are extracted
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)
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
(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?
Computing the explored fog as polygon is much more expensive
you have to do that every time you move or select a token right?
There was never anything like that in core that I know of
In Perfect Vision in V9 I experimented with that. All the intersection and union of polygons needed to be computed in a web worker and the worker wasn't able to keep up with the jobs it needed to do (in scenes with lots of lights of course).
Clipper operations aren't cheap.
i'm probably mixing things again, but when you move a token, the vision (los) polygon gets computed each time, and rendered anyway.
That's true, but we can't store just store all the LOS/FOV polygons as it. It would just be a list of polygons that grows and grows: that could be a real problem.
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.
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)
why 8 bytes? (and thank you btw for indulging me with this)
assuming 64bit floats
foundry uses integers for all position though
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
and what do we do with the other 2 bytes?
2 coordinates per point
you'd save them as radius no?
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
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
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
async webgl really changes things
do you have access to the canvas from a worker
or is it just offscreencanvas?
the worker is isolated, you have to send/transfer the data back and forth
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?
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
yea