ephtracy / ephtracy.github.io

2.62k stars 289 forks source link

Request for import sample code #146

Closed unphased closed 4 years ago

unphased commented 4 years ago

I'm currently using .vox format writing code that I found here: https://codepen.io/quasimondo/pen/QjqZvV This has apparently also been mirrored here: https://github.com/chaojian-zhang/MagicaVoxelWriter

I took the code from the first link and hacked it into a functioning node.js script that I use with my intermediate format which is one voxel per line, with

X Y Z M

where M is the material/palette index.

I am having kind of bad luck so far. At first I thought it was working great, but then I realized that there is some inconsistent behavior where sometimes the top few z-slices of my .vox export appear to just be missing, lopped off.

I'm currently using .vox files to debug a triangle-aabb collision routine I'm coding up, so I have some screenshots here to illustrate:

image image

I'm highly confident that my files contain the full set of voxels, but somewhere there is either a bug in this MV build or in my shoddy node script that is causing some number of z-layers to go missing.

The node script contents:

#!/usr/bin/env node

const fs = require('fs');
function VOX(X, Y, Z) {
  this.X = X;
  this.Y = Y;
  this.Z = Z;
  this.vcount = 0;
  this.voxels = [];
  this.palette = [];

  // greyscale placeholder palette
  for (let i = 256; i > -1; --i) {
    this.palette.push(0xff000000 | i | (i << 8) | (i << 16));
  }

  this.setVoxel = function(x, y, z, i) {
    i |= 0;
    x |= 0;
    y |= 0;
    z |= 0;

    if (i >= 0 && i < 256 && x >= 0 && y >= 0 && z >= 0 && x < this.X && z < this.Y && z < this.Z) {
      const key = x + "_" + y + "_" + z;
      if (i > 0) {
        if (!this.voxels[key]) this.vcount++;
        this.voxels[key] = i;
      } else {
        if (this.voxels[key]) this.vcount--;
        delete this.voxels[key];
      }
    }
  };

  this.appendString = function(data, str) {
    for (var i = 0, j = str.length; i < j; ++i) {
      data.push(str.charCodeAt(i));
    }
  };

  this.appendUInt32 = function(data, n) {
    data.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff);
  };

  this.appendRGBA = function(data, n) {
    data.push((n >>> 16) & 0xff,(n >>> 8) & 0xff, n & 0xff, (n >>> 24) & 0xff);
  };

  this.appendVoxel = function(data, key) {
    var v = key.split("_");
    data.push(v[0], v[1], v[2], this.voxels[key]);
  };

  this.export = function(filename) {
    var data = [];
    this.appendString(data, "VOX ");
    this.appendUInt32(data, 150);
    this.appendString(data, "MAIN");
    this.appendUInt32(data, 0);
    this.appendUInt32(data, this.vcount * 4 + 0x434);

    this.appendString(data, "SIZE");
    this.appendUInt32(data, 12);
    this.appendUInt32(data, 0);
    this.appendUInt32(data, this.X);
    this.appendUInt32(data, this.Y);
    this.appendUInt32(data, this.Z);
    this.appendString(data, "XYZI");
    this.appendUInt32(data, 4 + this.vcount * 4);
    this.appendUInt32(data, 0);
    this.appendUInt32(data, this.vcount);
    for (var key in this.voxels) {
      this.appendVoxel(data, key);
    }
    this.appendString(data, "RGBA");
    this.appendUInt32(data, 0x400);
    this.appendUInt32(data, 0);
    for (var i = 0; i < 256; i++) {
        this.appendRGBA(data, this.palette[i]);
    }
    this.saveByteArray(new Uint8Array(data), filename);
    return;
  };

  this.saveByteArray = function (data, name_) {
    fs.writeFileSync(name_, data);
  };
}

// read my basic voxel format into .vox
// it is just an ascii list of xyz asserted voxels with no dimensions specified.
const read = fs.readFileSync(process.argv[2], 'utf8');
const parsed = read.replace(/\n$/g, '').split('\n').map(line => line.split(' ').map(Number));
console.log(parsed);
console.assert(parsed.every(v => v.length === 4), `not parsed.every(vox => vox.length === 4)`);
const max = [0,0,0];
parsed.map((v) => {
  for (let i = 0; i < 3; ++i) {
    if (v[i] > max[i]) {
      // console.log('accepted max, i, ii, max[i], v[i]:', i, ii, max[i], v[i]);
      max[i] = v[i];
    }
  }
});
console.log('max:', max);
const vox = new VOX(max[0] + 1, max[1] + 1, max[2] + 1);
parsed.map(v => {
  vox.setVoxel(v[0], v[1], v[2], v[3]);
});
vox.export(process.argv[2] + '.vox');

In particular I'm scratching my head over this code: this.vcount * 4 + 0x434 and I wonder if that is related.

I will also note that I have done checks where layer z=156 is missing and should be there, and I look in my output format and it is indeed present. Not only that, but the calculation for the z size in the node script also correctly sets the boundary size in MV to that value, just the voxels don't appear.

The ability to have the beautiful renders has already been invaluable in allowing me to troubleshoot voxel based algorithms, and I can only see myself using MV more and more for voxel based software going forward. It will only add to this tool's influence for developers to be able to rapidly create .vox exporters!

I would really appreciate a more thorough documentation of the file format representation. In particular it would be useful to be able to reliably import arbitrarily sized scenes, whether or not we have to manually break them down into 126^3 or whatever size models.

unphased commented 4 years ago

Note I also found https://github.com/aiekick/MagicaVoxel_File_Writer which looks even more promising, however this code currently has missing dependencies and cannot be built.

unphased commented 4 years ago

@ephtracy Could you check if this .vox x12.vox.zip

(corresponding to the 2nd screenshot) really has any voxels on coordinates z=59 thru z=66?

unphased commented 4 years ago

Meanwhile, I did get @aiekick’s .vox writer c++ building after he gave me the missing dependencies, so I’ll be doing tests using that to see if it has the same issue, as well as post a link to my fork of it (since the build steps are nontrivial).

thanks

unphased commented 4 years ago

I also expect things to change soon since I’ve been seeing ephtracy’s recent tweets; especially when the max model size will be jumping up to 256^3. I do consider it at least somewhat likely that newer builds would be able to load “legacy” vox files though!

unphased commented 4 years ago

I'll reopen this issue if I continue to see it with @aiekick's .vox writer. If anyone wants the code you can find it here: https://github.com/unphased/MagicaVoxel_File_Writer

It already works for me for enormous generated scenes, so I'd be surprised if it also has issues within a single 126^3 model. I think the bug comes from the janky js that I posted above.

mgerhardy commented 3 years ago

You could check out this: https://github.com/Eisenwave/voxel-io Or my code here: https://github.com/mgerhardy/engine/blob/master/src/modules/voxelformat/VoxFormat.cpp