skinview3d-etf
v0.0.4
Published
Unofficial skinview3d extension that renders ETF (Entity Texture Features) player skin features on the 3D player model.
Maintainers
Readme
skinview3d-etf
[!WARNING] This project is in alpha. The public API, rendering behavior, package layout and documentation may still change in any
0.xrelease; treat every minor release as potentially breaking. Do not use it in production yet.
Unofficial, community-built extension for skinview3d
(GitHub | npm)
that renders
ETF (Entity Texture Features)
player skin features on the 3D player model:
- transparency on the base skin layer;
- emissive pixels;
- blinking eyes;
- nose (villager and textured);
- enchanted pixel overlay;
- jacket/dress extension.
The decoder (decodeSkin()) is complete and available now: it reads
every ETF player skin feature - the marker and its choice cells, the
palette and the seven choice slots, transparency and forced-solid,
blinking, nose, jacket (styles 1-8) and the emissive/enchanted
pattern data - and prepares the overlay images the renderer
consumes. The renderer ships all six features - including the
jacket/dress extension - through attachETFSkinFeatures() on a
live viewer.
The in-skin cape no longer exists upstream (all code paths are commented out), so it is out of scope; the five former cape texture regions are reused as textured-nose sources.
1. Status
Early development. The decoder (decodeSkin()) is complete and
unit-tested against the
ETF example skins.
The renderer ships all six features - transparency, the nose
(villager and textured), the emissive pixels, blinking eyes, the
enchanted pixel overlay and the jacket/dress extension - on a live
viewer. Published on npm as skinview3d-etf
(GitHub | npm);
a live demo deploys from main (§4).
The full documentation lives in this repository under
docs/;
the Wiki
mirrors the same pages.
2. Usage
Install from npm:
npm install skinview3d-etfskinview3d and three
(GitHub | npm)
are peer dependencies, so install the versions the viewer already
uses.
import { SkinViewer } from "skinview3d";
import { attachETFSkinFeatures } from "skinview3d-etf";
const viewer = new SkinViewer({ canvas, width: 400, height: 400 });
await viewer.loadSkin(skinUrl);
const controller = attachETFSkinFeatures(viewer, {
features: { transparency: true, emissive: true, nose: true },
// Blinking: intervals are in ms; a [min, max] tuple re-rolls the
// interval after every blink.
blink: { periodMs: [4000, 10000], closedMs: 200 },
onWarning: (message) => console.warn(message),
});
// Required after every viewer.loadSkin() call - skin changes are not
// detected automatically:
controller.refresh();
// Restores every artifact and leaves the viewer exactly as it was:
controller.detach();Blinking runs automatically while the viewer's animation slot is free
(or shared through addAnimation); pass manageTicker: false and
call controller.update(dt) yourself in a custom render loop, and use
controller.setBlinkOptions({ state: "closed" }) to hold a fixed eye
state or change the timing at runtime. The documented defaults are
exported as DEFAULT_BLINK_OPTIONS.
The options form four groups: features (the six toggles -
transparency, emissive, blink, nose, enchanted and
jacket), blink, enchanted (texture, speed, opacity,
scale, smooth) and villagerNose (texture).
DEFAULT_ENCHANTED_OPTIONS exports the enchanted defaults, and the
runtime setters (setFeatures(), setBlinkOptions(),
setEnchantedOptions(), setVillagerNoseOptions()) merge partial
updates: an omitted property keeps its current value, while for the
two texture options an explicit texture: undefined restores the
built-in default and null turns the feature off.
The texture options accept the host's input forms unchanged: the
TextureSource / RemoteImage types are re-used from
skinview-utils
(GitHub | npm),
so a canvas from loadSkinToCanvas() / loadCapeToCanvas() passes
directly; decodeSkin() likewise takes any plain ImageData-shaped
buffer (e.g. ctx.getImageData()).
The extension never rebuilds the scene graph and never seizes the
viewer's animation slot; it adds artifacts under the existing meshes
and cleans all of them up on detach().
The decoder is a standalone, three-free module that operates on plain
pixel buffers (ImageData-compatible; 64x64, plus legacy 64x32 skins,
which are converted to the 1.8 layout first):
import { decodeSkin } from "skinview3d-etf";
const result = decodeSkin(imageData);
// result.skin, result.emissive?.mask, result.blink?.frames, ...[!CAUTION] The decoder API (
decodeSkin()and its types) now lives in the standaloneetf-skin-decoder(GitHub | npm) package and is re-exported here for the rest of the v0.0.x line. v0.1.0 will remove it from this package (BREAKING). New code should import it frometf-skin-decoderdirectly.
Note the migrations (breaking, expected before 1.0): the flat v0.0.1
villagerNoseTexture option moved to villagerNose.texture, and
the reserved v0.0.1 glintTexture option is now
enchanted.texture (the group adds speed, opacity, scale and
smooth). Upgrading from v0.0.2, the glint option group and its
setter are renamed enchanted (glint.texture ->
enchanted.texture, setGlintOptions() -> setEnchantedOptions()).
Browser support: the build targets ES2022 and the runtime expects
WebGL 2 (matching the three release's own browser target) - the
current versions of Chrome, Edge, Opera, Firefox and Safari all
qualify.
3. Building
Requirements: Node.js 22.12 or newer (see engines in package.json)
and pnpm enabled through corepack enable (the repository pins
[email protected]). Then:
pnpm install
pnpm typecheck # tsc --noEmit
pnpm build # tsup -> dist/ (minified ESM + type declarations)
pnpm test # vitest; real-fixture specs skip when the local
# example skins are absent
pnpm lint # eslint
pnpm format:check4. Demo
A Vite demo lives in examples/ and is part of this repository:
pnpm install
pnpm dev # serves the demo (default http://localhost:5173/)A live build is deployed from main to the
demo page.
- The page mounts a live
skinview3dviewer in a square stage. - The fixture bar offers the bundled sample skin
(
examples/src/assets/skins/example.png) and accepts a PNG upload of your own skin: 64x64, or a legacy 64x32 skin that is converted automatically (rejected files raise a browser alert). - The 3D tab shows three control trees grouped by owning package,
in order:
skinview3d-etf, the hostskinview3dandskinview3d-blockbench(GitHub | npm) (hidden until itsSkinViewBlockbenchmode is picked in theviewer.animationrow); every group title carries its package version. Each row carries one exact API keyword at its API-path depth and its tooltip shows the dotted path plus a description; after every load the demo decodes the skin and grays the rows the skin has no data for (a skin without the ETF marker grays almost everything). - Function rows (
attachETFSkinFeatures,detach,loadSkin,setAnimation) carry anexecutebutton with their parameters as child rows; values the demo derives itself (the fixture source, the animation name) are locked read-only inputs that explain the derivation in their tooltip. - Every parameter row carries a
resetin its own action column, and every table title and parameter group (the container rows) carries a groupresetthat restores every row in its group; the texture rows pick between the built-in default, the off state and a transient[upload]entry through one(select) [choose]pair. Theresetbutton in the stage's corner restores the camera pose. - The
skinview3d-etf:tree gates everything on itsattachETFSkinFeatures/detachexecute rows; theblinkrows couple to the feature switch, theenchantedrows tofeatures.enchantedand thevillagerNose.texturerow to the villager nose. - The
skinview3d-blockbench:tree picks its input file (animation, the bundled self-made copy or a transient[upload]entry) and plays its animations (animationName,setAnimation,forceLoop,paused,speed) next to the ETF features. - Uploaded files are processed in the browser only and are never sent anywhere.
- The decoder preview tab draws every prepared
decodeSkin()artifact next to the 3D view. - The demo is not part of the npm package.
5. Roadmap
v0.0.4 (current milestone):
- [x] reuse the
skinview-utilstexture-input types (interop); - [x] hand the decoder over to the standalone
etf-skin-decoderpackage and re-export it for the v0.0.x line; - [x] add the README badges and the package links;
- [x] refresh the documentation pages;
- [x] v0.0.4 release.
Planned:
- [ ] remove the decoder API (v0.1.0; breaking).
Exploring (no timeline):
- optional bloom quality mode;
- headless rendering (Node) for screenshot tests;
- enchanted direction / angle parameters.
History:
- [x] v0.0.3 release.
- [x] v0.0.2 release.
- [x] v0.0.1 release.
Bug reports and feature requests are welcome through the issue tracker.
6. Credits and disclaimer
Not affiliated with or endorsed by the ETF or skinview3d projects. ETF is LGPL-3.0 and serves as a specification reference only; no ETF code, comments, or assets are copied into this project.
7. License
MIT - see LICENSE.
