Blocky Renderer
A VS Code extension that shows Hytale models (.blockymodel) as a 3D preview and plays their animations (.blockyanim)
on them, for quick checks without Blockbench.
It works like the Markdown preview: the JSON text is on the left, the preview next to it on the right. The divider
between them can be dragged as usual.
- Opening a
.blockymodel opens the preview on the right automatically (can be turned off: blockyRenderer.autoOpenPreview).
Manually: button in the title bar, Ctrl+K V (to the side) or Ctrl+Shift+V (in the same tab).
- There is one preview. It follows the active
.blockymodel editor or the selection in the model browser.
- Live: the preview already shows unsaved changes. If a script rewrites the model or texture, it reloads.
If the JSON is briefly broken while typing, the last valid state stays and the error is shown at the top.
- Cursor in the text → the node is outlined in the 3D view and its pivot is shown. Click in the 3D view → the node name
is highlighted in the text. Hovering shows the name path at the bottom.
- Texture:
<Name>_Texture.png, <Name>.png, <Name>*.png, Texture.png or <Name>_Textures/*.png in the
model folder, otherwise a dropdown with all PNGs in the folder.
- Grid in block size (32) with the outline of one block and axes at the origin.
The preview itself changes nothing, editing happens in the Blocky Editor (below). If you don't need the text at all,
open the files in the renderer only (see Renderer only). The lighting is only approximated: shape,
UVs and proportions should be right, the in-game look is not.
Animations
Opening a .blockyanim opens the same preview (also Ctrl+K V, Ctrl+Shift+V, title bar). It looks for the
matching model and plays the animation on it, looping or once to the end, as holdLastKeyframe says.
- Which model: an animation does not name a model. The search covers the server JSONs that name model and animation
together (
Server/Models/**, Server/Item/Items/**, such as AnimationSets or CustomModelAnimation) and the
folders above the animation including Models/. Among the matches, the model with the most animated nodes wins.
There are many item files, they are read in the background; a better model then appears on its own.
- Model at the top lists the candidates, with the share of known nodes if not all of them match.
"Other model…" searches all models. A manual choice applies to this animation and is stored in the
workspace. Without a match the preview offers "Choose Model…".
- Timeline at the bottom: ▶/⏸, slider, frame and seconds (60 frames per second), speed 0.25× to 2×, loop.
Keys: Space plays and pauses,
←/→ steps one frame (ten with Shift), Home/End jumps.
- If the animation moves nodes the model does not have, "⚠ n of m nodes missing" is shown; hover for the names.
This is common in vanilla, many animations have tracks for nodes that no longer exist in the model.
- Cursor in the animation → the node is outlined in the 3D view. Click in the 3D view → its tracks are highlighted in
the text. Changes to the text, including unsaved ones, apply immediately; the time stays where it is.
- In the preview of a model, Animation offers its animations. "More animations…" or "Other animation…" searches
all of them, so you can view a vanilla animation on your own model, for example. The Blocky Editor does not have
this list.
Editing
The Blocky Editor (3D) is a second editor for the same file: the JSON text on the left, the 3D view on the right,
both editable. Open it with the pencil icon in the title bar of the text or the preview, by right-clicking in the
Explorer or with Blocky: Edit in 3D Editor. If the preview shows the same model, the editor takes its place.
- Every change in the 3D view, the outliner or the properties panel becomes a text edit at exactly the affected spot.
The rest of the file stays byte for byte, including fields the extension does not know. Saving, the unsaved dot and
Ctrl+Z/Ctrl+Y come from VS Code, one command is one undo step.
- Numbers are rounded to 6 digits as in vanilla files. New nodes get the next free
id and a free name
(Box, Box2, …), duplicates get new ids for the whole subtree.
- In the 3D view: click selects.
Delete deletes, Ctrl+D duplicates, H toggles visibility, F2 renames, Esc
deselects.
- Properties on the right of the editor: name, position, rotation (in degrees, stored as a quaternion), offset,
size, stretch, normal (quad), visible, double-sided, shading. Type and press
Enter, use the arrow keys (finer with
Shift) or drag the axis letter. While dragging, the view moves along, the value is written on release.
- If the view does not match the text at the moment (for example while the JSON is broken), a command is discarded
instead of guessed.
Renderer only
Instead of the text with a preview next to it, a file can also open on its own in the renderer, as a separate tab
without a code tab.
Once: right-click in the Explorer or the model browser → Open in Renderer Only, or the button in the title bar
of the text. This opens the Blocky Preview (3D): the 3D view with texture, model and animation selection and the
timeline, without editing.
Always: setting blockyRenderer.openFilesIn or Open Blocky Files In… (gear menu in the Blocky Renderer tab):
text (default): text editor, the preview opens next to it.
editor: models in the Blocky Editor (editable), animations in the Blocky Preview.
viewer: models and animations in the Blocky Preview.
The setting adds the matching entries to workbench.editorAssociations, at the same level (user or workspace).
Other associations there stay untouched, text only removes the extension's own entries again.
The text is still available via Show Source in the title bar. Clicks in the 3D view only highlight the node in
the text if it is already open. From the Blocky Preview of a model, the pencil icon switches to the Blocky Editor.
Blocky Renderer tab
The extension has its own Blocky Renderer tab (cube icon) in the Activity Bar on the left with the model browser
(Models) and the node tree (Nodes). Everything starts from there:
- Title bar of Models: new model, refresh, collapse all, and in the
… menu "Open Blocky Files In…".
- Right-click a model: preview to the side, edit in 3D editor, open in renderer only, show source.
Right-click a folder: new Blocky model in it.
- Title bar of Nodes: add box and group, edit in 3D editor, and in the
… menu add quad, preview to the side and
show source, each for the model whose nodes are currently shown.
- Without models or without an open model, both views show a hint with a button for a new model.
Outliner
In the Blocky Renderer tab, Nodes shows the node tree of the model in the active tab (text, Blocky Editor or
Blocky Preview). The selection is the same everywhere: a click in the outliner, a click in the 3D view and the cursor
in the text all show the same node.
- Context menu: add box, quad or group as a child, rename (
F2), duplicate (Ctrl+D), toggle visibility (eye),
delete (Delete). + in the title bar adds a box to the selected node, or at the top level without a selection.
- New nodes sit at the level of their parent and get a texture layout starting at the atlas corner so that they are
visible. The extension cannot search for free space in the atlas yet.
New models
Blocky: New Blocky Model… (Command Palette, right-click a folder in the Explorer, + in the model browser) asks
for a name and creates <Name>.blockymodel: a cube with the starter texture <Name>_Texture.png, a cube without a
texture or an empty model. An existing texture with that name is left untouched.
- An empty
.blockymodel newly created in the Explorer gets the cube right away and is saved, Ctrl+Z empties it
again. Setting blockyRenderer.newModelTemplate: cube (default), empty or off.
- If a file is empty because a script or the terminal created it, the preview offers "Start with a Cube".
- The cube is 32×32×32, stands on the ground and has all six faces as a cross layout in a 128×64 atlas:
left, front, right, back side by side, top and bottom above front and right. In the starter texture each face has
its own color.
- A JSON schema adds hover help, validation and completion to the text.
Ctrl+Space offers the cube in an empty
file, and a box or a group inside nodes and children.
Model browser
In the Blocky Renderer tab, the Models list shows all .blockymodel files of the workspace, by folder.
A click shows the model in the preview without opening the file in an editor. The arrow keys step through all models
while the focus stays in the list. Blocky: Open Model Browser jumps there.
- The list is searched once at startup (
files.exclude applies) and then kept up to date by the file watcher.
The ↻ button in the title bar searches again. Without models in the workspace, a button for a new model is shown.
- Clicks in the 3D view do not open a text editor here. The source is opened by the button on the entry or in the
title bar of the preview.
- Clicking or typing in the text of a model shows that model in the preview again.
Build and install
npm install
npm run package # type check, tests, bundle, publish/blocky-renderer-<version>.vsix
code --install-extension publish/blocky-renderer-1.5.0.vsix
VS Code has three tasks for this (Terminal › Run Task…):
restore installs the dependencies into node_modules/ (npm ci, needed after cloning).
build bundles into dist/ (default build task, Ctrl+Shift+B).
publish runs the type check and tests and puts the .vsix into publish/.
Development: F5 starts a second VS Code window with the extension (build runs first), npm run watch rebuilds
continuously.
Tests and snapshots
npm test # unit tests
BLOCKY_VANILLA=<folder> npm test # additionally check all vanilla files in <folder> (several minutes)
npm run build && node scripts/snapshot.ts <model.blockymodel> [image.png] [--texture <png>] [--size 900x700]
[--editable] [--select <node, e.g. 0.1>]
[--animation <file.blockyanim>] [--frame <n>]
With BLOCKY_VANILLA, all models are read, validated against the JSON schema and put through a round trip:
sample nodes are changed, deleted, duplicated and inserted, and the rest of the file must stay the same. In addition,
all animations are read and evaluated at several points in time, and more than 99 % of them must find a model.
--editable renders the page as in the Blocky Editor, --select selects a node, --animation puts an animation on
the model, paused at frame --frame.
snapshot.ts renders a model with the same webview code in a headless Chromium (Chromium, Chrome or Brave,
otherwise CHROME=<path>) and saves a PNG. The first run sets up a browser profile and can take a minute.
Sources: 2830 vanilla models from Assets.zip and the official Hytale plugin for Blockbench
(~/.config/Blockbench/plugins/hytale_plugin.js). The UV calculation in src/shared/faces.ts
is taken from the plugin and checked against its code. It has not been compared with the game yet.
- JSON with
nodes (list) and lod, sometimes format (character or prop).
- Node:
id, name, position, orientation (quaternion), shape, children.
The node rotates around position. Children are relative to position + shape.offset of the parent node.
- Units: 1 block = 32, y up, origin at the bottom center of the block (x and z from −16 to 16).
front points to +Z, right to +X, top to +Y.
shape.type: box, quad or none (pure group or pivot).
A quad lies across settings.normal (+X … -Z, default +Z) and always uses the front layout.
With ±X, size.x becomes the z extent, with ±Y, size.y becomes the z extent.
- The shape is shifted by
shape.offset. stretch scales it around its center without changing the UVs.
A negative value mirrors it (in vanilla often -1 on x).
- UV size per face from the box size: front/back x×y, left/right z×y, top/bottom x×z.
offset is the corner in the atlas. mirror reverses the reading direction from that corner (to the left or up).
angle (0/90/180/270) rotates the texture on the face and swaps width and height.
shadingMode: flat, standard, fullbright, reflective. Here only fullbright glows by itself.
- Not evaluated:
unwrapMode (custom, full, collapsed), isPiece (attachments) and isStaticBox.
The last two are in shape.settings, isPiece on every group.
lod is auto, off, disappear or billboard. id is always a number as a string.
- Two styles: about 1050 files with
lod first and "nodes": [ { (probably from Blockbench), the rest with
nodes first. Both: every property on its own line, two spaces, LF.
Animations (.blockyanim)
Sources: 6736 vanilla animations and the official plugin
(hytale-blockbench-plugin, src/blockyanim.ts and
src/animations.ts) together with Blockbench's own interpolation. Rebuilt in src/shared/pose.ts,
it has not been compared with the game yet.
- JSON with
formatVersion (1), duration in frames (60 per second), holdLastKeyframe and nodeAnimations.
nodeAnimations is keyed by node name; the tracks apply to all nodes with that name.
- Five tracks per node made of keyframes
{ time, delta, interpolationType }, interpolationType is smooth or
linear (linear if missing):
position: {x, y, z}, added to the node's position.
orientation: quaternion, applied after the node's own rotation (model × delta).
shapeStretch: {x, y, z}, multiplies the stretch of the node's own shape.
shapeVisible: true/false for the node's own shape, not for its children.
shapeUvOffset: {x, y} in whole pixels, shifts the texture of the node's own shape; negative y moves down in
the image (this is how flames run through a vertical strip).
- Between two keyframes: rotations via slerp, between two
smooth keyframes with a custom, asymmetric easing curve
(57 % at half time). Position and stretch are linear if both keyframes are linear, otherwise Catmull-Rom over the
neighboring keyframes. Visibility and UV offset jump.
- When looping (
holdLastKeyframe: false), it goes from the last keyframe past the end to the first; otherwise the
last value stays.
License
Proprietary, all rights reserved. See the LICENSE file for the terms of use. Third-party components and their
licenses are listed in THIRD_PARTY_NOTICES.md.
| |