Skip to content

Commit

Permalink
Merge branch 'release/0.4.9' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
caewok committed Nov 4, 2024
2 parents ae7787a + 2803b68 commit 2424040
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 67 deletions.
9 changes: 9 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 0.4.9
Correct issue preventing editing of multiple wall heights when Wall Height module is active.
Prevent elevation from dropping to a large negative value when moving into and out of a region hole in one movement. Addresses #61.
Improvements to how region steps are measured to avoid having the token elevation lose sync with the steps during certain movement paths.
Change the overlay legend for region steps to vertical bars that slightly increase in width as the steps move up. Addresses #59.
When a token teleports into a plateau or ramp, set the token elevation to the plateau/ramp height at that location. Will only cause the token to change elevation if the region is active at the starting elevation of the token. Closes #63.

Update to libGeomtry v0.3.17.

## 0.4.8
Address issues with moving through plateaus with holes. Fix handling of scene floor for regions with holes so that tokens do not "fall through" to large negative elevations and then back. Addresses #64.

Expand Down
30 changes: 21 additions & 9 deletions scripts/ElevationHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,10 @@ export class ElevationHandler {
// Determine every intersection point with the cutaways.
// Intersection here just means the left and right bounds of the cutaway if the cutaway
// is below or at the scene floor.

const end2d = CONFIG.GeometryLib.utils.cutaway.to2d(end, start, end);
const ixs = [];
let inside = 0;
const end2d = CONFIG.GeometryLib.utils.cutaway.to2d(end, start, end);
for ( const cutaway of cutaways ) {
const bounds = cutaway.getBounds();
if ( bounds.top > sceneFloor ) continue; // Y is reversed, so this is bottom > sceneFloor.
Expand All @@ -516,18 +517,17 @@ export class ElevationHandler {
ixs.sort((a, b) => a.x - b.x);
let prevX = 0;
for ( const ix of ixs ) {
if ( ix.movingIn ) {
// Moving into a polygon.
if ( ix.isHole ^ ix.movingIn ) {
// Moving into a normal polygon or moving out of a hole; implied move into a normal polygon..
if ( inside <= 0 ) {
// x + 1 so the polygons will combine if touching.
const pts = [prevX, sceneFloor, prevX, MIN_ELEV, ix.x, MIN_ELEV, ix.x, sceneFloor];
sceneFloorPolys.push(CutawayPolygon.fromCutawayPoints(pts, start, end));
}
inside += (ix.isHole ? -1 : 1);
inside += 1;
} else {
// Leaving a polygon.
inside += (ix.isHole ? 1 : -1);
if ( !inside ) prevX = ix.x; // Moving out and going from inside to not inside.
// Moving into a hole or moving out of a normal polygon.
inside -= 1;
if ( !inside ) prevX = ix.x;
}
}

Expand All @@ -543,7 +543,19 @@ export class ElevationHandler {

// If all holes or no polygons, we are done.
if ( !combinedPolys.length || combinedPolys.every(poly => !poly.isPositive) ) return [];
return combinedPolys.map(poly => CutawayPolygon._convertFromPolygon(poly, start, end));
return combinedPolys.map(poly => {
// Strip duplicate points, which will cause problems later.
const pts = [...poly.iteratePoints({ close: false })];
let lastPt = pts[0];
const deduped = [lastPt];
for (let i = 1, n = pts.length; i < n; i += 1 ) {
const pt = pts[i];
if ( pt.almostEqual(lastPt) ) continue;
deduped.push(pt);
lastPt = pt;
}
return CutawayPolygon.fromCutawayPoints(deduped, start, end);
});
}


Expand Down
29 changes: 28 additions & 1 deletion scripts/Token.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* globals
canvas,
CanvasAnimation,
CONFIG,
CONST,
game,
PIXI,
Ruler,
ui
*/
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
Expand Down Expand Up @@ -49,6 +51,30 @@ function preCreateToken(tokenD, data, _options, _userId) {

/**
* Hook preUpdateToken
* Adjust elevation if teleporting into a region.
* @param {Document} document The Document instance being updated
* @param {object} changed Differential data that will be used to update the document
* @param {Partial<DatabaseUpdateOperation>} options Additional options which modify the update request
* @param {string} userId The ID of the requesting user, always game.user.id
* @returns {boolean|void} Explicitly return false to prevent update of this Document
*/
function preUpdateToken(tokenD, changed, options, _userId) {
if ( !(Object.hasOwn(changed, "x") || Object.hasOwn(changed, "y")) ) return;

// If teleporting to another elevated region, set the elevation of the destination accordingly.
if ( options.teleport ) {
const dest = tokenD.object.getCenterPoint({ x: changed.x ?? tokenD.x, y: changed.y ?? tokenD.y });
dest.elevation = changed.elevation ?? tokenD.elevation;
for ( const region of canvas.regions.placeables ) {
const tm = region.terrainmapper;
if ( !(tm.isElevated && region.testPoint(dest, dest.elevation)) ) continue;
changed.elevation = tm.elevationUponEntry(dest);
}
}
}

/**
* Hook updateToken
* If disposition changes, change actor's unique effect status icon display.
* @param {Document} document The Document instance being updated
* @param {object} changed Differential data that will be used to update the document
Expand Down Expand Up @@ -127,7 +153,8 @@ function refreshToken(token, flags) {
PATCHES.BASIC.HOOKS = {
preCreateToken,
refreshToken,
updateToken
updateToken,
preUpdateToken
};

// ----- NOTE: Wraps ----- //
Expand Down
57 changes: 41 additions & 16 deletions scripts/regions/HighlightRegionShader.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ Hooks.on("init", function() {
uniform mediump float hatchY;
uniform vec4 border; // Region border: s: left, t: top, p: right, q: bottom
uniform mediump float insetBorderThickness;
varying vec2 percentFromBorder;
varying vec2 vPercentFromBorder;
varying float vHatchHorizontal;
varying float vHatchVertical;
varying vec2 vSquarePercentFromBorder;
void main() {
vec2 pixelCoord = aVertexPosition;
Expand All @@ -48,10 +49,15 @@ Hooks.on("init", function() {
// Added by Terrain Mapper.
// Determine where we are as a percent of the region border.
percentFromBorder = (pixelCoord - border.st) / (border.pq - border.st);
vHatchOffset = ((pixelCoord.x * hatchX) + (pixelCoord.y * hatchY)) / (SQRT2 * 2.0 * hatchThickness);
vPercentFromBorder = (pixelCoord - border.st) / (border.pq - border.st);
vHatchOffset = ((pixelCoord.x * hatchX) + (pixelCoord.y * hatchY)) / (SQRT2 * 2.0);
vHatchHorizontal = pixelCoord.y / (SQRT2 * 2.0 * insetBorderThickness); // hatchX = 0, hatchY = 1
vHatchVertical = pixelCoord.x / (SQRT2 * 2.0 * insetBorderThickness); // hatchX = 1, hatchY = 0
// Determine how far along the ramp path we are.
// stpq
float size = max(border.p - border.s, border.q - border.t);
vSquarePercentFromBorder = (pixelCoord - border.st) / size;
}
`;

Expand All @@ -66,25 +72,42 @@ Hooks.on("init", function() {
uniform mediump float hatchThickness;
// Added by Terrain Mapper
uniform mediump float hatchX;
uniform mediump float hatchY;
uniform mediump float insetPercentage;
uniform mediump float insetBorderThickness;
varying vec2 percentFromBorder;
uniform bool variableHatchThickness;
varying vec2 vPercentFromBorder;
varying float vHatchHorizontal;
varying float vHatchVertical;
varying vec2 vSquarePercentFromBorder;
void main() {
gl_FragColor = tintAlpha;
if ( !hatchEnabled ) return;
// Added by Terrain Mapper.
float hatchOffset = vHatchOffset;
float thisHatchThickness = hatchThickness;
if ( variableHatchThickness ) {
// hatchX and hatchY are between -1.0 and 1.0
// vSquarePercentFromBorder are between 0.0 and 1.0.
float xPortion = (hatchX >= 0.0 ? vSquarePercentFromBorder.x : (1.0 - vSquarePercentFromBorder.x)) * abs(hatchX);
float yPortion = (hatchY <= 0.0 ? vSquarePercentFromBorder.y : (1.0 - vSquarePercentFromBorder.y)) * abs(hatchY);
float ratio = clamp(xPortion + yPortion, 0.0, 1.0);
// (thisHatchThickness * xPortion) + (thisHatchThickness * yPortion)
thisHatchThickness = hatchThickness + (hatchThickness * ratio * 3.0);
// gl_FragColor = vec4(vSquarePercentFromBorder.x, vSquarePercentFromBorder.y, 0.0, 0.8);
// return;
}
// Added by Terrain Mapper.
float hatchOffset = vHatchOffset / thisHatchThickness;
if ( insetPercentage != 0.0 ) {
bvec4 isInset = bvec4(
percentFromBorder.x < insetPercentage,
percentFromBorder.y < insetPercentage,
percentFromBorder.x > 1.0 - insetPercentage,
percentFromBorder.y > 1.0 - insetPercentage
vPercentFromBorder.x < insetPercentage,
vPercentFromBorder.y < insetPercentage,
vPercentFromBorder.x > 1.0 - insetPercentage,
vPercentFromBorder.y > 1.0 - insetPercentage
);
// s: left, t: top, p: right, q: bottom
Expand All @@ -93,28 +116,31 @@ Hooks.on("init", function() {
// Split the corners along the diagonal.
if ( all(isInset.st) ) {
if ( percentFromBorder.x < percentFromBorder.y ) hatchOffset = vHatchVertical;
if ( vPercentFromBorder.x < vPercentFromBorder.y ) hatchOffset = vHatchVertical;
else hatchOffset = vHatchHorizontal;
}
if ( all(isInset.pq) ) {
if ( percentFromBorder.x > percentFromBorder.y ) hatchOffset = vHatchVertical;
if ( vPercentFromBorder.x > vPercentFromBorder.y ) hatchOffset = vHatchVertical;
else hatchOffset = vHatchHorizontal;
}
if ( all(isInset.sq) ) {
if ( percentFromBorder.x < (1.0 - percentFromBorder.y) ) hatchOffset = vHatchVertical;
if ( vPercentFromBorder.x < (1.0 - vPercentFromBorder.y) ) hatchOffset = vHatchVertical;
else hatchOffset = vHatchHorizontal;
}
if ( all(isInset.tp) ) {
if ( (1.0 - percentFromBorder.x) < percentFromBorder.y ) hatchOffset = vHatchVertical;
if ( (1.0 - vPercentFromBorder.x) < vPercentFromBorder.y ) hatchOffset = vHatchVertical;
else hatchOffset = vHatchHorizontal;
}
if ( any(isInset) ) thisHatchThickness = insetBorderThickness;
}
// From original HighlightRegionShader.
float x = abs(hatchOffset - floor(hatchOffset + 0.5)) * 2.0;
float s = thisHatchThickness * resolution;
Expand All @@ -130,7 +156,6 @@ Hooks.on("init", function() {
HighlightRegionShader.defaultUniforms.insetPercentage = 0;
HighlightRegionShader.defaultUniforms.border = [0, 0, 0, 0];
HighlightRegionShader.defaultUniforms.insetBorderThickness = 1;


HighlightRegionShader.defaultUniforms.variableHatchThickness = false;
});

10 changes: 7 additions & 3 deletions scripts/regions/Region.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,18 +208,21 @@ function _refreshTerrainMapperMesh() {
let hatchY = 1;
let insetPercentage = 0;
let insetBorderThickness = hatchThickness;
let variableHatchThickness = false;
if ( this[MODULE_ID].isPlateau ) {
// Set a striped inset border.
// Inside the border is solid.
insetPercentage = 0.1;
hatchThickness = 0;
} else if ( this[MODULE_ID].isRamp ) {
// Set a striped inset border.
// Direction stripes within the border.
insetPercentage = 0.1;
// Stripe across with no inset.
// Direction controls stripes, which get wider as the ramp increases.
insetPercentage = 0.0;
const res = calculateHatchXY(this[MODULE_ID].rampDirection);
hatchX = res.hatchX;
hatchY = res.hatchY;
variableHatchThickness = true;
hatchThickness *= 2;
}
const { left, top, right, bottom } = this.bounds;
mesh.shader.uniforms.border = [left, top, right, bottom];
Expand All @@ -228,6 +231,7 @@ function _refreshTerrainMapperMesh() {
mesh.shader.uniforms.hatchThickness = hatchThickness;
mesh.shader.uniforms.insetPercentage = insetPercentage;
mesh.shader.uniforms.insetBorderThickness = insetBorderThickness
mesh.shader.uniforms.variableHatchThickness = variableHatchThickness;
}

PATCHES.REGIONS.METHODS = { _refreshTerrainMapperMesh };
Expand Down
61 changes: 23 additions & 38 deletions scripts/regions/RegionElevationHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,19 +212,14 @@ export class RegionElevationHandler {
const opts = this.#cutawayOptionFunctions(usePlateauElevation);
const addSteps = this.isRamp && this.rampStepSize;
const stepFn = addSteps ? (a, b) => {
const cutPoints = this._rampCutpointsForSegment(a, b);
if ( !cutPoints.length ) return [];
const cutpoints = this._rampCutpointsForSegment(a, b);
if ( !cutpoints.length ) return [];

// Ensure the steps are going in the right direction.
const rampDir = a.z > b.z;
const stepDir = cutPoints[0].z > cutPoints.at(-1);
if ( rampDir ^ stepDir ) cutPoints.reverse();
const steps = insertSteps([a, ...cutPoints, b]);

// Drop a and b so as not to repeat points.
steps.shift();
steps.pop();
return steps;
const stepDir = cutpoints[0].z > cutpoints.at(-1);
if ( rampDir ^ stepDir ) cutpoints.reverse();
return [a, ...cutpoints, b];
} : undefined;
for ( const regionPoly of this.region.polygons ) {
allHoles &&= !regionPoly.isPositive;
Expand Down Expand Up @@ -364,16 +359,28 @@ export class RegionElevationHandler {
const dir = minMax.max.subtract(minMax.min);
const orthoDir = new PIXI.Point(dir.y, -dir.x); // 2d Orthogonal of {x, y} is {y, -x}
const cutpoints = [];
for ( const idealCutpoint of this.getRampCutpoints(poly) ) {

// Create steps where position is same but elevation changes.
// Start at the elevation for a—before the first cutpoint.
const idealCutpoints = this.getRampCutpoints(poly);
let startingElevation = this.rampFloor;
for ( let i = 0, n = idealCutpoints.length; i < n; i += 1 ) {
const idealCutpoint = idealCutpoints[i];
const orthoPt = idealCutpoint.add(orthoDir);
const ix = foundry.utils.lineLineIntersection(a, b, idealCutpoint, orthoPt);
if ( !ix ) break; // If one does not intersect, none will intersect.
if ( ix.t0 < 0 || ix.t0 > 1 ) continue;
const cutPoint = RegionMovementWaypoint3d.fromObject(ix);
cutPoint.elevation = idealCutpoint.elevation;
cutPoint.t0 = ix.t0;
cutpoints.push(cutPoint);
if ( ix.t0 < 0 || ix.t0 > 1 ) {
startingElevation = idealCutpoint.elevation;
continue;
}
const cutpoint0 = RegionMovementWaypoint3d.fromLocationWithElevation(ix, startingElevation);
const cutpoint1 = RegionMovementWaypoint3d.fromLocationWithElevation(ix, idealCutpoint.elevation);
cutpoint0.t0 = ix.t0;
cutpoint1.t0 = ix.t0;
cutpoints.push(cutpoint0, cutpoint1);
startingElevation = idealCutpoint.elevation;
}

return cutpoints;
}

Expand Down Expand Up @@ -637,25 +644,3 @@ function rotatePolygon(poly, rotation = 0, centroid) { // eslint-disable-line de
}
return new PIXI.Polygon(rotatedPoints);
}


/**
* Helper function to calculate steps along an a|b segment.
* Adds points so that the move between locations is either vertical or 2d but not both.
* @param {Point3d[]} cutPoints Array of points where a step occurs
* @returns {Point3d[]} The original cutPoints with additional points added in a new array
*/
function insertSteps(cutPoints) {
if ( cutPoints.length < 2 ) return cutPoints;
const res = [];
let prevPt = cutPoints[0];
const cl = prevPt.constructor;
for ( let i = 1, n = cutPoints.length; i < n; i += 1 ) {
const currPt = cutPoints[i];
res.push(prevPt);
res.push(cl.fromObject({ x: currPt.x, y: currPt.y, z: prevPt.z }));
prevPt = currPt;
}
res.push(cutPoints.at(-1));
return res;
}

0 comments on commit 2424040

Please sign in to comment.