Skip to content

Instantly share code, notes, and snippets.

@rezoner
Last active August 15, 2021 14:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rezoner/de9ac9b74017546d128dee2fa796e0c4 to your computer and use it in GitHub Desktop.
Save rezoner/de9ac9b74017546d128dee2fa796e0c4 to your computer and use it in GitHub Desktop.
SpriteStack format specs draft

A spritestack model is a ZIP file with two obligatory entries:

package.json

{
    "fileType": "SpriteStackModelProject",
    "title": "Whatever"
}

model.json

{
    "formatVersion": 2,
    "fileType": "SpriteStackModel",
    "parts": [
      {
          "name": "Name of a part",
          "data": [-3902, 0, -2, 2, -62, 0, -2, 2, -62, 0, -2, 2, -62, 0, -2, 2, -141218, 0],
          "hidden": false
      }
    ],
    "size": [64, 64, 64],
    "palette": [0, 2236468, 4532284, 6699313, 9393723, 14643494, 14262374, 15647642, 16777215, 16511542, 10085712, 6995504, 3642478, 4942127, 5393188, 3292217, 4145012, 3170434, 5992161, 6527999, 6278628, 13360124, 10202551, 8683143, 6908522, 5854802, 7750282, 11285042, 14243683, 14121914, 9410378, 9072432]
}

Size

Currently only 64x64x64 size is supported. Even if your model is smaller put it within 64x64x64 space.

How to read data?

SpriteStack model is a set of equally sized cube spaces (parts) filled with voxels.

Data is an array of palette color indexes of each voxel.

0 means that the space is empty 1 means the palette index 0 2 means the palette index 1 ...

To safe space repeated indexes are folded using negative number to represent how many time the next value should be repeated

Let's decipher one array:

[-100, 0, 2, 3, 2, -7, 8]

Means:

100 x 0, 2, 3, 2, 7 x 8

Useful formulas

sizeX, sizeY, sizeZ is a cube (model) size

Array (cube) index to 3D coordinates

function to3D(index) {

  let x, y, z;
  
  z = floor(index / (sizeX * sizeY));

  index -= (z * sizeX * sizeY);

  y = floor(index / sizeX);
  x = index % sizeX;

  return { x, y, z }

}

3D coordinates to array (cube) index

function to1D(x, y, z) {

    return (z * sizeX * sizeY) + (y * sizeX) + x;

}

RGB to Number

function rgbToInt(r, g, b) {
    
    return b + g * 256 + r * 65536;
    
}

Number to RGB

function intToRGB(color) {

    return {
        r: floor(color / 65536), 
        g: floor(color / 256) % 256, 
        b: color % 256
    }

}
@VictorTaelin
Copy link

VictorTaelin commented Jan 8, 2020

For anyone needing this boring conversion, here is a ready JS function that receives a SpriteStack model (the exported JSON) and returns an array of voxels (position and color):

function model_to_voxels(model) {
  var voxels = [];

  // A model has many parts
  for (var m = 0; m < model.parts.length; ++m) {

    // Each part has a data array storing 64x64x64 color ids
    var data = model.parts[m].data;
    var size = model.size;
    for (var i = 0, d = 0; d < data.length; ++d) {
      var val = data[d];

      // When an element of the array is negative, it isn't
      // a color id (cid), but a number of times to repeat
      // the next color id; this is a micro-compression
      if (val < 0) {
        var len = Math.abs(val);
        var cid = data[++d];
      } else {
        var len = 1;
        var cid = val;
      }

      // If the color id isn't zero, then the actual color
      // is stored on `model.pallete[col - 1]`, so we
      // get it, together with its position, and add
      if (cid !== 0) {
        for (var k = 0; k < len; ++k) {
          var n = i + k;
          var x = n % size[0] - size[0]/2;
          var y = ((n/size[0])>>>0) % size[0] - size[1]/2;
          var z = ((n/(size[0]*size[1]))>>>0) - size[2]/2;
          var pos = {x,y,z};
          var col = model.palette[cid - 1];
          var r = Math.floor(col / 65536);
          var g = Math.floor(col / 256) % 256;
          var b = col % 256;
          var col = {r,g,b};
          voxels.push([pos,col]);
        }
      }

      i += len;
    }
  }

  return voxels;
};

Positions are centered on origin, so, for example, a 64x64x64 box ranges from -32 to 31.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment