Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spritesheet animation handling. Also fix several minor issues #275

Merged
merged 42 commits into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
55d52cd
Add spritesheet animation handling and fix several minor issues
Codas Jul 23, 2024
3acf55b
Fix .template() not overriding db specified template
Codas Jul 23, 2024
4af202e
Fix playbackRate breaking syncGroups and loops
Codas Jul 24, 2024
4051a1f
Address pr review comments
Codas Jul 29, 2024
8141ade
Add .DS_Store files to gitignore
Codas Aug 9, 2024
af6b014
Merge branch 'master' into spritesheets
Codas Aug 9, 2024
8eff936
Fix attachTo with bindScale errors
Codas Aug 9, 2024
ea2d928
Merge branch 'master' into spritesheets
Codas Aug 10, 2024
a315aa4
Fix spritesheet animations currentTime / duration calculation
Codas Aug 11, 2024
1581664
Merge branch 'master' into spritesheets
Codas Aug 13, 2024
8b32318
Merge branch 'master' into spritesheets
Codas Aug 18, 2024
0a31980
Merge branch 'master' into spritesheets
Codas Aug 19, 2024
a50017d
Merge remote-tracking branch 'wasp/master' into spritesheets
Codas Sep 1, 2024
787f0fc
Create batchable, vision masking, optionally tileable base effect shader
Codas Sep 16, 2024
b8ee848
Spritesheet player for sequencer database viewer
Codas Sep 17, 2024
7f31adf
Add custom fps for flipbooks
Codas Sep 18, 2024
2226055
Add color matrix application to batchable vision sampler shader
Codas Sep 23, 2024
dd64ac0
Fix some tiling issues
Codas Sep 24, 2024
7642d3e
sprite manager refactor, allow for easier hot replacement of assets
Codas Sep 24, 2024
6a2e6c9
Merge branch 'master' into spritesheets
Codas Sep 27, 2024
1e399c1
Merge branch 'master' into spritesheets
Codas Sep 28, 2024
01ff75f
Add JIT spritesheet compilation and compression
Codas Oct 4, 2024
9976677
Merge branch 'master' into spritesheets
Codas Oct 4, 2024
4462498
Clean up dependencies
Codas Oct 4, 2024
557804d
Fix effect shader after merge...
Codas Oct 4, 2024
9eb42c7
Re-use cached video file for spritesheet generation if available
Codas Oct 4, 2024
f1c944b
Fix memory leak for video base texture after spritesheet generation
Codas Oct 4, 2024
e778ec9
Fix formatting, alpha trimming
Codas Oct 4, 2024
84b6362
Use premultiplied alpha for spritesheets
Codas Oct 4, 2024
3f31d65
Add semi-persistent offline cache for compiled spritesheets
Codas Oct 4, 2024
ad93b00
Add feature detection for spritesheet generator
Codas Oct 5, 2024
8058a9b
Use correct frameRate for spritesheets, formatting, feature detection…
Codas Oct 5, 2024
80777b4
Fix size calculations, cleanup
Codas Oct 5, 2024
ea00dcc
Resolve issue with frames not being closed if frame is 100% transparent
Codas Oct 5, 2024
d20cfcd
Increase cached file size to 40MB to include some more marker effects
Codas Oct 5, 2024
277221b
Fix issue with multiple jobs for the same file being created
Codas Oct 5, 2024
11dd17f
Fix astc and bc7 compression flag being switched
Codas Oct 6, 2024
02efb2e
Merge branch 'master' into spritesheets
Codas Oct 12, 2024
d5a94da
Merge branch 'fix-shape-fade-in-out' into spritesheets
Codas Oct 15, 2024
70b1fe1
Merge remote-tracking branch 'wasp/master' into spritesheets
Codas Oct 18, 2024
478c17a
Add changelog, spritesheet database documentation
Codas Oct 18, 2024
c091eef
Verify cached spritesheet source with simple hashing function
Codas Oct 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ module.js
module.js.map
style.css
!./src/module.js
.DS_Store
/dist
2,245 changes: 1,121 additions & 1,124 deletions docs/api/effect.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
## Changelog

