Skip to content

Commit

Permalink
Merge pull request #5 from Feu-Secret/tokenmagic-dev
Browse files Browse the repository at this point in the history
Tokenmagic dev
  • Loading branch information
Feu-Secret authored Jul 8, 2020
2 parents 3f02be4 + 9125eea commit 0da1da0
Show file tree
Hide file tree
Showing 21 changed files with 771 additions and 174 deletions.
Binary file modified .vs/Tokenmagic/v16/.suo
Binary file not shown.
4 changes: 2 additions & 2 deletions .vs/VSWorkspaceState.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
"",
"\\tokenmagic",
"\\tokenmagic\\fx",
"\\tokenmagic\\fx\\assets",
"\\tokenmagic\\fx\\filters",
"\\tokenmagic\\fx\\filters\\proto",
"\\tokenmagic\\fx\\glsl",
"\\tokenmagic\\fx\\glsl\\fragmentshaders",
"\\tokenmagic\\fx\\glsl\\vertexshaders",
"\\tokenmagic\\libs",
"\\tokenmagic\\module"
],
"SelectedNode": "\\tokenmagic\\fx\\glsl\\fragmentshaders\\fire.js",
"SelectedNode": "\\tokenmagic\\module\\tokenmagic.js",
"PreviewInSolutionExplorer": false
}
Binary file modified .vs/slnx.sqlite
Binary file not shown.
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
# Token Magic FX - Update v0.1.2-alpha

## FX

*Added :*
- Smoke Filter
- A high quality "fog/smoke/fume" filter who support blend modes.
- Flood Filter
- Ideal to simulate reflective surfaces on small or large areas.
- A lot of customizable properties (see compendium)

The new filters have been added to the TokenMagic macro compendium

*Fixed issues :*
- The blend modes performed their calculations using all channels, including the alpha channel, which was not correct. Now they only use RGB channels. Impacted filters are Fumes, Fire and Electricity in some special properties configuration. Check your effects.
- Some effects were distorted if part of the image was in an invisible area of the screen (especially the edges).

## Filters handling

- All `delete` API functions now support an optional parameter : `filterId`. They will restrict deletion only on filters which match the filterId passed in parameter.
```javascript
// Example
TokenMagic.deleteFiltersOnTargeted("poisonSmoke_1");
TokenMagic.deleteFiltersOnSelected("mySuperSpookyGlow");
// Etc...
```

- Added new properties for filters creation and update.
- `autoDisable` : When this property is set to `true` the filter is automatically disabled when each animation become inactive (number of loops reached).
- `autoDestroy` : The same as autoDisable, but the filter is destroyed.
- Note : if you set `loops` with `Infinity` (default value if the property is not present), you will never trigger the autoDestroy or autoDisable. But you can prepare your filter with an auto keyword and then, later, update the `loops` properties with finite values to start the countdown.

```javascript
// autoDestroy example
let params =
[{
filterType: "glow",
filterId: "mySuperSpookyGlow",
autoDestroy: true,
outerStrength: 4,
padding: 10,
animated:
{
color:
{
active: true,
loopDuration: 3000,
loops: 5,
animType: "colorOscillation",
val1:0x003000,
val2:0x00FF00
}
}
}];
TokenMagic.addFiltersOnSelected(params);
```
## Compatibility

Fine with Foundry VTT 0.6.5

# Token Magic FX - Update v0.1.1-alpha

## FX
Expand Down
Binary file modified Tokenmagic.zip
Binary file not shown.
79 changes: 63 additions & 16 deletions tokenmagic/fx/Anime.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export class Anime {
this.elapsedTime[effect] += frameTime;
}
});

this.autoDisableCheck();
}

