├── .gitignore ├── .gitmodules ├── .haxerc ├── LICENSE ├── README.md ├── build-js.hxml ├── dist ├── format-vox.js └── index.js ├── format └── vox │ ├── VoxNodeTools.hx │ ├── VoxReader.hx │ ├── VoxTools.hx │ └── types │ ├── Color.hx │ ├── Dict.hx │ ├── Frame.hx │ ├── Model.hx │ ├── Node.hx │ ├── RotationBits.hx │ ├── Size.hx │ ├── Vox.hx │ └── Voxel.hx ├── haxelib.json ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sh-dave/haxe-format-vox/4626fc4e962e934a1e3ea064abc6cf19a9dc0cc2/.gitmodules -------------------------------------------------------------------------------- /.haxerc: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4.2.5", 3 | "resolveLibs": "scoped" 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 sh-dave 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # haxe-format-vox 2 | 3 | A reader for [MagicaVoxels](https://ephtracy.github.io)'s VOX files. Reads most of the v0.99 chunks including the world builder nodes. 4 | 5 | - [x] PACK - skipped / unused 6 | - [x] SIZE 7 | - [x] XYZI 8 | - [x] RGBA 9 | - [x] MATT - skipped / unused 10 | - [X] MATL 11 | - [x] nTRN 12 | - [x] nSHP 13 | - [x] nGRP 14 | - [ ] rOBJ - [missing specs](https://github.com/ephtracy/voxel-model/issues/19) 15 | - [ ] LAYR - [missing specs](https://github.com/ephtracy/voxel-model/issues/19) 16 | 17 | ## haxe 18 | 19 | ### usage in haxe 20 | ```haxe 21 | var data: BytesData = ...; 22 | format.vox.VoxReader.read(data, function( ?vox, ?err ) { 23 | if (err != null) { 24 | trace(err); 25 | return; 26 | } 27 | 28 | // use vox.models to get the meshes 29 | // use vox.nodeGraph to access to world builder nodes 30 | }); 31 | ``` 32 | 33 | ### haxe examples 34 | 35 | - old vox viewer - [code](https://github.com/sh-dave/haxe-format-vox-examples) - [html5 demo](https://sh-dave.github.io/haxe-format-vox-examples) 36 | - new vox viewer - [code](https://github.com/sh-dave/vox-viewer) 37 | 38 | ## javascript 39 | 40 | ### usage in javascript 41 | 42 | ```js 43 | const VoxReader = require('@sh-dave/format-vox').VoxReader; 44 | const data = ...some ArrayBuffer...; 45 | 46 | VoxReader.read(data, (vox, err) => { 47 | if (err) { 48 | console.error(err); 49 | return; 50 | } 51 | 52 | // use vox.models to get the meshes 53 | // use vox.nodeGraph to access to world builder nodes 54 | }); 55 | ``` 56 | 57 | - a more complete [usage example](https://github.com/sh-dave/haxe-format-vox-examples-js) 58 | 59 | ## javascript library build instructions 60 | 61 | All dependencies are simply installed via `npm` thanks to [lix](https://github.com/lix-pm/lix.client) 62 | 63 | ```shell 64 | npm install 65 | npx haxe build-js.hxml 66 | ``` 67 | -------------------------------------------------------------------------------- /build-js.hxml: -------------------------------------------------------------------------------- 1 | -dce full 2 | -D analyzer-optimize 3 | -D shallow-expose 4 | 5 | -js dist/format-vox.js 6 | 7 | format.vox.VoxReader 8 | format.vox.VoxTools 9 | format.vox.VoxNodeTools 10 | -------------------------------------------------------------------------------- /dist/format-vox.js: -------------------------------------------------------------------------------- 1 | var $hx_exports = typeof exports != "undefined" ? exports : typeof window != "undefined" ? window : typeof self != "undefined" ? self : this; 2 | (function ($global) { "use strict"; 3 | $hx_exports["format"] = $hx_exports["format"] || {}; 4 | $hx_exports["format"]["vox"] = $hx_exports["format"]["vox"] || {}; 5 | var $estr = function() { return js_Boot.__string_rec(this,''); },$hxEnums = $hxEnums || {},$_; 6 | function $extend(from, fields) { 7 | var proto = Object.create(from); 8 | for (var name in fields) proto[name] = fields[name]; 9 | if( fields.toString !== Object.prototype.toString ) proto.toString = fields.toString; 10 | return proto; 11 | } 12 | Math.__name__ = true; 13 | var Std = function() { }; 14 | Std.__name__ = true; 15 | Std.parseInt = function(x) { 16 | if(x != null) { 17 | var _g = 0; 18 | var _g1 = x.length; 19 | while(_g < _g1) { 20 | var i = _g++; 21 | var c = x.charCodeAt(i); 22 | if(c <= 8 || c >= 14 && c != 32 && c != 45) { 23 | var nc = x.charCodeAt(i + 1); 24 | var v = parseInt(x,nc == 120 || nc == 88 ? 16 : 10); 25 | if(isNaN(v)) { 26 | return null; 27 | } else { 28 | return v; 29 | } 30 | } 31 | } 32 | } 33 | return null; 34 | }; 35 | var format_vox_VoxNodeTools = $hx_exports["format"]["vox"]["VoxNodeTools"] = function() { }; 36 | format_vox_VoxNodeTools.__name__ = true; 37 | format_vox_VoxNodeTools.walkNodeGraph = function(vox,w) { 38 | w.beginGraph(vox); 39 | format_vox_VoxNodeTools.nodeWalker(vox.nodeGraph,w); 40 | w.endGraph(); 41 | }; 42 | format_vox_VoxNodeTools.nodeWalker = function(node,w) { 43 | if(node == null) { 44 | console.log("format/vox/VoxNodeTools.hx:30:","TODO (DK)"); 45 | } else { 46 | switch(node._hx_index) { 47 | case 0: 48 | w.onTransform(node.frames[0]); 49 | format_vox_VoxNodeTools.nodeWalker(node.child,w); 50 | break; 51 | case 1: 52 | var _g = node.children; 53 | w.beginGroup(node.attributes); 54 | var _g1 = 0; 55 | while(_g1 < _g.length) format_vox_VoxNodeTools.nodeWalker(_g[_g1++],w); 56 | w.endGroup(); 57 | break; 58 | case 2: 59 | w.onShape(node.attributes,node.models); 60 | break; 61 | } 62 | } 63 | }; 64 | var format_vox_VoxReader = $hx_exports["format"]["vox"]["VoxReader"] = function() { }; 65 | format_vox_VoxReader.__name__ = true; 66 | format_vox_VoxReader.read = function(data,then) { 67 | if(data == null) { 68 | then(null,"Invalid input"); 69 | return; 70 | } 71 | var input = new haxe_io_BytesInput(haxe_io_Bytes.ofData(data)); 72 | if(input.readString(4) != "VOX ") { 73 | then(null,"Expected \"VOX \" header"); 74 | return; 75 | } 76 | var version = input.readInt32(); 77 | if(version != 150 && version != 200) { 78 | then(null,"Unsupported version \"" + version + "\""); 79 | return; 80 | } 81 | var vox = new format_vox_types_Vox(); 82 | var _this = format_vox_VoxReader.get_DefaultPalette(); 83 | var f = format_vox_VoxTools.transformColor; 84 | var result = new Array(_this.length); 85 | var _g = 0; 86 | var _g1 = _this.length; 87 | while(_g < _g1) { 88 | var i = _g++; 89 | result[i] = f(_this[i]); 90 | } 91 | vox.palette = result; 92 | var nodeData = []; 93 | format_vox_VoxReader.readChunk(input,vox,nodeData,{ modelIndex : 0, sizeIndex : 0}); 94 | if(nodeData.length > 0) { 95 | vox.nodeGraph = format_vox_VoxReader.buildNodeGraph(vox,nodeData,0); 96 | } 97 | then(vox,null); 98 | }; 99 | format_vox_VoxReader.readChunk = function(input,vox,nodeData,state) { 100 | var chunkId = input.readString(4); 101 | var contentSize = input.readInt32(); 102 | var childBytes = input.readInt32(); 103 | switch(chunkId) { 104 | case "MAIN": 105 | break; 106 | case "MATL": 107 | var m_id = input.readInt32(); 108 | var _g = new haxe_ds_StringMap(); 109 | var _g1 = 0; 110 | var _g2 = input.readInt32(); 111 | while(_g1 < _g2) { 112 | ++_g1; 113 | var key = input.read(input.readInt32()).toString(); 114 | var value = input.read(input.readInt32()).toString(); 115 | _g.h[key] = value; 116 | } 117 | vox.materials[m_id] = _g; 118 | break; 119 | case "PACK": 120 | input.readInt32(); 121 | break; 122 | case "RGBA": 123 | var palette = format_vox_VoxReader.get_DefaultPalette(); 124 | var _g = 0; 125 | while(_g < 255) palette[_g++ + 1] = input.readInt32(); 126 | input.readInt32(); 127 | var f = format_vox_VoxTools.transformColor; 128 | var result = new Array(palette.length); 129 | var _g = 0; 130 | var _g1 = palette.length; 131 | while(_g < _g1) { 132 | var i = _g++; 133 | result[i] = f(palette[i]); 134 | } 135 | vox.palette = result; 136 | break; 137 | case "SIZE": 138 | vox.sizes[state.sizeIndex++] = new format_vox_types_Size(input.readInt32(),input.readInt32(),input.readInt32()); 139 | break; 140 | case "XYZI": 141 | var vox1 = vox.models; 142 | var tmp = state.modelIndex++; 143 | var _g = []; 144 | var _g1 = 0; 145 | var _g2 = input.readInt32(); 146 | while(_g1 < _g2) { 147 | ++_g1; 148 | _g.push(new format_vox_types_Voxel(input.readByte(),input.readByte(),input.readByte(),input.readByte())); 149 | } 150 | vox1[tmp] = _g; 151 | break; 152 | case "nGRP": 153 | var nodeId = input.readInt32(); 154 | var _g = new haxe_ds_StringMap(); 155 | var _g1 = 0; 156 | var _g2 = input.readInt32(); 157 | while(_g1 < _g2) { 158 | ++_g1; 159 | var key = input.read(input.readInt32()).toString(); 160 | var value = input.read(input.readInt32()).toString(); 161 | _g.h[key] = value; 162 | } 163 | var numChildren = input.readInt32(); 164 | var _g1 = []; 165 | var _g2 = 0; 166 | while(_g2 < numChildren) { 167 | ++_g2; 168 | _g1.push(input.readInt32()); 169 | } 170 | nodeData[nodeId] = format_vox__$VoxReader_NodeData.GroupNodeData(_g,_g1); 171 | break; 172 | case "nSHP": 173 | var nodeId = input.readInt32(); 174 | var _g = new haxe_ds_StringMap(); 175 | var _g1 = 0; 176 | var _g2 = input.readInt32(); 177 | while(_g1 < _g2) { 178 | ++_g1; 179 | var key = input.read(input.readInt32()).toString(); 180 | var value = input.read(input.readInt32()).toString(); 181 | _g.h[key] = value; 182 | } 183 | var numModels = input.readInt32(); 184 | var _g1 = []; 185 | var _g2 = 0; 186 | while(_g2 < numModels) { 187 | ++_g2; 188 | var _g3 = input.readInt32(); 189 | var _g4 = new haxe_ds_StringMap(); 190 | var _g5 = 0; 191 | var _g6 = input.readInt32(); 192 | while(_g5 < _g6) { 193 | ++_g5; 194 | var key = input.read(input.readInt32()).toString(); 195 | var value = input.read(input.readInt32()).toString(); 196 | _g4.h[key] = value; 197 | } 198 | _g1.push(new format_vox_types_Model(_g3,_g4)); 199 | } 200 | nodeData[nodeId] = format_vox__$VoxReader_NodeData.ShapeNodeData(_g,_g1); 201 | break; 202 | case "nTRN": 203 | var nodeId = input.readInt32(); 204 | var _g = new haxe_ds_StringMap(); 205 | var _g1 = 0; 206 | var _g2 = input.readInt32(); 207 | while(_g1 < _g2) { 208 | ++_g1; 209 | var key = input.read(input.readInt32()).toString(); 210 | var value = input.read(input.readInt32()).toString(); 211 | _g.h[key] = value; 212 | } 213 | var childNodeId = input.readInt32(); 214 | var reserved = input.readInt32(); 215 | var layerId = input.readInt32(); 216 | var numFrames = input.readInt32(); 217 | var _g1 = []; 218 | var _g2 = 0; 219 | while(_g2 < numFrames) { 220 | ++_g2; 221 | var _g3 = new haxe_ds_StringMap(); 222 | var _g4 = 0; 223 | var _g5 = input.readInt32(); 224 | while(_g4 < _g5) { 225 | ++_g4; 226 | var key = input.read(input.readInt32()).toString(); 227 | var value = input.read(input.readInt32()).toString(); 228 | _g3.h[key] = value; 229 | } 230 | _g1.push(_g3); 231 | } 232 | nodeData[nodeId] = format_vox__$VoxReader_NodeData.TransformNodeData(_g,childNodeId,reserved,layerId,_g1); 233 | break; 234 | default: 235 | input.read(contentSize); 236 | } 237 | var chunkSize = 12 + contentSize + childBytes; 238 | while(childBytes > 0) childBytes -= format_vox_VoxReader.readChunk(input,vox,nodeData,state); 239 | return chunkSize; 240 | }; 241 | format_vox_VoxReader.buildNodeGraph = function(vox,nodeData,nodeId) { 242 | var n = nodeData[nodeId]; 243 | switch(n._hx_index) { 244 | case 0: 245 | return format_vox_types_Node.Transform(n.attributes,n.reserved,n.layerId,n.frames,format_vox_VoxReader.buildNodeGraph(vox,nodeData,n.childNodeId)); 246 | case 1: 247 | var _g = n.attributes; 248 | var _g1 = n.children; 249 | var _g2 = []; 250 | var _g3 = 0; 251 | while(_g3 < _g1.length) _g2.push(format_vox_VoxReader.buildNodeGraph(vox,nodeData,_g1[_g3++])); 252 | return format_vox_types_Node.Group(_g,_g2); 253 | case 2: 254 | return format_vox_types_Node.Shape(n.attributes,n.models); 255 | } 256 | }; 257 | format_vox_VoxReader.readVoxel = function(input) { 258 | return new format_vox_types_Voxel(input.readByte(),input.readByte(),input.readByte(),input.readByte()); 259 | }; 260 | format_vox_VoxReader.readMaterial = function(input) { 261 | var tmp = input.readInt32(); 262 | var _g = new haxe_ds_StringMap(); 263 | var _g1 = 0; 264 | var _g2 = input.readInt32(); 265 | while(_g1 < _g2) { 266 | ++_g1; 267 | var key = input.read(input.readInt32()).toString(); 268 | var value = input.read(input.readInt32()).toString(); 269 | _g.h[key] = value; 270 | } 271 | return { id : tmp, props : _g}; 272 | }; 273 | format_vox_VoxReader.readDict = function(input) { 274 | var _g = new haxe_ds_StringMap(); 275 | var _g1 = 0; 276 | var _g2 = input.readInt32(); 277 | while(_g1 < _g2) { 278 | ++_g1; 279 | var key = input.read(input.readInt32()).toString(); 280 | var value = input.read(input.readInt32()).toString(); 281 | _g.h[key] = value; 282 | } 283 | return _g; 284 | }; 285 | format_vox_VoxReader.i32 = function(input) { 286 | return input.readInt32(); 287 | }; 288 | format_vox_VoxReader.byte = function(input) { 289 | return input.readByte(); 290 | }; 291 | format_vox_VoxReader.string = function(input) { 292 | return input.read(input.readInt32()).toString(); 293 | }; 294 | format_vox_VoxReader.get_DefaultPalette = function() { 295 | return [0,-1,-3342337,-6684673,-10027009,-13369345,-16711681,-13057,-3355393,-6697729,-10040065,-13382401,-16724737,-26113,-3368449,-6710785,-10053121,-13395457,-16737793,-39169,-3381505,-6723841,-10066177,-13408513,-16750849,-52225,-3394561,-6736897,-10079233,-13421569,-16763905,-65281,-3407617,-6749953,-10092289,-13434625,-16776961,-52,-3342388,-6684724,-10027060,-13369396,-16711732,-13108,-3355444,-6697780,-10040116,-13382452,-16724788,-26164,-3368500,-6710836,-10053172,-13395508,-16737844,-39220,-3381556,-6723892,-10066228,-13408564,-16750900,-52276,-3394612,-6736948,-10079284,-13421620,-16763956,-65332,-3407668,-6750004,-10092340,-13434676,-16777012,-103,-3342439,-6684775,-10027111,-13369447,-16711783,-13159,-3355495,-6697831,-10040167,-13382503,-16724839,-26215,-3368551,-6710887,-10053223,-13395559,-16737895,-39271,-3381607,-6723943,-10066279,-13408615,-16750951,-52327,-3394663,-6736999,-10079335,-13421671,-16764007,-65383,-3407719,-6750055,-10092391,-13434727,-16777063,-154,-3342490,-6684826,-10027162,-13369498,-16711834,-13210,-3355546,-6697882,-10040218,-13382554,-16724890,-26266,-3368602,-6710938,-10053274,-13395610,-16737946,-39322,-3381658,-6723994,-10066330,-13408666,-16751002,-52378,-3394714,-6737050,-10079386,-13421722,-16764058,-65434,-3407770,-6750106,-10092442,-13434778,-16777114,-205,-3342541,-6684877,-10027213,-13369549,-16711885,-13261,-3355597,-6697933,-10040269,-13382605,-16724941,-26317,-3368653,-6710989,-10053325,-13395661,-16737997,-39373,-3381709,-6724045,-10066381,-13408717,-16751053,-52429,-3394765,-6737101,-10079437,-13421773,-16764109,-65485,-3407821,-6750157,-10092493,-13434829,-16777165,-256,-3342592,-6684928,-10027264,-13369600,-16711936,-13312,-3355648,-6697984,-10040320,-13382656,-16724992,-26368,-3368704,-6711040,-10053376,-13395712,-16738048,-39424,-3381760,-6724096,-10066432,-13408768,-16751104,-52480,-3394816,-6737152,-10079488,-13421824,-16764160,-65536,-3407872,-6750208,-10092544,-13434880,-16776978,-16776995,-16777029,-16777046,-16777080,-16777097,-16777131,-16777148,-16777182,-16777199,-16716288,-16720640,-16729344,-16733696,-16742400,-16746752,-16755456,-16759808,-16768512,-16772864,-1179648,-2293760,-4521984,-5636096,-7864320,-8978432,-11206656,-12320768,-14548992,-15663104,-1118482,-2236963,-4473925,-5592406,-7829368,-8947849,-11184811,-12303292,-14540254,-15658735]; 296 | }; 297 | var format_vox__$VoxReader_NodeData = $hxEnums["format.vox._VoxReader.NodeData"] = { __ename__:true,__constructs__:null 298 | ,TransformNodeData: ($_=function(attributes,childNodeId,reserved,layerId,frames) { return {_hx_index:0,attributes:attributes,childNodeId:childNodeId,reserved:reserved,layerId:layerId,frames:frames,__enum__:"format.vox._VoxReader.NodeData",toString:$estr}; },$_._hx_name="TransformNodeData",$_.__params__ = ["attributes","childNodeId","reserved","layerId","frames"],$_) 299 | ,GroupNodeData: ($_=function(attributes,children) { return {_hx_index:1,attributes:attributes,children:children,__enum__:"format.vox._VoxReader.NodeData",toString:$estr}; },$_._hx_name="GroupNodeData",$_.__params__ = ["attributes","children"],$_) 300 | ,ShapeNodeData: ($_=function(attributes,models) { return {_hx_index:2,attributes:attributes,models:models,__enum__:"format.vox._VoxReader.NodeData",toString:$estr}; },$_._hx_name="ShapeNodeData",$_.__params__ = ["attributes","models"],$_) 301 | }; 302 | format_vox__$VoxReader_NodeData.__constructs__ = [format_vox__$VoxReader_NodeData.TransformNodeData,format_vox__$VoxReader_NodeData.GroupNodeData,format_vox__$VoxReader_NodeData.ShapeNodeData]; 303 | var format_vox_VoxTools = $hx_exports["format"]["vox"]["VoxTools"] = function() { }; 304 | format_vox_VoxTools.__name__ = true; 305 | format_vox_VoxTools.transformYZ = function(vox) { 306 | var _g = 0; 307 | var _g1 = vox.models.length; 308 | while(_g < _g1) { 309 | var i = _g++; 310 | var dy = vox.sizes[i].y; 311 | var _g2 = 0; 312 | var _g3 = vox.models[i]; 313 | while(_g2 < _g3.length) { 314 | var v = _g3[_g2]; 315 | ++_g2; 316 | var y = v.y; 317 | v.y = v.z; 318 | v.z = dy - 1 - y; 319 | } 320 | } 321 | }; 322 | format_vox_VoxTools.transformColor = function(color) { 323 | return new format_vox_types_Color(color & 255,color >> 8 & 255,color >> 16 & 255,color >> 24 & 255); 324 | }; 325 | format_vox_VoxTools.dictHasTranslation = function(d) { 326 | return d.h["_t"] != null; 327 | }; 328 | format_vox_VoxTools.getTranslationFromDict = function(d) { 329 | var t = d.h["_t"]; 330 | if(t == null) { 331 | return { x : 0, y : 0, z : 0}; 332 | } 333 | var split = t.split(" "); 334 | return { x : Std.parseInt(split[0]), y : Std.parseInt(split[1]), z : Std.parseInt(split[2])}; 335 | }; 336 | format_vox_VoxTools.dictHasRotation = function(d) { 337 | return d.h["_r"] != null; 338 | }; 339 | format_vox_VoxTools.getRotationFromDict = function(d) { 340 | var r = d.h["_r"]; 341 | if(r == null) { 342 | return { _00 : 1, _10 : 0, _20 : 0, _01 : 0, _11 : 1, _21 : 0, _02 : 0, _12 : 0, _22 : 1}; 343 | } 344 | var value = Std.parseInt(r); 345 | var s0 = (value & 16) == 0 ? 1 : -1; 346 | var s1 = (value & 32) == 0 ? 1 : -1; 347 | var s2 = (value & 64) == 0 ? 1 : -1; 348 | var r0 = (value & 1) + (value & 2); 349 | var r1 = (value >> 2 & 1) + (value >> 2 & 2); 350 | var r2; 351 | switch(r0) { 352 | case 0: 353 | switch(r1) { 354 | case 1: 355 | r2 = 2; 356 | break; 357 | case 2: 358 | r2 = 1; 359 | break; 360 | default: 361 | console.log("format/vox/VoxTools.hx:90:","missing r0;r1 match"); 362 | r2 = 0; 363 | } 364 | break; 365 | case 1: 366 | switch(r1) { 367 | case 0: 368 | r2 = 2; 369 | break; 370 | case 2: 371 | r2 = 0; 372 | break; 373 | default: 374 | console.log("format/vox/VoxTools.hx:90:","missing r0;r1 match"); 375 | r2 = 0; 376 | } 377 | break; 378 | case 2: 379 | switch(r1) { 380 | case 0: 381 | r2 = 1; 382 | break; 383 | case 1: 384 | r2 = 0; 385 | break; 386 | default: 387 | console.log("format/vox/VoxTools.hx:90:","missing r0;r1 match"); 388 | r2 = 0; 389 | } 390 | break; 391 | default: 392 | console.log("format/vox/VoxTools.hx:90:","missing r0;r1 match"); 393 | r2 = 0; 394 | } 395 | return { _00 : r0 == 0 ? s0 : 0, _10 : r0 == 1 ? s0 : 0, _20 : r0 == 2 ? s0 : 0, _01 : r1 == 0 ? s1 : 0, _11 : r1 == 1 ? s1 : 0, _21 : r1 == 2 ? s1 : 0, _02 : r2 == 0 ? s2 : 0, _12 : r2 == 1 ? s2 : 0, _22 : r2 == 2 ? s2 : 0}; 396 | }; 397 | var format_vox_types_Color = function(r,g,b,a) { 398 | this.r = r; 399 | this.g = g; 400 | this.b = b; 401 | this.a = a; 402 | }; 403 | format_vox_types_Color.__name__ = true; 404 | var format_vox_types_Model = function(modelId,attributes) { 405 | this.modelId = modelId; 406 | this.attributes = attributes; 407 | }; 408 | format_vox_types_Model.__name__ = true; 409 | var format_vox_types_Node = $hxEnums["format.vox.types.Node"] = { __ename__:true,__constructs__:null 410 | ,Transform: ($_=function(attributes,reserved,layerId,frames,child) { return {_hx_index:0,attributes:attributes,reserved:reserved,layerId:layerId,frames:frames,child:child,__enum__:"format.vox.types.Node",toString:$estr}; },$_._hx_name="Transform",$_.__params__ = ["attributes","reserved","layerId","frames","child"],$_) 411 | ,Group: ($_=function(attributes,children) { return {_hx_index:1,attributes:attributes,children:children,__enum__:"format.vox.types.Node",toString:$estr}; },$_._hx_name="Group",$_.__params__ = ["attributes","children"],$_) 412 | ,Shape: ($_=function(attributes,models) { return {_hx_index:2,attributes:attributes,models:models,__enum__:"format.vox.types.Node",toString:$estr}; },$_._hx_name="Shape",$_.__params__ = ["attributes","models"],$_) 413 | }; 414 | format_vox_types_Node.__constructs__ = [format_vox_types_Node.Transform,format_vox_types_Node.Group,format_vox_types_Node.Shape]; 415 | var format_vox_types_Size = function(x,y,z) { 416 | this.x = x; 417 | this.y = y; 418 | this.z = z; 419 | }; 420 | format_vox_types_Size.__name__ = true; 421 | var format_vox_types_Vox = function() { 422 | this.materials = []; 423 | this.models = []; 424 | this.sizes = []; 425 | }; 426 | format_vox_types_Vox.__name__ = true; 427 | var format_vox_types_Voxel = function(x,y,z,colorIndex) { 428 | this.x = x; 429 | this.y = y; 430 | this.z = z; 431 | this.colorIndex = colorIndex; 432 | }; 433 | format_vox_types_Voxel.__name__ = true; 434 | var haxe_Exception = function(message,previous,native) { 435 | Error.call(this,message); 436 | this.message = message; 437 | this.__previousException = previous; 438 | this.__nativeException = native != null ? native : this; 439 | }; 440 | haxe_Exception.__name__ = true; 441 | haxe_Exception.caught = function(value) { 442 | if(((value) instanceof haxe_Exception)) { 443 | return value; 444 | } else if(((value) instanceof Error)) { 445 | return new haxe_Exception(value.message,null,value); 446 | } else { 447 | return new haxe_ValueException(value,null,value); 448 | } 449 | }; 450 | haxe_Exception.thrown = function(value) { 451 | if(((value) instanceof haxe_Exception)) { 452 | return value.get_native(); 453 | } else if(((value) instanceof Error)) { 454 | return value; 455 | } else { 456 | var e = new haxe_ValueException(value); 457 | return e; 458 | } 459 | }; 460 | haxe_Exception.__super__ = Error; 461 | haxe_Exception.prototype = $extend(Error.prototype,{ 462 | unwrap: function() { 463 | return this.__nativeException; 464 | } 465 | ,toString: function() { 466 | return this.get_message(); 467 | } 468 | ,get_message: function() { 469 | return this.message; 470 | } 471 | ,get_native: function() { 472 | return this.__nativeException; 473 | } 474 | }); 475 | var haxe_ValueException = function(value,previous,native) { 476 | haxe_Exception.call(this,String(value),previous,native); 477 | this.value = value; 478 | }; 479 | haxe_ValueException.__name__ = true; 480 | haxe_ValueException.__super__ = haxe_Exception; 481 | haxe_ValueException.prototype = $extend(haxe_Exception.prototype,{ 482 | unwrap: function() { 483 | return this.value; 484 | } 485 | }); 486 | var haxe_ds_StringMap = function() { 487 | this.h = Object.create(null); 488 | }; 489 | haxe_ds_StringMap.__name__ = true; 490 | var haxe_exceptions_PosException = function(message,previous,pos) { 491 | haxe_Exception.call(this,message,previous); 492 | if(pos == null) { 493 | this.posInfos = { fileName : "(unknown)", lineNumber : 0, className : "(unknown)", methodName : "(unknown)"}; 494 | } else { 495 | this.posInfos = pos; 496 | } 497 | }; 498 | haxe_exceptions_PosException.__name__ = true; 499 | haxe_exceptions_PosException.__super__ = haxe_Exception; 500 | haxe_exceptions_PosException.prototype = $extend(haxe_Exception.prototype,{ 501 | toString: function() { 502 | return "" + haxe_Exception.prototype.toString.call(this) + " in " + this.posInfos.className + "." + this.posInfos.methodName + " at " + this.posInfos.fileName + ":" + this.posInfos.lineNumber; 503 | } 504 | }); 505 | var haxe_exceptions_NotImplementedException = function(message,previous,pos) { 506 | if(message == null) { 507 | message = "Not implemented"; 508 | } 509 | haxe_exceptions_PosException.call(this,message,previous,pos); 510 | }; 511 | haxe_exceptions_NotImplementedException.__name__ = true; 512 | haxe_exceptions_NotImplementedException.__super__ = haxe_exceptions_PosException; 513 | haxe_exceptions_NotImplementedException.prototype = $extend(haxe_exceptions_PosException.prototype,{ 514 | }); 515 | var haxe_io_Bytes = function(data) { 516 | this.length = data.byteLength; 517 | this.b = new Uint8Array(data); 518 | this.b.bufferValue = data; 519 | data.hxBytes = this; 520 | data.bytes = this.b; 521 | }; 522 | haxe_io_Bytes.__name__ = true; 523 | haxe_io_Bytes.ofData = function(b) { 524 | var hb = b.hxBytes; 525 | if(hb != null) { 526 | return hb; 527 | } 528 | return new haxe_io_Bytes(b); 529 | }; 530 | haxe_io_Bytes.prototype = { 531 | getString: function(pos,len,encoding) { 532 | if(pos < 0 || len < 0 || pos + len > this.length) { 533 | throw haxe_Exception.thrown(haxe_io_Error.OutsideBounds); 534 | } 535 | if(encoding == null) { 536 | encoding = haxe_io_Encoding.UTF8; 537 | } 538 | var s = ""; 539 | var b = this.b; 540 | var i = pos; 541 | var max = pos + len; 542 | switch(encoding._hx_index) { 543 | case 0: 544 | while(i < max) { 545 | var c = b[i++]; 546 | if(c < 128) { 547 | if(c == 0) { 548 | break; 549 | } 550 | s += String.fromCodePoint(c); 551 | } else if(c < 224) { 552 | var code = (c & 63) << 6 | b[i++] & 127; 553 | s += String.fromCodePoint(code); 554 | } else if(c < 240) { 555 | var code1 = (c & 31) << 12 | (b[i++] & 127) << 6 | b[i++] & 127; 556 | s += String.fromCodePoint(code1); 557 | } else { 558 | var u = (c & 15) << 18 | (b[i++] & 127) << 12 | (b[i++] & 127) << 6 | b[i++] & 127; 559 | s += String.fromCodePoint(u); 560 | } 561 | } 562 | break; 563 | case 1: 564 | while(i < max) { 565 | var c = b[i++] | b[i++] << 8; 566 | s += String.fromCodePoint(c); 567 | } 568 | break; 569 | } 570 | return s; 571 | } 572 | ,toString: function() { 573 | return this.getString(0,this.length); 574 | } 575 | }; 576 | var haxe_io_Input = function() { }; 577 | haxe_io_Input.__name__ = true; 578 | haxe_io_Input.prototype = { 579 | readByte: function() { 580 | throw new haxe_exceptions_NotImplementedException(null,null,{ fileName : "haxe/io/Input.hx", lineNumber : 53, className : "haxe.io.Input", methodName : "readByte"}); 581 | } 582 | ,readBytes: function(s,pos,len) { 583 | var k = len; 584 | var b = s.b; 585 | if(pos < 0 || len < 0 || pos + len > s.length) { 586 | throw haxe_Exception.thrown(haxe_io_Error.OutsideBounds); 587 | } 588 | try { 589 | while(k > 0) { 590 | b[pos] = this.readByte(); 591 | ++pos; 592 | --k; 593 | } 594 | } catch( _g ) { 595 | if(!((haxe_Exception.caught(_g).unwrap()) instanceof haxe_io_Eof)) { 596 | throw _g; 597 | } 598 | } 599 | return len - k; 600 | } 601 | ,readFullBytes: function(s,pos,len) { 602 | while(len > 0) { 603 | var k = this.readBytes(s,pos,len); 604 | if(k == 0) { 605 | throw haxe_Exception.thrown(haxe_io_Error.Blocked); 606 | } 607 | pos += k; 608 | len -= k; 609 | } 610 | } 611 | ,read: function(nbytes) { 612 | var s = new haxe_io_Bytes(new ArrayBuffer(nbytes)); 613 | var p = 0; 614 | while(nbytes > 0) { 615 | var k = this.readBytes(s,p,nbytes); 616 | if(k == 0) { 617 | throw haxe_Exception.thrown(haxe_io_Error.Blocked); 618 | } 619 | p += k; 620 | nbytes -= k; 621 | } 622 | return s; 623 | } 624 | ,readInt32: function() { 625 | var ch1 = this.readByte(); 626 | var ch2 = this.readByte(); 627 | var ch3 = this.readByte(); 628 | var ch4 = this.readByte(); 629 | if(this.bigEndian) { 630 | return ch4 | ch3 << 8 | ch2 << 16 | ch1 << 24; 631 | } else { 632 | return ch1 | ch2 << 8 | ch3 << 16 | ch4 << 24; 633 | } 634 | } 635 | ,readString: function(len,encoding) { 636 | var b = new haxe_io_Bytes(new ArrayBuffer(len)); 637 | this.readFullBytes(b,0,len); 638 | return b.getString(0,len,encoding); 639 | } 640 | }; 641 | var haxe_io_BytesInput = function(b,pos,len) { 642 | if(pos == null) { 643 | pos = 0; 644 | } 645 | if(len == null) { 646 | len = b.length - pos; 647 | } 648 | if(pos < 0 || len < 0 || pos + len > b.length) { 649 | throw haxe_Exception.thrown(haxe_io_Error.OutsideBounds); 650 | } 651 | this.b = b.b; 652 | this.pos = pos; 653 | this.len = len; 654 | this.totlen = len; 655 | }; 656 | haxe_io_BytesInput.__name__ = true; 657 | haxe_io_BytesInput.__super__ = haxe_io_Input; 658 | haxe_io_BytesInput.prototype = $extend(haxe_io_Input.prototype,{ 659 | readByte: function() { 660 | if(this.len == 0) { 661 | throw haxe_Exception.thrown(new haxe_io_Eof()); 662 | } 663 | this.len--; 664 | return this.b[this.pos++]; 665 | } 666 | ,readBytes: function(buf,pos,len) { 667 | if(pos < 0 || len < 0 || pos + len > buf.length) { 668 | throw haxe_Exception.thrown(haxe_io_Error.OutsideBounds); 669 | } 670 | if(this.len == 0 && len > 0) { 671 | throw haxe_Exception.thrown(new haxe_io_Eof()); 672 | } 673 | if(this.len < len) { 674 | len = this.len; 675 | } 676 | var b1 = this.b; 677 | var b2 = buf.b; 678 | var _g = 0; 679 | var _g1 = len; 680 | while(_g < _g1) { 681 | var i = _g++; 682 | b2[pos + i] = b1[this.pos + i]; 683 | } 684 | this.pos += len; 685 | this.len -= len; 686 | return len; 687 | } 688 | }); 689 | var haxe_io_Encoding = $hxEnums["haxe.io.Encoding"] = { __ename__:true,__constructs__:null 690 | ,UTF8: {_hx_name:"UTF8",_hx_index:0,__enum__:"haxe.io.Encoding",toString:$estr} 691 | ,RawNative: {_hx_name:"RawNative",_hx_index:1,__enum__:"haxe.io.Encoding",toString:$estr} 692 | }; 693 | haxe_io_Encoding.__constructs__ = [haxe_io_Encoding.UTF8,haxe_io_Encoding.RawNative]; 694 | var haxe_io_Eof = function() { 695 | }; 696 | haxe_io_Eof.__name__ = true; 697 | haxe_io_Eof.prototype = { 698 | toString: function() { 699 | return "Eof"; 700 | } 701 | }; 702 | var haxe_io_Error = $hxEnums["haxe.io.Error"] = { __ename__:true,__constructs__:null 703 | ,Blocked: {_hx_name:"Blocked",_hx_index:0,__enum__:"haxe.io.Error",toString:$estr} 704 | ,Overflow: {_hx_name:"Overflow",_hx_index:1,__enum__:"haxe.io.Error",toString:$estr} 705 | ,OutsideBounds: {_hx_name:"OutsideBounds",_hx_index:2,__enum__:"haxe.io.Error",toString:$estr} 706 | ,Custom: ($_=function(e) { return {_hx_index:3,e:e,__enum__:"haxe.io.Error",toString:$estr}; },$_._hx_name="Custom",$_.__params__ = ["e"],$_) 707 | }; 708 | haxe_io_Error.__constructs__ = [haxe_io_Error.Blocked,haxe_io_Error.Overflow,haxe_io_Error.OutsideBounds,haxe_io_Error.Custom]; 709 | var haxe_iterators_ArrayIterator = function(array) { 710 | this.current = 0; 711 | this.array = array; 712 | }; 713 | haxe_iterators_ArrayIterator.__name__ = true; 714 | haxe_iterators_ArrayIterator.prototype = { 715 | hasNext: function() { 716 | return this.current < this.array.length; 717 | } 718 | ,next: function() { 719 | return this.array[this.current++]; 720 | } 721 | }; 722 | var js_Boot = function() { }; 723 | js_Boot.__name__ = true; 724 | js_Boot.__string_rec = function(o,s) { 725 | if(o == null) { 726 | return "null"; 727 | } 728 | if(s.length >= 5) { 729 | return "<...>"; 730 | } 731 | var t = typeof(o); 732 | if(t == "function" && (o.__name__ || o.__ename__)) { 733 | t = "object"; 734 | } 735 | switch(t) { 736 | case "function": 737 | return ""; 738 | case "object": 739 | if(o.__enum__) { 740 | var e = $hxEnums[o.__enum__]; 741 | var con = e.__constructs__[o._hx_index]; 742 | var n = con._hx_name; 743 | if(con.__params__) { 744 | s = s + "\t"; 745 | return n + "(" + ((function($this) { 746 | var $r; 747 | var _g = []; 748 | { 749 | var _g1 = 0; 750 | var _g2 = con.__params__; 751 | while(true) { 752 | if(!(_g1 < _g2.length)) { 753 | break; 754 | } 755 | var p = _g2[_g1]; 756 | _g1 = _g1 + 1; 757 | _g.push(js_Boot.__string_rec(o[p],s)); 758 | } 759 | } 760 | $r = _g; 761 | return $r; 762 | }(this))).join(",") + ")"; 763 | } else { 764 | return n; 765 | } 766 | } 767 | if(((o) instanceof Array)) { 768 | var str = "["; 769 | s += "\t"; 770 | var _g = 0; 771 | var _g1 = o.length; 772 | while(_g < _g1) { 773 | var i = _g++; 774 | str += (i > 0 ? "," : "") + js_Boot.__string_rec(o[i],s); 775 | } 776 | str += "]"; 777 | return str; 778 | } 779 | var tostr; 780 | try { 781 | tostr = o.toString; 782 | } catch( _g ) { 783 | return "???"; 784 | } 785 | if(tostr != null && tostr != Object.toString && typeof(tostr) == "function") { 786 | var s2 = o.toString(); 787 | if(s2 != "[object Object]") { 788 | return s2; 789 | } 790 | } 791 | var str = "{\n"; 792 | s += "\t"; 793 | var hasp = o.hasOwnProperty != null; 794 | var k = null; 795 | for( k in o ) { 796 | if(hasp && !o.hasOwnProperty(k)) { 797 | continue; 798 | } 799 | if(k == "prototype" || k == "__class__" || k == "__super__" || k == "__interfaces__" || k == "__properties__") { 800 | continue; 801 | } 802 | if(str.length != 2) { 803 | str += ", \n"; 804 | } 805 | str += s + k + " : " + js_Boot.__string_rec(o[k],s); 806 | } 807 | s = s.substring(1); 808 | str += "\n" + s + "}"; 809 | return str; 810 | case "string": 811 | return o; 812 | default: 813 | return String(o); 814 | } 815 | }; 816 | if( String.fromCodePoint == null ) String.fromCodePoint = function(c) { return c < 0x10000 ? String.fromCharCode(c) : String.fromCharCode((c>>10)+0xD7C0)+String.fromCharCode((c&0x3FF)+0xDC00); } 817 | String.__name__ = true; 818 | Array.__name__ = true; 819 | js_Boot.__toStr = ({ }).toString; 820 | format_vox_VoxTools.TranslationKey = "_t"; 821 | format_vox_VoxTools.RotationKey = "_r"; 822 | })({}); 823 | var format = $hx_exports["format"]; 824 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./format-vox.js').format.vox; 2 | -------------------------------------------------------------------------------- /format/vox/VoxNodeTools.hx: -------------------------------------------------------------------------------- 1 | package format.vox; 2 | 3 | import format.vox.types.Dict; 4 | import format.vox.types.Model; 5 | import format.vox.types.Node; 6 | import format.vox.types.Vox; 7 | 8 | typedef Walker = { 9 | function beginGraph( vox: Vox ) : Void; 10 | function endGraph() : Void; 11 | 12 | function beginGroup( att: Dict ) : Void; 13 | function endGroup() : Void; 14 | 15 | function onTransform( att: Dict ) : Void; 16 | function onShape( att: Dict, models: Array ) : Void; 17 | } 18 | 19 | @:expose @:keep 20 | class VoxNodeTools { 21 | public static function walkNodeGraph( vox: Vox, w: Walker ) { 22 | w.beginGraph(vox); 23 | nodeWalker(vox.nodeGraph, w); 24 | w.endGraph(); 25 | } 26 | 27 | static function nodeWalker( node: Node, w: Walker ) { 28 | return switch node { 29 | case null: // TODO (DK) just for dummy scenes without node graph, should be removed 30 | trace('TODO (DK)'); 31 | case Transform(att, res, lyr, frames, child): 32 | w.onTransform(frames[0]); 33 | nodeWalker(child, w); 34 | case Group(att, children): 35 | w.beginGroup(att); 36 | for (child in children) { 37 | nodeWalker(child, w); 38 | } 39 | w.endGroup(); 40 | case Shape(att, models): 41 | w.onShape(att, models); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /format/vox/VoxReader.hx: -------------------------------------------------------------------------------- 1 | package format.vox; 2 | 3 | import format.vox.types.*; 4 | import haxe.io.Bytes; 5 | import haxe.io.BytesData; 6 | import haxe.io.BytesInput; 7 | import haxe.io.Input; 8 | 9 | typedef VoxError = Any; 10 | 11 | @:expose @:keep 12 | class VoxReader { 13 | public static function read( data: BytesData, then: ?Vox -> ?VoxError -> Void ) { 14 | if (data == null) { 15 | then(null, 'Invalid input'); 16 | return; 17 | } 18 | 19 | var input = new BytesInput(Bytes.ofData(data)); 20 | 21 | if (input.readString(4) != 'VOX ') { 22 | then(null, 'Expected "VOX " header'); 23 | return; 24 | } 25 | 26 | var version = i32(input); 27 | 28 | if (version != 150 && version != 200) { 29 | then(null, 'Unsupported version "$version"'); 30 | return; 31 | } 32 | 33 | var vox = new Vox(); 34 | vox.palette = DefaultPalette.map(VoxTools.transformColor); 35 | var state = { modelIndex: 0, sizeIndex: 0 } 36 | var nodeData: Array = []; 37 | 38 | readChunk(input, vox, nodeData, state); 39 | 40 | if (nodeData.length > 0) { 41 | vox.nodeGraph = buildNodeGraph(vox, nodeData, 0); 42 | } 43 | 44 | then(vox, null); 45 | } 46 | 47 | static function readChunk( input: Input, vox: Vox, nodeData: Array, state: State ) : Int { 48 | var chunkId = input.readString(4); 49 | #if haxe_format_vox_trace trace('chunk id = "${chunkId}"'); #end 50 | var contentSize = i32(input); 51 | #if haxe_format_vox_trace trace('content size = "${contentSize}"'); #end 52 | var childBytes = i32(input); 53 | #if haxe_format_vox_trace trace('child bytes = "${childBytes}"'); #end 54 | 55 | switch chunkId { 56 | case 'MAIN': 57 | case 'PACK': 58 | var numModels = i32(input); 59 | case 'SIZE': 60 | vox.sizes[state.sizeIndex++] = { 61 | x :i32(input), 62 | y: i32(input), 63 | z: i32(input) 64 | } 65 | case 'XYZI': 66 | vox.models[state.modelIndex++] = [for (c in 0...i32(input)) readVoxel(input)]; 67 | case 'RGBA': 68 | var palette = DefaultPalette; 69 | 70 | for (i in 0...255) { 71 | palette[i + 1] = input.readInt32(); 72 | } 73 | 74 | input.readInt32(); 75 | 76 | if (vox.palette != null) { 77 | #if haxe_format_vox_trace trace('vox.palette is already assigned'); #end 78 | } 79 | 80 | vox.palette = palette.map(VoxTools.transformColor); 81 | case 'MATL': 82 | var m = readMaterial(input); 83 | vox.materials[m.id] = m.props; 84 | case 'nTRN': 85 | var nodeId = i32(input); // 0 is root? 86 | var attributes = readDict(input); 87 | var childNodeId = i32(input); 88 | var reserved = i32(input); 89 | var layerId = i32(input); 90 | var numFrames = i32(input); 91 | var frames = [for (i in 0...numFrames) readDict(input)]; 92 | #if haxe_format_vox_trace trace('$chunkId $nodeId $attributes $childNodeId $reserved $layerId $numFrames $frames'); #end 93 | nodeData[nodeId] = TransformNodeData(attributes, childNodeId, reserved, layerId, frames); 94 | case 'nSHP': 95 | var nodeId = i32(input); 96 | var attributes = readDict(input); 97 | var numModels = i32(input); 98 | var models: Array = [for (i in 0...numModels) { modelId: i32(input), attributes: readDict(input) }]; 99 | #if haxe_format_vox_trace trace('$chunkId $nodeId $attributes $numModels $models'); #end 100 | nodeData[nodeId] = ShapeNodeData(attributes, models); 101 | case 'nGRP': 102 | var nodeId = i32(input); 103 | var attributes = readDict(input); 104 | var numChildren = i32(input); 105 | var children = [for (i in 0...numChildren) i32(input)]; 106 | #if haxe_format_vox_trace trace('$chunkId $nodeId $attributes $numChildren $children'); #end 107 | nodeData[nodeId] = GroupNodeData(attributes, children); 108 | // case 'rOBJ': // TODO (DK) not defined in the docs (https://github.com/ephtracy/voxel-model/issues/19#issuecomment-380194831) 109 | // #if haxe_format_vox_trace trace('TODO (DK) chunk "${chunkId}" ($contentSize bytes)'); #end 110 | // input.read(contentSize); 111 | // case 'LAYR': // TODO (DK) not defined in the docs (https://github.com/ephtracy/voxel-model/issues/19#issuecomment-380194831) 112 | // #if haxe_format_vox_trace trace('TODO (DK) chunk "${chunkId}" ($contentSize bytes)'); #end 113 | // input.read(contentSize); 114 | default: 115 | #if haxe_format_vox_trace trace('skipping unsupported chunk "${chunkId}" ($contentSize bytes)'); #end 116 | input.read(contentSize); 117 | } 118 | 119 | var chunkSize = 4 + 4 + 4 + contentSize + childBytes; 120 | 121 | while (childBytes > 0) { 122 | childBytes -= readChunk(input, vox, nodeData, state); 123 | } 124 | 125 | return chunkSize; 126 | } 127 | 128 | static function buildNodeGraph( vox: Vox, nodeData: Array, nodeId: Int ) : Node { 129 | var n = nodeData[nodeId]; 130 | 131 | return switch n { 132 | case TransformNodeData(att, childNodeId, r, l, fr): 133 | Transform(att, r, l, fr, buildNodeGraph(vox, nodeData, childNodeId)); 134 | case GroupNodeData(att, children): 135 | Group(att, [for (childId in children) buildNodeGraph(vox, nodeData, childId)]); 136 | case ShapeNodeData(att, models): 137 | Shape(att, models); 138 | } 139 | } 140 | 141 | static inline function readVoxel( input: Input ) : Voxel 142 | return { x: byte(input), y: byte(input), z: byte(input), colorIndex: byte(input) } 143 | 144 | static inline function readMaterial( input: Input ) : { id: Int, props: Dict } 145 | return { 146 | id: i32(input), 147 | props: readDict(input), 148 | } 149 | 150 | static inline function readDict( input: Input ) : Dict 151 | return [for (i in 0...i32(input)) string(input) => string(input)]; 152 | 153 | static inline function i32( input: Input ) : Int 154 | return input.readInt32(); 155 | 156 | static inline function byte( input: Input ) : Int 157 | return input.readByte(); 158 | 159 | static inline function string( input: Input ) : String 160 | return input.read(i32(input)).toString(); 161 | 162 | // default palette from https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt 163 | public static var DefaultPalette(get, null): Array; 164 | 165 | static function get_DefaultPalette() return [ 166 | 0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff, 167 | 0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff, 168 | 0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc, 169 | 0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc, 170 | 0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99, 171 | 0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, 0xffcc6699, 0xff996699, 0xff666699, 0xff336699, 172 | 0xff006699, 0xffff3399, 0xffcc3399, 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66, 173 | 0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, 0xffff6666, 174 | 0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, 0xff990066, 0xff660066, 0xff330066, 175 | 0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933, 176 | 0xff669933, 0xff339933, 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033, 177 | 0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00, 178 | 0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300, 179 | 0xff663300, 0xff333300, 0xff003300, 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044, 180 | 0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000, 181 | 0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111, 182 | ]; 183 | } 184 | 185 | private typedef State = { 186 | modelIndex: Int, 187 | sizeIndex: Int, 188 | } 189 | 190 | private enum NodeData { 191 | TransformNodeData( attributes: Dict, childNodeId: Int, reserved: Int, layerId: Int, frames: Array ); 192 | GroupNodeData( attributes: Dict, children: Array ); 193 | ShapeNodeData( attributes: Dict, models: Array ); 194 | } 195 | -------------------------------------------------------------------------------- /format/vox/VoxTools.hx: -------------------------------------------------------------------------------- 1 | package format.vox; 2 | 3 | import format.vox.types.*; 4 | 5 | typedef Translation = { 6 | x: Float, 7 | y: Float, 8 | z: Float, 9 | } 10 | 11 | typedef Rotation = { 12 | _00: Float, _10: Float, _20: Float, // , 0 13 | _01: Float, _11: Float, _21: Float, // , 0 14 | _02: Float, _12: Float, _22: Float, // , 0 15 | // 0, 0, 0, 1 16 | } 17 | 18 | @:expose @:keep 19 | class VoxTools { 20 | public static function transformYZ( vox: Vox ) { 21 | for (i in 0...vox.models.length) { 22 | var dy = vox.sizes[i].y; 23 | 24 | for (v in vox.models[i]) { 25 | var y = v.y; 26 | var z = v.z; 27 | v.y = z; 28 | v.z = dy - 1 - y; 29 | } 30 | } 31 | } 32 | 33 | public static function transformColor( color: Int ) : Color return { 34 | r : color & 0xff, 35 | g : (color >> 8) & 0xff, 36 | b : (color >> 16) & 0xff, 37 | a : (color >> 24) & 0xff, 38 | } 39 | 40 | static inline var TranslationKey = '_t'; 41 | 42 | public static function dictHasTranslation( d: Dict ) : Bool 43 | return d.get(TranslationKey) != null; 44 | 45 | public static function getTranslationFromDict( d: Dict ) : Translation { 46 | var t = d.get(TranslationKey); 47 | 48 | if (t == null) { 49 | return { x: 0, y: 0, z: 0 } 50 | } 51 | 52 | var split = t.split(' '); 53 | var x = Std.parseInt(split[0]); 54 | var y = Std.parseInt(split[1]); 55 | var z = Std.parseInt(split[2]); 56 | 57 | return { x: x, y: y, z: z } 58 | } 59 | 60 | static inline var RotationKey = '_r'; 61 | 62 | public static function dictHasRotation( d: Dict ) : Bool 63 | return d.get(RotationKey) != null; 64 | 65 | public static function getRotationFromDict( d: Dict ) : Rotation { 66 | var r = d.get(RotationKey); 67 | 68 | if (r == null) { 69 | return { 70 | _00: 1, _10: 0, _20: 0, 71 | _01: 0, _11: 1, _21: 0, 72 | _02: 0, _12: 0, _22: 1, 73 | } 74 | } 75 | 76 | var value: RotationBits = Std.parseInt(r); 77 | 78 | var s0 = value.sign0; 79 | var s1 = value.sign1; 80 | var s2 = value.sign2; 81 | var r0 = value.r0; 82 | var r1 = value.r1; 83 | var r2 = switch [r0, r1] { 84 | case [0, 1]: 2; 85 | case [1, 0]: 2; 86 | case [0, 2]: 1; 87 | case [2, 0]: 1; 88 | case [1, 2]: 0; 89 | case [2, 1]: 0; 90 | case _: trace('missing r0;r1 match'); 0; 91 | } 92 | 93 | return { 94 | _00: r0 == 0 ? s0 : 0, _10: r0 == 1 ? s0 : 0, _20: r0 == 2 ? s0 : 0, 95 | _01: r1 == 0 ? s1 : 0, _11: r1 == 1 ? s1 : 0, _21: r1 == 2 ? s1 : 0, 96 | _02: r2 == 0 ? s2 : 0, _12: r2 == 1 ? s2 : 0, _22: r2 == 2 ? s2 : 0, 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /format/vox/types/Color.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | @:structInit 4 | class Color { 5 | public var r: Int; 6 | public var g: Int; 7 | public var b: Int; 8 | public var a: Int; 9 | } 10 | -------------------------------------------------------------------------------- /format/vox/types/Dict.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | typedef Dict = Map; 4 | -------------------------------------------------------------------------------- /format/vox/types/Frame.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | typedef Frame = Dict; 4 | -------------------------------------------------------------------------------- /format/vox/types/Model.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | @:structInit 4 | class Model { 5 | public var modelId: Int; 6 | public var attributes: Dict; 7 | } 8 | -------------------------------------------------------------------------------- /format/vox/types/Node.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | enum Node { 4 | Transform( attributes: Dict, reserved: Int, layerId: Int, frames: Array, child: Node ); 5 | Group( attributes: Dict, children: Array ); 6 | Shape( attributes: Dict, models: Array ); 7 | } 8 | 9 | // T -> G / S 10 | // G -> T 11 | -------------------------------------------------------------------------------- /format/vox/types/RotationBits.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | abstract RotationBits(Int) from Int { 4 | public inline function new( r: Int ) 5 | this = r; 6 | 7 | public var r0(get, never): Int; 8 | public var r1(get, never): Int; 9 | 10 | public var sign0(get, never): Int; 11 | public var sign1(get, never): Int; 12 | public var sign2(get, never): Int; 13 | 14 | inline function get_r0() : Int { 15 | var a = (this & 1 << 0); 16 | var b = (this & 1 << 1); 17 | return a + b; 18 | } 19 | 20 | inline function get_r1() : Int { 21 | var a = (this >> 2 & 1 << 0); 22 | var b = (this >> 2 & 1 << 1); 23 | return a + b; 24 | } 25 | 26 | inline function get_sign0() : Int { 27 | return this & (1 << 4) == 0 ? 1 : -1; 28 | } 29 | 30 | inline function get_sign1() : Int { 31 | return this & (1 << 5) == 0 ? 1 : -1; 32 | } 33 | 34 | inline function get_sign2() : Int { 35 | return this & (1 << 6) == 0 ? 1 : -1; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /format/vox/types/Size.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | @:structInit 4 | class Size { 5 | public var x: Int; 6 | public var y: Int; 7 | public var z: Int; // gravity direction 8 | } 9 | -------------------------------------------------------------------------------- /format/vox/types/Vox.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | class Vox { 4 | public var sizes: Array = []; 5 | public var models: Array> = []; 6 | public var palette: Array; 7 | public var materials: Array = []; 8 | 9 | // public var nodeData: Array = []; 10 | public var nodeGraph: Node; 11 | 12 | public function new() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /format/vox/types/Voxel.hx: -------------------------------------------------------------------------------- 1 | package format.vox.types; 2 | 3 | @:structInit 4 | class Voxel { 5 | public var x: Int; 6 | public var y: Int; 7 | public var z: Int; 8 | public var colorIndex: Int; 9 | // public var color: Color; 10 | } 11 | -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "haxe-format-vox", 3 | "license": "zlib", 4 | "url": "https://github.com/sh-dave/haxe-format-vox", 5 | "contributors": [ 6 | "sh-dave", 7 | "Benjythebee" 8 | ], 9 | "version": "0.1.6" 10 | } 11 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sh-dave/format-vox", 3 | "version": "0.1.6", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "lix": { 8 | "version": "15.12.0", 9 | "resolved": "https://registry.npmjs.org/lix/-/lix-15.12.0.tgz", 10 | "integrity": "sha512-FA36oCl+M+3Of8L4eErXw7tAHGOjqEC4IgEvH6oPDsiYd4yN6XpzZGcbLuIyu4PiztjOrr1TKJnpwi32qb2ddw==", 11 | "dev": true 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sh-dave/format-vox", 3 | "version": "0.1.6", 4 | "description": "A reader for MagicaVoxel's VOX file format", 5 | "devDependencies": { 6 | "lix": "^15.12.0" 7 | }, 8 | "main": "./dist/index.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/sh-dave/haxe-format-vox.git" 12 | }, 13 | "keywords": [ 14 | "VOX", 15 | "magicavoxel" 16 | ], 17 | "bugs": { 18 | "url": "https://github.com/sh-dave/haxe-format-vox/issues" 19 | }, 20 | "license": "zlib" 21 | } 22 | --------------------------------------------------------------------------------