# Version 3.4.0

- *Sequencer* - Added support for custom FPS for flipbooks through a a `_fps` database tag
- *Sequencer* - Added support for spritesheet type effects through linking the spritesheet `.json` manifest in the database
- *Sequencer* - Added a just in tile compiler to transform webm video-based effects to gpu-optimized spritesheets
- This greatly improves performance when many instances of the same persisted effects are playing at once
- Persisted effects are automatically transformed to spritesheets in background threads. Once the compiled spritesheets are available, video effects are seemlessly replaced with spritesheets, greatly reducing the overhead for video decoding for those effects. Depending on size of the effect, spritesheet generation is expected to take anywhere from 5 seconds to 90 seconds. The size of generated Spritesheet textures is limited to 8192x8192px to maximize compatibility and limit memory usage
- Generated spritesheets are cached client-side in a semi-persistent, file-based browser cache
- Spritesheet generation for video files is only supported in trusted contexts (meaning https or hosted and connected locally on the same machine). This is a hard limitation by the browser vendors and not expected to be lifted in the future
- Fixed flipbook animations framerate being tied to canvas FPS
- Fixed offset from `rotateTowards` applying to the source location instead of the target
- Fixed effects with added text and `rotateTowards` having their anchor point moved to an unexpected location
- Fixed persisted `stretchTo` effects not correctly looping the effect in certain cases
- Fixed `loopOptions` not working correctly for persisted effects when combined with `timeRange`
- Fixed `scaleOut` end value being multiplied by a potentially set `scaleIn` value.
- `scaleIn(0.25).scaleOut(2)` now scales the effect in from 0.25 to 1. Plays the animation at 1x size and then scales the animation out to 2x default animation size. Previously, the animation would scale out to 0.5x the default effect size.

# Version 3.3.8

- *Effects* - Fixed `screenSpace` being a required property instead of optional for `.animateProperty()` and `.loopProperty()`
Expand Down
81 changes: 79 additions & 2 deletions docs/database-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ Another feature of the database is being able to bundle up files into flipbook a

If you define `_flipbook: true` anywhere near files, they will become a single file that will animate accordingly. This is great for performance, since Foundry keeps all the textures in memory, which means that duplicating the same effect 100 times will cost almost as much as just 10.

The default FPS for these assets is 24 frames per second.
The default FPS for these assets is 24 frames per second but can be modified with the `_fps` property.

```js
const database = {
Expand All @@ -271,7 +271,84 @@ const database = {
"flipbook_tests/border/TokenBorderCircle01_12_Regular_Blue_400x400_00094.webp",
"flipbook_tests/border/TokenBorderCircle01_12_Regular_Blue_400x400_00095.webp"
],
_flipbook: true
_flipbook: true,
_fps: 30
},
}
```

## Spritesheet textures

Even more advanced and efficient than using flipbooks is the use of spritesheets for animated effects. A Spritesheet acts like a flipbook in a sense that all the frames of an animation are avilable as images. In the case of a spritesheet these images are all packed inside one larger image file together with a json document describing the location, size and timing of the individual frames.

The spritesheet image file can be a regular image (jpeg, webp, png, avif, ...) or a basis universal (gpu compressed texture format, supercompressed for network transfer). For smaller animations (up to about 2048x2048px), a regular image file is fine. For larger files, consider using basis universal to not overload the users RAM and video memory. A texture 8.000x8000 px in size takes up 256MB or RAM and video memory for a regular image file but "only" 64MB if encoded as basis_universal.