cycleCheck(effect, frameTime) {
Expand All @@ -79,6 +81,29 @@ export class Anime {
return true;
}

async autoDisableCheck() {
// Check of conscience
if (this.puppet == null) { return; }

// Only the owner can alter the filter parameters
if (!(this.puppet.filterOwner === game.data.userId
&& (this.puppet.autoDisable || this.puppet.autoDestroy))) { return; }

if (this.puppet.enabled === false) { return; }

if (Object.values(this.animated).every(animeEffect => animeEffect.active === false)) {

let params = {};
params.filterId = this.puppet.filterId;
this.puppet.autoDestroy ? params.destroy = true : params.enabled = false;

var placeable = this.puppet.getPlaceable();

// updating the filter trigger an update{placeable} for everyone
await window.TokenMagic.updateFilterByPlaceable(params, placeable);
}
}

isPauseBetweenLoop(effect, frametime) {
if (this.pauseBetween[effect] && this.animated[effect].pauseBetweenDuration > 0) {
this.pauseBetweenElapsedTime[effect] += frametime;
Expand Down Expand Up @@ -110,25 +135,25 @@ export class Anime {
}

colorOscillation(effect) {
var rgbValue1 = Anime.valueToRgb(this.animated[effect].val1);
var rgbValue2 = Anime.valueToRgb(this.animated[effect].val2);

this.puppet[effect] = Anime.rgbToValue(
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[0], rgbValue2[0], Math.cos, false)),
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[1], rgbValue2[1], Math.cos, false)),
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[2], rgbValue2[2], Math.cos, false))
);
this.puppet[effect] =
Anime.colOscillation(
this.elapsedTime[effect],
this.animated[effect].loopDuration,
this.animated[effect].syncShift,
this.animated[effect].val1,
this.animated[effect].val2,
false);
}

syncColorOscillation(effect) {
var rgbValue1 = Anime.valueToRgb(this.animated[effect].val1);
var rgbValue2 = Anime.valueToRgb(this.animated[effect].val2);

this.puppet[effect] = Anime.rgbToValue(
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[0], rgbValue2[0], Math.cos, true)),
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[1], rgbValue2[1], Math.cos, true)),
Math.floor(Anime.oscillation(this.elapsedTime[effect], this.animated[effect].loopDuration, this.animated[effect].syncShift, rgbValue1[2], rgbValue2[2], Math.cos, true))
);
this.puppet[effect] =
Anime.colOscillation(
this.elapsedTime[effect],
this.animated[effect].loopDuration,
this.animated[effect].syncShift,
this.animated[effect].val1,
this.animated[effect].val2,
true);
}

cosOscillation(effect) {
Expand Down Expand Up @@ -271,6 +296,17 @@ export class Anime {
+ 1) / 2) + val2;
}

static colOscillation(elapsed, loopDuration, syncShift, val1, val2, isSync) {
var rgbValue1 = Anime.valueToRgb(val1);
var rgbValue2 = Anime.valueToRgb(val2);

return Anime.rgbToValue(
Math.floor(Anime.oscillation(elapsed, loopDuration, syncShift, rgbValue1[0], rgbValue2[0], Math.cos, isSync)),
Math.floor(Anime.oscillation(elapsed, loopDuration, syncShift, rgbValue1[1], rgbValue2[1], Math.cos, isSync)),
Math.floor(Anime.oscillation(elapsed, loopDuration, syncShift, rgbValue1[2], rgbValue2[2], Math.cos, isSync))
);
}

static getSynchronizedTime(loopDuration, syncShift) {
return (Anime._lastTime / loopDuration) + syncShift;
}
Expand Down Expand Up @@ -307,6 +343,17 @@ export class Anime {
}
}

static removeAnimationByFilterId(placeableId, filterId) {
Anime._animeMap.forEach((anime, id) => {
if (anime.puppet.placeableId === placeableId && anime.puppet.filterId === filterId) {
Anime._animeMap.delete(id);
}
});
if (Anime._animeMap.size === 0) {
Anime._suspendAnimation();
}
}

static resetAnimation() {
Anime._animeMap = new Map();
}
Expand Down
34 changes: 0 additions & 34 deletions tokenmagic/fx/filters/FilterBulgePinch.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,46 +38,12 @@ export class FilterBulgePinch extends PIXI.filters.BulgePinchFilter {
}
}

