Skip to content

Commit

Permalink
docs setup for sprite mesh
Browse files Browse the repository at this point in the history
  • Loading branch information
jerzakm committed Nov 18, 2023
1 parent c38c297 commit 296a668
Show file tree
Hide file tree
Showing 12 changed files with 205 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/light-foxes-perform.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@threejs-kit/instanced-sprite-mesh": minor
---

Docs sketch for sprites, fps setter
4 changes: 2 additions & 2 deletions apps/docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export default defineConfig({
autogenerate: { directory: 'intro' },
},
{
label: 'Sprites',
autogenerate: { directory: 'sprites' },
label: 'Instanced Sprite',
autogenerate: { directory: 'instancedSprite' },
},
{
label: 'Materials',
Expand Down
Binary file added apps/docs/src/assets/sprite/typescript1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: Overview
description: Installation and reference sheet for InstancedSpriteMesh
---


import { Image } from "astro:assets";

`InstancedSpriteMesh` was created to enabled high performant instancing of animated sprites in threejs. Current solutions do not fare well with very high instance counts. This package aims to resolve that, boasting the capability to render tens of thousands individually animated sprites even on low/medium power devices.


## Installation

Package is published as `@threejs-kit/instanced-sprite-mesh` and you can add it to your project by installing it as such:

```bash
npm i @threejs-kit/instanced-sprite-mesh
```



## Getting started
At it's core `InstancedSpriteMesh` is built on top of, and extends [InstancedUniformsMesh](https://protectwise.github.io/troika/three-instanced-uniforms-mesh/) so any of their documentation might be helpful to you - it's recommended to get familiar with it as well.

### Basic example

InstancedSpriteMesh needs spritesheet texture and spritesheet metadata provided to it (imported from Aseprite, others or generated on the fly)

- load texture
- create a base material
- make instanced sprite mesh
- add spritesheet to mesh

```js
const texture = new THREE.TextureLoader().load('/spritesheet.png');
texture.minFilter = THREE.NearestFilter;
texture.magFilter = THREE.NearestFilter;

const baseMaterial = new THREE.MeshBasicMaterial({
transparent: true,
alphaTest: 0.01,
side: THREE.DoubleSide,
map: texture
});

const mesh = new InstancedSpriteMesh(baseMaterial, INSTANCE_COUNT);

mesh.fps = 15

const spritesheet = parseAseprite(JSON.parse(rawSpritesheet));
mesh.spritesheet = spritesheet;
```

### Quick api preview


```js
// set global FPS for updating
mesh.fps = 15

// loop one animation globally for all sprites without indivudually set animation
mesh.play('IdleBackward', true).global();
// play this animation once for sprite at instanceId 0
mesh.play('RunLeft').at(0);

// only set animation
mesh.animation.setGlobal('IdleRight');
mesh.animation.setAt(0, 'RunBackward');

// defined looping behaviour
mesh.loop.setGlobal(true);
mesh.loop.setAt(0, false);

// billboarding
mesh.billboarding.setGlobal(true);
mesh.billboarding.setAt(0, true);

// tint, only global for now
mesh.tint.setGlobal({h:2, s:0.8, v: 0.8})
```

## Updating sprite animations

Currently the sprites are only animated based on provided FPS and a timer. Either:

- `mesh.updateTime();` has to be called to update the animations - manually or in RAF (performance.now())
- `mesh.time = 100` can be set manually



Support for animations independent on time and setting specific frameIDs will be coming soon


## Typescript support
Yes, wip.

import materialPreview from "../../../assets/sprite/typescript1.png";




```ts
type SpriteAnimations =
| 'RunRight'
| 'RunLeft'
| 'RunForward'
| 'IdleRight'
| 'IdleLeft'
| 'IdleForward'
| 'RunBackward'
| 'IdleBackward';
const mesh: InstancedSpriteMesh<THREE.MeshBasicMaterial, SpriteAnimations> =
new InstancedSpriteMesh(baseMaterial, INSTANCE_COUNT);
```

for example, the above will allow for autocompletion of animation names

<Image src={materialPreview} alt="shows how glint material looks like" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: Reference sheet
description: InstancedSpriteMesh
---

##
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Spritesheet metadata
description: InstancedSpriteMesh
---

## Spritesheet format

Each instanced mesh has to be provided with a spritesheet formatted like this. The library provides utilities to parse aseprite json metadata and to generate it from a image file, but you can (should?) write your own


```ts
type SpritesheetFormat = {
frames: [x: number, y: number, w: number, h: number][];
animations: Record<string, [frameId: number, duration: number][]>;
sheetSize: [w: number, h: number][];
animationLengths: number[];
};
```

- `frames`: each frame of the spritesheet has its cooridnates and size specified separately - this will allow for non-uniform sized frames
- `animations`: `Map` of named animations. Each animation is defined by an array of frames it consists of (frameId is the index of a frame in `frames`)
- `sheetSize` - size of an image
- `animationLengths` - util, how many frames does each animation have
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
title: Making spritesheet from images
description: InstancedSpriteMesh
---


### InstancedSpriteMesh

tutorial on how to make spritesheet metadata with just images:

Sprites from
https://luizmelo.itch.io/monsters-creatures-fantasy


1. load images
2. 1 img file = 1 animation
3. Name animations and Combine img files into one spritesheet via html canvas so there's only one texture
4. Generate meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
title: Importing from aseprite
description: InstancedSpriteMesh
---


### InstancedSpriteMesh


`parseAseprite` helper function docs.
7 changes: 0 additions & 7 deletions apps/docs/src/content/docs/sprites/instanced-sprite-mesh.mdx

This file was deleted.

24 changes: 12 additions & 12 deletions apps/docs/tailwind.config.cjs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const colors = require('tailwindcss/colors');
const starlightPlugin = require('@astrojs/starlight-tailwind');

// Generated color palettes
const accent = { 200: '#dcc78c', 600: '#836800', 900: '#3f3000', 950: '#2d2200' };
const gray = { 100: '#f6f6f6', 200: '#eeeded', 300: '#c2c2c1', 400: '#8c8b8b', 500: '#585857', 700: '#383838', 800: '#272726', 900: '#181818' };

/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
// Your preferred accent color. Indigo is closest to Starlight’s defaults.
accent: colors.indigo,
// Your preferred gray scale. Zinc is closest to Starlight’s defaults.
gray: colors.zinc,
},
},
},
plugins: [starlightPlugin()],
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: { accent, gray }
},
},
plugins: [starlightPlugin()],
};

Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,6 @@ export const start = async () => {
playerIndicator.position.set(posX[0], 2, posZ[0]);
updateAgents(0.01);

sprite.updateTime();
if (dirtyInstanceMatrix) {
sprite.instanceMatrix.needsUpdate = true;
dirtyInstanceMatrix = false;
Expand Down
10 changes: 10 additions & 0 deletions packages/instanced-sprite-mesh/src/InstancedSpriteMesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class InstancedSpriteMesh<
private _spritesheet?: SpritesheetFormat | undefined;
private _animationMap: Map<V, number>;
private _time: number = 0;
private _fps: number = 15;

constructor(
baseMaterial: T,
Expand Down Expand Up @@ -165,6 +166,15 @@ export class InstancedSpriteMesh<
this._time = value;
}

public get fps(): number {
return this._fps;
}

public set fps(value: number) {
this._spriteMaterial.uniforms.fps.value = value;
this._fps = value;
}

public updateTime() {
const value = performance.now() * 0.001;
this._spriteMaterial.uniforms.time.value = value;
Expand Down

0 comments on commit 296a668

Please sign in to comment.