The spritesheet json file follows the PIXI.js structure ([official documentation](https://pixijs.com/7.x/guides/components/sprite-sheets)), with one addition: a `frameRate` property can be passed as meta attribute. The `animation` section is mandatory.

Example json file:

```json
{
"frames": {
"fire_01c-0": {
"frame": { "x": 0, "y": 0, "w": 128, "h": 128 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 128, "h": 128 },
"sourceSize": { "w": 128, "h": 128 }
},
"fire_01c-1": {
"frame": { "x": 128, "y": 0, "w": 128, "h": 128 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 128, "h": 128 },
"sourceSize": { "w": 128, "h": 128 }
},
"fire_01c-2": {
"frame": { "x": 256, "y": 0, "w": 128, "h": 128 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 128, "h": 128 },
"sourceSize": { "w": 128, "h": 128 }
},
"fire_01c-3": {
"frame": { "x": 384, "y": 0, "w": 128, "h": 128 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 128, "h": 128 },
"sourceSize": { "w": 128, "h": 128 }
}
/* ... more frames */
},
"animations": {
"fire_01c": [
"fire_01c-0",
"fire_01c-1",
"fire_01c-2",
"fire_01c-3"

/* ... more frames */
]
},
"meta": {
"image": "fire.basis",
"format": "BASISU_ETC1S",
"size": { "w": 1024, "h": 1024 },
"scale": "1",
}
}


```

The database entry for this file works just like regular image files, except the path should point to the json manifest file, not the image.

```js
const database = {
effects: {
generic: {
fire: "modules/your_module_name/Library/VFX/Generic/fire.json",
}
}
}
```
63 changes: 63 additions & 0 deletions examples/effects/13-rotateTowards-template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
(async () => {
ui.notifications.info("Rotate towards different positions. Left is original scale, right are scaled variants");
const buildStar = (center, scale = 1) => {
for (let row = -1; row <= 1; row++) {
for (let col = -1; col <= 1; col++) {
if (!col && !row) {
continue;
}
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation(center)
.rotateTowards({ x: center.x + col, y: center.y + row }, { template: true })
.scale(scale)
.play();
}
}
};
buildStar({ x: 1400, y: 1400 });
buildStar({ x: 2100, y: 1400 }, 0.75);
buildStar({ x: 2900, y: 1400 }, 1.25);

ui.notifications.info(
"Below, rotation straight to the right (control) and offset by +/- 45 deg. Right is offset by +/- 100 units"
);
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 1400, y: 2000 })
.rotateTowards({ x: 1500, y: 2000 }, { rotationOffset: 45, template: true })
.play();
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 1400, y: 2000 })
.rotateTowards({ x: 1500, y: 2000 }, { rotationOffset: -45, template: true })
.play();
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 1400, y: 2000 })
.rotateTowards({ x: 1500, y: 2000 }, { template: true })
.play();

new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 2000, y: 2000 })
.rotateTowards({ x: 2300, y: 2000 }, { offset: { y: -100 }, template: true })
.play();
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 2000, y: 2000 })
.rotateTowards({ x: 2300, y: 2000 }, { offset: { y: 100 }, template: true })
.play();
new Sequence()
.effect()
.file("jb2a.energy_beam.normal.bluepink.03.15ft")
.atLocation({ x: 2000, y: 2000 })
.rotateTowards({ x: 2300, y: 2000 }, { template: true })
.play();
})();
6 changes: 6 additions & 0 deletions examples/effects/23-scaleToObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
const token3 = canvas.tokens.placeables.find((t) => t.name === "Tester (3)");
const token4 = canvas.tokens.placeables.find((t) => t.name === "Tester (4)");


if (!token1 || !token2 || !token2 || !token4) {
ui.notifications.error('Please make sure tokens named "Tester (1)", "Tester (2)", "Tester (3)" and "Tester (4)" exist');
return;
}

new Sequence().effect().file("jb2a.token_border.circle.static.blue.001").atLocation({ x: 2000, y: 1500 }).play();