// Headache guaranteed !
// TODO : To improve and factorize (but there, I'm fed up !)
handleTransform() {
if (this.hasOwnProperty("zIndex")) {
this.placeableImg.parent.zIndex = this.zIndex;
}
this.radius = (Math.max(this.placeableImg.width, this.placeableImg.height)
* this.placeableImg.parent.worldTransform.a
* this.radiusPercent) / 200;

var placeableWidth = this.placeableImg.parent.width * this.placeableImg.parent.worldTransform.a;
var placeableHeight = this.placeableImg.parent.width * this.placeableImg.parent.worldTransform.a;

this.center = [0.5, 0.5];

// Anchor point takes into account only visible parts.
// To avoid undesirables effects, we must recompute the center when the image has parts outside the screen.
// TODO : take into account the additive padding (with an IFilterInterface)
if ((this.placeableImg.parent.worldTransform.tx + placeableWidth + this.padding) > canvas.app.screen.width) {
let invisibleWidth = (this.placeableImg.parent.worldTransform.tx + placeableWidth + this.padding) - canvas.app.screen.width;
let visibleWidth = placeableWidth - invisibleWidth + this.padding;
this.center[0] = (0.5 * (visibleWidth + (invisibleWidth / 2))) / visibleWidth
}

if ((this.placeableImg.parent.worldTransform.ty + placeableHeight + this.padding) > canvas.app.screen.height) {
let invisibleHeight = (this.placeableImg.parent.worldTransform.ty + placeableHeight + this.padding) - canvas.app.screen.height;
let visibleHeight = placeableHeight - invisibleHeight + this.padding;
this.center[1] = (0.5 * (visibleHeight + (invisibleHeight / 2))) / visibleHeight
}

if ((this.placeableImg.parent.worldTransform.tx - this.padding) < 0) {
let invisibleWidth = this.placeableImg.parent.worldTransform.tx - this.padding;
let visibleWidth = placeableWidth + invisibleWidth + this.padding;
this.center[0] = (0.5 * (visibleWidth + (invisibleWidth / 2))) / visibleWidth
}

if ((this.placeableImg.parent.worldTransform.ty - this.padding) < 0) {
let invisibleHeight = this.placeableImg.parent.worldTransform.ty - this.padding;
let visibleHeight = placeableHeight + invisibleHeight + this.padding;
this.center[1] = (0.5 * (visibleHeight + (invisibleHeight / 2))) / visibleHeight
}
}
}
114 changes: 114 additions & 0 deletions tokenmagic/fx/filters/FilterFlood.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { seaFlood } from '../glsl/fragmentshaders/flood.js';
import { customVertex2D } from '../glsl/vertexshaders/customvertex2D.js';
import { Anime } from "../Anime.js";
import "./proto/FilterProto.js";

export class FilterFlood extends PIXI.Filter {

constructor(params) {
let {
time,
scale,
glint,
billowy,
color,
shiftX,
shiftY,
tintIntensity
} = Object.assign({}, FilterFlood.defaults, params);

// using specific vertex shader and fragment shader
super(customVertex2D, seaFlood);

this.uniforms.waterColor = new Float32Array([0.00, 0.18, 0.54]);
this.uniforms.shift = new Float32Array([0.0, 0.0]);

Object.assign(this, {
time, scale, glint, billowy, color, shiftX, shiftY, tintIntensity
});

this.animated = {};
this.setTMParams(params);
this.anime = new Anime(this);
this.normalizeTMParams();
}

get time() {
return this.uniforms.time;
}

set time(value) {
this.uniforms.time = value;
}


get color() {
return PIXI.utils.rgb2hex(this.uniforms.waterColor);
}

set color(value) {
PIXI.utils.hex2rgb(value, this.uniforms.waterColor);
}

get scale() {
return this.uniforms.scale;
}

set scale(value) {
this.uniforms.scale = value;
}

get glint() {
return this.uniforms.glint;
}

set glint(value) {
this.uniforms.glint = value;
}

get billowy() {
return this.uniforms.billowy;
}

set billowy(value) {
this.uniforms.billowy = value;
}

get tintIntensity() {
return this.uniforms.tintIntensity;
}

set tintIntensity(value) {
this.uniforms.tintIntensity = value;
}

get shiftX() {
return this.uniforms.shift[0];
}

set shiftX(value) {
this.uniforms.shift[0] = value;
}

get shiftY() {
this.uniforms.shift[1];
}

set shiftY(value) {
this.uniforms.shift[1] = value;
}
}

FilterFlood.defaults = {
time: 0,
glint: 0.5,
scale: 70,
billowy: 0.5,
color: 0x0020A9,
shiftX: 0,
shiftY: 0,
tintIntensity: 0.2,
};



Loading

0 comments on commit 0da1da0

Please sign in to comment.