new Sequence().effect().file("jb2a.token_border.circle.static.blue.001").atLocation(token1).scaleToObject().play();
Expand Down
3 changes: 2 additions & 1 deletion examples/effects/52-flipbook.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"modules/sequencer/samples/flipbooks/fire/fire_01c-63.webp",
],
_flipbook: true,
_fps: 20, // defaults to 24 if not set
},
},
};
Expand All @@ -91,7 +92,7 @@
.atLocation({ x: 1200, y: 1100 })
.playbackRate(1.25)
.scale(2)
.text("flipbook", {...textStyle, anchor: {x: 0.5, y: -2.5}})
.text("flipbook", { ...textStyle, anchor: { x: 0.5, y: -2.5 } })
.duration(5000)
.play();
})();
21 changes: 21 additions & 0 deletions examples/effects/52-spritesheets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(async () => {
const textStyle = {
dropShadow: true,
dropShadowAngle: 0,
dropShadowBlur: 10,
dropShadowDistance: 0,
fill: "#ffffff",
fontStyle: "oblique",
miterLimit: 2,
strokeThickness: 5,
};

new Sequence()
.effect()
.file("modules/sequencer/samples/spritesheets/fire.json")
.atLocation({ x: 1200, y: 1100 })
.scale(2)
.text("spritesheet", {...textStyle, anchor: {x: 0.5, y: -2.5}})
.duration(5000)
.play();
})();
34 changes: 34 additions & 0 deletions examples/effects/53-stresstest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
(async () => {
// WARNING: this is taxing on the system, executing this script with sequencer 3.2.11 on time of writing
// will most likely make your world unresponsive. Safer values for cols and rows would be 10x15
const assets = [
{ name: "video", file: "modules/sequencer/samples/fire.webm", cols: 30, rows: 22 },
{ name: "spritesheet", file: "modules/sequencer/samples/spritesheets/fire.json", cols: 30, rows: 22 },
];

const effectSize = 120;
const effectGridSize = 120;

for (const { name, file, cols, rows } of assets) {
ui.notifications.info(`Now playing ${name} effects`);
await Sequencer.Helpers.wait(250);

const start = { x: 1200, y: 900 };
const seq = new Sequence();
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
seq.effect()
.file(file)
.atLocation({
x: start.x + col * effectGridSize,
y: start.y + row * effectGridSize,
})
.size(effectSize)
.duration(20 * 1000);
}
}

await seq.play({ preload: true });
await Sequencer.Helpers.wait(21 * 1000);
}
})();
45 changes: 45 additions & 0 deletions examples/effects/54-attachTo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(async () => {
const token1 = canvas.tokens.placeables.find((t) => t.name === "Tester (1)");

if (!token1) {
ui.notifications.error('Please make sure tokens named "Tester (1)" and "Tester (2)" exist');
return;
}

await token1.document.update({ x: 1200, y: 1200 }, { animate: false });

let seq = new Sequence()
.effect()
.file("jb2a.aura_themed.01.orbit.loop.cold.01.blue")
.text("moves with token")
.attachTo(token1)
.waitUntilFinished()
.play();

await Sequencer.Helpers.wait(500);

await token1.document.update({ x: 1600, y: 1200 }, { animate: true });

await seq;

seq = new Sequence()
.effect()
.file("jb2a.aura_themed.01.orbit.loop.cold.01.blue")
.text("scales with token")
.scaleToObject(2)
.attachTo(token1, { bindScale: true })
.waitUntilFinished()
.play();

await Sequencer.Helpers.wait(500);

const flags = token1.document.flags.pf2e;
await token1.document.update(
{ width: 2, height: 2, "flags.pf2e.linkToActorSize": false, "flags.pf2e.autoscale": false },
{ animate: true }
);

await seq;

await token1.document.update({ width: 1, height: 1, "flags.pf2e": flags }, { animate: true });
})();
4 changes: 2 additions & 2 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
"website": "http://fantasycomputer.works/"
}
],
"esmodules": ["module.js"],
"styles": ["style.css"],
"esmodules": ["dist/module.js"],
"styles": ["dist/style.css"],
"languages": [
{
"lang": "en",
Expand Down
Loading