├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── mesh-buffer.js ├── mesh-plugin.js ├── mesh.js ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/* 16 | test/* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4 4 | - 5 5 | - 6 6 | - stable 7 | sudo: false 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2013 Mikola Lysenko 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | voxel-mesher 2 | ============ 3 | A voxel mesher for ndarrays that handles ambient occlusion and transparency. 4 | 5 | Based on @mikolalysenko's [ao-mesher](https://github.com/mikolalysenko/ao-mesher) 6 | 7 | [![Build Status](https://travis-ci.org/voxel/voxel-mesher.png)](https://travis-ci.org/voxel/voxel-mesher) 8 | 9 | ## Install 10 | 11 | npm install voxel-mesher 12 | 13 | Load with [voxel-plugins](https://github.com/voxel/voxel-plugins) 14 | 15 | ### API 16 | 17 | var mesher = game.plugins.get('voxel-mesher'); 18 | 19 | var mesh = mesher.createVoxelMesh(voxels, voxelSideTextureIDs, voxelSideTextureSizes, position, pad) 20 | 21 | Constructs a mesh for `voxels`. 22 | 23 | * `voxels`: 3D ndarray of voxels 24 | * `voxelSideTextureIDs`: 2D ndarray (15-bit voxel ID, side 0-6) to 16-bit texture ID, defaults to voxel ID 25 | * `voxelSideTextureSizes`: 2D ndarray (15-bit voxel ID, side 0-6) to log2(texture size), defaults to 4 (2^4=16) 26 | * `position`: vector `[x,y,z]` of this chunk's position 27 | * `pad`: twice the number of voxels to pad around each edge (4) 28 | 29 | Returns a typed array encoding the mesh, or else null if there were no facets. 30 | This is in the same format that [voxel-shader](https://github.com/voxel/voxel-shader) expects: 31 | 32 | { 33 | vertexArrayObjects: {surface: ..., porous: ...}, 34 | center: [x, y, z], 35 | radius: w, 36 | modelMatrix: mat4 37 | } 38 | 39 | Other plugins can add their own VAOs to `vertexArrayObjects`, by listening for the `meshed` event: 40 | 41 | mesher.on('meshed', function(result, gl, vert_data, voxels) { 42 | result.vertexArrayObjects.myVAO = ... 43 | }); 44 | 45 | This event is used by [voxel-wireframe](https://github.com/voxel/voxel-wireframe) and [voxel-chunkborder](https://github.com/voxel/voxel-chunkborder). 46 | 47 | ### Voxel types 48 | 49 | [voxel-registry](https://github.com/voxel/voxel-registry) is used to lookup voxel information for meshing, 50 | the following properties are supported: 51 | 52 | * `transparent`: if true, the voxel textures have transparency, otherwise assumed fully opaque 53 | * `blockModel`: if present, passed to [block-models](https://github.com/deathcap/block-models) for custom non-cube models 54 | 55 | These properties determines the voxel type and how they are meshed: 56 | 57 | * *Solid*: phase 1 rendering pass - included in `surface` VAO 58 | * *Opaque*: default, bit 15 set (example: dirt) 59 | * *Transparent*: property `transparent` true, bit 15 clear (example: glass) 60 | * *Porous*: phase 2 rendering pass - included in `porous` VAO 61 | * `blockModel` property present 62 | * Allows for translucent (example: stained glass) and custom model (example: slabs) blocks 63 | 64 | # Credits 65 | (c) 2013 Mikola Lysenko, (c) 2014-2015 deathcap. MIT License 66 | -------------------------------------------------------------------------------- /mesh-buffer.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var ndarray = require("ndarray") 4 | var createBuffer = require("gl-buffer") 5 | var createVAO = require("gl-vao") 6 | var createAOMesh = require("./mesh.js") 7 | var ops = require("ndarray-ops") 8 | var mat4 = require("gl-mat4") 9 | 10 | //Creates a mesh from a set of voxels 11 | function createVoxelMesh(gl, voxels, voxelSideTextureIDs, voxelSideTextureSizes, position, pad, that) { 12 | //Create mesh 13 | var vert_data = createAOMesh(voxels, voxelSideTextureIDs, voxelSideTextureSizes) 14 | var vertexArrayObjects = {} 15 | 16 | if (vert_data === null) { 17 | // no vertices allocated 18 | } else { 19 | //Upload triangle mesh to WebGL 20 | var vert_buf = createBuffer(gl, vert_data) 21 | var triangleVAO = createVAO(gl, [ 22 | { "buffer": vert_buf, 23 | "type": gl.UNSIGNED_BYTE, 24 | "size": 4, 25 | "offset": 0, 26 | "stride": 8, 27 | "normalized": false 28 | }, 29 | { "buffer": vert_buf, 30 | "type": gl.UNSIGNED_BYTE, 31 | "size": 4, 32 | "offset": 4, 33 | "stride": 8, 34 | "normalized": false 35 | } 36 | ]) 37 | triangleVAO.length = Math.floor(vert_data.length/8) 38 | 39 | vertexArrayObjects.surface = triangleVAO 40 | } 41 | 42 | // move the chunk into place 43 | var modelMatrix = mat4.create() 44 | var w = voxels.shape[2] - pad // =[1]=[0]=game.chunkSize 45 | var translateVector = [ 46 | position[0] * w, 47 | position[1] * w, 48 | position[2] * w] 49 | 50 | mat4.translate(modelMatrix, modelMatrix, translateVector) 51 | 52 | //Bundle result and return 53 | var result = { 54 | vertexArrayObjects: vertexArrayObjects, // other plugins can add their own VAOs 55 | center: [w>>1, w>>1, w>>1], 56 | radius: w, 57 | modelMatrix: modelMatrix 58 | } 59 | 60 | if (that) that.emit('meshed', result, gl, vert_data, voxels) 61 | 62 | return result 63 | } 64 | 65 | module.exports = createVoxelMesh 66 | -------------------------------------------------------------------------------- /mesh-plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var createVoxelMesh = require('./mesh-buffer.js'); 4 | var inherits = require('inherits'); 5 | var EventEmitter = require('events').EventEmitter; 6 | var ndarray = require('ndarray'); 7 | var ops = require('ndarray-ops'); 8 | var parseBlockModel = require("block-models"); 9 | var createBuffer = require("gl-buffer"); 10 | var createVAO = require("gl-vao"); 11 | 12 | module.exports = function(game, opts) { 13 | return new MesherPlugin(game, opts); 14 | }; 15 | module.exports.pluginInfo = { 16 | loadAfter: ['voxel-registry', 'voxel-stitch'], 17 | clientOnly: true 18 | }; 19 | 20 | function MesherPlugin(game, opts) { 21 | this.game = game; 22 | this.shell = game.shell; 23 | 24 | this.registry = game.plugins.get('voxel-registry'); 25 | if (!this.registry) throw new Error('voxel-mesher requires voxel-registry plugin'); 26 | 27 | this.stitcher = game.plugins.get('voxel-stitch'); 28 | if (!this.stitcher) throw new Error('voxel-mesher requires voxel-stitch plugin'); 29 | 30 | this.isTransparent = undefined; 31 | this.hasBlockModel = undefined; 32 | 33 | var s = game.chunkSize + (game.chunkPad|0) 34 | this.solidVoxels = ndarray(new game.arrayType(s*s*s), [s,s,s]); 35 | }; 36 | inherits(MesherPlugin, EventEmitter); 37 | 38 | MesherPlugin.prototype.createVoxelMesh = function(gl, voxels, voxelSideTextureIDs, voxelSideTextureSizes, position, pad) { 39 | var porousMesh = this.splitVoxelArray(voxels); 40 | 41 | var mesh = createVoxelMesh(gl, this.solidVoxels, voxelSideTextureIDs, voxelSideTextureSizes, position, pad, this); 42 | 43 | mesh.vertexArrayObjects.porous = porousMesh; 44 | 45 | return mesh; 46 | } 47 | 48 | // mesh custom voxel 49 | MesherPlugin.prototype.meshCustomBlock = function(value,x,y,z) { 50 | var modelDefn = this.registry.blockProps[value].blockModel; 51 | var stitcher = this.stitcher; 52 | 53 | // parse JSON to vertices and UV 54 | var model = parseBlockModel( 55 | modelDefn, 56 | //getTextureUV: 57 | function(name) { 58 | return stitcher.getTextureUV(name); // only available when textures are ready 59 | }, 60 | x,y,z 61 | ); 62 | 63 | return model; 64 | }; 65 | 66 | // populates solidVoxels array, returns porousMesh 67 | MesherPlugin.prototype.splitVoxelArray = function(voxels) { 68 | if (!this.isTransparent) { 69 | // cache list of transparent voxels TODO: refresh cache when changes 70 | this.isTransparent = this.registry.getBlockPropsAll('transparent'); 71 | this.isTransparent.unshift(true); // air (0) is transparent 72 | } 73 | if (!this.hasBlockModel) { 74 | this.hasBlockModel = this.registry.getBlockPropsAll('blockModel'); 75 | this.hasBlockModel.unshift(undefined); 76 | } 77 | 78 | // phase 1: solid voxels = opaque, transparent (terrain blocks, glass, greedily meshed) 79 | var solidVoxels = this.solidVoxels; 80 | var isTransparent = this.isTransparent; 81 | ops.assign(solidVoxels, voxels); 82 | 83 | // phase 2: porous voxels = translucent, custom block models (stained glass, slabs, stairs) 84 | var hasBlockModel = this.hasBlockModel; 85 | var porousMeshes = this.porousMeshes = []; 86 | 87 | var length = solidVoxels.data.length; 88 | var vertices = []; 89 | var uv = []; 90 | for (var i = 0; i < length; ++i) { 91 | var value = solidVoxels.data[i]; 92 | if (hasBlockModel[value]) { 93 | solidVoxels.data[i] = 0; 94 | 95 | // extract coordinate from ndarray index (sorry) 96 | var o = i; 97 | var z = (o % 36)-2; o = Math.floor(o / 36); 98 | var y = (o % 36)-2; o = Math.floor(o / 36); 99 | var x = o-2; 100 | 101 | // accumulate mesh vertices and uv 102 | var model = this.meshCustomBlock(value,x,y,z); 103 | vertices = vertices.concat(model.vertices); 104 | uv = uv.concat(model.uv); 105 | } else if (!isTransparent[value]) { 106 | solidVoxels.data[i] = value | (1<<15); // opaque bit 107 | } 108 | } 109 | 110 | // load combined porous mesh into GL 111 | var gl = this.shell.gl; 112 | var verticesBuf = createBuffer(gl, new Float32Array(vertices)); 113 | var uvBuf = createBuffer(gl, new Float32Array(uv)); 114 | var porousMesh = createVAO(gl, [ 115 | { buffer: verticesBuf, 116 | size: 3 117 | }, 118 | { 119 | buffer: uvBuf, 120 | size: 2 121 | } 122 | ]); 123 | porousMesh.length = vertices.length/3; 124 | 125 | return porousMesh; 126 | }; 127 | 128 | -------------------------------------------------------------------------------- /mesh.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | var ndarray = require("ndarray") 4 | var compileCWise = require("cwise-compiler") 5 | var compileMesher = require("greedy-mesher") 6 | var pool = require("typedarray-pool") 7 | 8 | var OPAQUE_BIT = (1<<15) 9 | var VOXEL_MASK = (1<<16)-1 10 | var AO_SHIFT = 16 11 | var AO_BITS = 2 12 | var AO_MASK = (1<>>0 156 | var z = this.z|0 157 | var u = this.u|0 158 | var v = this.v|0 159 | var d = this.d|0 160 | 161 | //Grow buffer if we exceed capacity 162 | if(ptr + 6*VERTEX_SIZE > buffer.length) { 163 | var tmp = pool.mallocUint8(2*buffer.length); 164 | tmp.set(buffer) 165 | pool.freeUint8(buffer) 166 | buffer = tmp 167 | this.buffer = buffer 168 | } 169 | 170 | var flip = !!(val & FLIP_BIT) 171 | var side = d + (flip ? 3 : 0) 172 | 173 | var a00 = AO_TABLE[((val>>>AO_SHIFT)&AO_MASK)] 174 | var a10 = AO_TABLE[((val>>>(AO_SHIFT+AO_BITS))&AO_MASK)] 175 | var a11 = AO_TABLE[((val>>>(AO_SHIFT+2*AO_BITS))&AO_MASK)] 176 | var a01 = AO_TABLE[((val>>>(AO_SHIFT+3*AO_BITS))&AO_MASK)] 177 | 178 | var tex_id = voxelTexture(val&VOXEL_MASK, side, this.voxelSideTextureIDs) 179 | var hi_tex_id = tex_id >> 8 180 | var lo_tex_id = tex_id & 0xff 181 | 182 | // pack normal into 6 bits 183 | var nx=1, ny=1, nz=1 // 0 184 | var sign = flip ? 0 : 2 // -1, +1 185 | if(d === 0) { 186 | nx = sign 187 | } else if(d === 1) { 188 | ny = sign 189 | } else if(d === 2) { 190 | nz = sign 191 | } 192 | var packed_normal = (nx << 4) + (ny << 2) + nz 193 | 194 | var tex_size = voxelTextureSizeLg(val&VOXEL_MASK, side, this.voxelSideTextureSizes) 195 | 196 | var flipAO = a00 + a11 < a10 + a01 197 | 198 | if(a00 + a11 === a10 + a01) { 199 | flipAO = Math.max(a00,a11) < Math.max(a10,a01) 200 | } 201 | 202 | if(flipAO) { 203 | if(!flip) { 204 | buffer[ptr+u] = lo_x 205 | buffer[ptr+v] = lo_y 206 | buffer[ptr+d] = z 207 | buffer[ptr+3] = a00 208 | buffer[ptr+4] = packed_normal 209 | buffer[ptr+5] = tex_size 210 | buffer[ptr+6] = hi_tex_id 211 | buffer[ptr+7] = lo_tex_id 212 | 213 | ptr += 8 214 | 215 | buffer[ptr+u] = lo_x 216 | buffer[ptr+v] = hi_y 217 | buffer[ptr+d] = z 218 | buffer[ptr+3] = a01 219 | buffer[ptr+4] = packed_normal 220 | buffer[ptr+5] = tex_size 221 | buffer[ptr+6] = hi_tex_id 222 | buffer[ptr+7] = lo_tex_id 223 | 224 | ptr += 8 225 | 226 | buffer[ptr+u] = hi_x 227 | buffer[ptr+v] = lo_y 228 | buffer[ptr+d] = z 229 | buffer[ptr+3] = a10 230 | buffer[ptr+4] = packed_normal 231 | buffer[ptr+5] = tex_size 232 | buffer[ptr+6] = hi_tex_id 233 | buffer[ptr+7] = lo_tex_id 234 | 235 | ptr += 8 236 | 237 | buffer[ptr+u] = hi_x 238 | buffer[ptr+v] = hi_y 239 | buffer[ptr+d] = z 240 | buffer[ptr+3] = a11 241 | buffer[ptr+4] = packed_normal 242 | buffer[ptr+5] = tex_size 243 | buffer[ptr+6] = hi_tex_id 244 | buffer[ptr+7] = lo_tex_id 245 | 246 | ptr += 8 247 | 248 | buffer[ptr+u] = hi_x 249 | buffer[ptr+v] = lo_y 250 | buffer[ptr+d] = z 251 | buffer[ptr+3] = a10 252 | buffer[ptr+4] = packed_normal 253 | buffer[ptr+5] = tex_size 254 | buffer[ptr+6] = hi_tex_id 255 | buffer[ptr+7] = lo_tex_id 256 | 257 | ptr += 8 258 | 259 | buffer[ptr+u] = lo_x 260 | buffer[ptr+v] = hi_y 261 | buffer[ptr+d] = z 262 | buffer[ptr+3] = a01 263 | buffer[ptr+4] = packed_normal 264 | buffer[ptr+5] = tex_size 265 | buffer[ptr+6] = hi_tex_id 266 | buffer[ptr+7] = lo_tex_id 267 | 268 | ptr += 8 269 | 270 | } else { 271 | 272 | buffer[ptr+u] = lo_x 273 | buffer[ptr+v] = lo_y 274 | buffer[ptr+d] = z 275 | buffer[ptr+3] = a00 276 | buffer[ptr+4] = packed_normal 277 | buffer[ptr+5] = tex_size 278 | buffer[ptr+6] = hi_tex_id 279 | buffer[ptr+7] = lo_tex_id 280 | 281 | ptr += 8 282 | 283 | buffer[ptr+u] = hi_x 284 | buffer[ptr+v] = lo_y 285 | buffer[ptr+d] = z 286 | buffer[ptr+3] = a10 287 | buffer[ptr+4] = packed_normal 288 | buffer[ptr+5] = tex_size 289 | buffer[ptr+6] = hi_tex_id 290 | buffer[ptr+7] = lo_tex_id 291 | 292 | ptr += 8 293 | 294 | buffer[ptr+u] = lo_x 295 | buffer[ptr+v] = hi_y 296 | buffer[ptr+d] = z 297 | buffer[ptr+3] = a01 298 | buffer[ptr+4] = packed_normal 299 | buffer[ptr+5] = tex_size 300 | buffer[ptr+6] = hi_tex_id 301 | buffer[ptr+7] = lo_tex_id 302 | 303 | ptr += 8 304 | 305 | buffer[ptr+u] = hi_x 306 | buffer[ptr+v] = hi_y 307 | buffer[ptr+d] = z 308 | buffer[ptr+3] = a11 309 | buffer[ptr+4] = packed_normal 310 | buffer[ptr+5] = tex_size 311 | buffer[ptr+6] = hi_tex_id 312 | buffer[ptr+7] = lo_tex_id 313 | 314 | ptr += 8 315 | 316 | buffer[ptr+u] = lo_x 317 | buffer[ptr+v] = hi_y 318 | buffer[ptr+d] = z 319 | buffer[ptr+3] = a01 320 | buffer[ptr+4] = packed_normal 321 | buffer[ptr+5] = tex_size 322 | buffer[ptr+6] = hi_tex_id 323 | buffer[ptr+7] = lo_tex_id 324 | 325 | ptr += 8 326 | 327 | buffer[ptr+u] = hi_x 328 | buffer[ptr+v] = lo_y 329 | buffer[ptr+d] = z 330 | buffer[ptr+3] = a10 331 | buffer[ptr+4] = packed_normal 332 | buffer[ptr+5] = tex_size 333 | buffer[ptr+6] = hi_tex_id 334 | buffer[ptr+7] = lo_tex_id 335 | 336 | ptr += 8 337 | } 338 | } else { 339 | //Check if flipped 340 | if(flip) { 341 | buffer[ptr+u] = lo_x 342 | buffer[ptr+v] = hi_y 343 | buffer[ptr+d] = z 344 | buffer[ptr+3] = a01 345 | buffer[ptr+4] = packed_normal 346 | buffer[ptr+5] = tex_size 347 | buffer[ptr+6] = hi_tex_id 348 | buffer[ptr+7] = lo_tex_id 349 | 350 | ptr += 8 351 | 352 | buffer[ptr+u] = lo_x 353 | buffer[ptr+v] = lo_y 354 | buffer[ptr+d] = z 355 | buffer[ptr+3] = a00 356 | buffer[ptr+4] = packed_normal 357 | buffer[ptr+5] = tex_size 358 | buffer[ptr+6] = hi_tex_id 359 | buffer[ptr+7] = lo_tex_id 360 | 361 | ptr += 8 362 | 363 | buffer[ptr+u] = hi_x 364 | buffer[ptr+v] = hi_y 365 | buffer[ptr+d] = z 366 | buffer[ptr+3] = a11 367 | buffer[ptr+4] = packed_normal 368 | buffer[ptr+5] = tex_size 369 | buffer[ptr+6] = hi_tex_id 370 | buffer[ptr+7] = lo_tex_id 371 | 372 | ptr += 8 373 | 374 | buffer[ptr+u] = hi_x 375 | buffer[ptr+v] = lo_y 376 | buffer[ptr+d] = z 377 | buffer[ptr+3] = a10 378 | buffer[ptr+4] = packed_normal 379 | buffer[ptr+5] = tex_size 380 | buffer[ptr+6] = hi_tex_id 381 | buffer[ptr+7] = lo_tex_id 382 | 383 | ptr += 8 384 | 385 | buffer[ptr+u] = hi_x 386 | buffer[ptr+v] = hi_y 387 | buffer[ptr+d] = z 388 | buffer[ptr+3] = a11 389 | buffer[ptr+4] = packed_normal 390 | buffer[ptr+5] = tex_size 391 | buffer[ptr+6] = hi_tex_id 392 | buffer[ptr+7] = lo_tex_id 393 | 394 | ptr += 8 395 | 396 | buffer[ptr+u] = lo_x 397 | buffer[ptr+v] = lo_y 398 | buffer[ptr+d] = z 399 | buffer[ptr+3] = a00 400 | buffer[ptr+4] = packed_normal 401 | buffer[ptr+5] = tex_size 402 | buffer[ptr+6] = hi_tex_id 403 | buffer[ptr+7] = lo_tex_id 404 | 405 | ptr += 8 406 | } else { 407 | buffer[ptr+u] = lo_x 408 | buffer[ptr+v] = lo_y 409 | buffer[ptr+d] = z 410 | buffer[ptr+3] = a00 411 | buffer[ptr+4] = packed_normal 412 | buffer[ptr+5] = tex_size 413 | buffer[ptr+6] = hi_tex_id 414 | buffer[ptr+7] = lo_tex_id 415 | 416 | ptr += 8 417 | 418 | buffer[ptr+u] = lo_x 419 | buffer[ptr+v] = hi_y 420 | buffer[ptr+d] = z 421 | buffer[ptr+3] = a01 422 | buffer[ptr+4] = packed_normal 423 | buffer[ptr+5] = tex_size 424 | buffer[ptr+6] = hi_tex_id 425 | buffer[ptr+7] = lo_tex_id 426 | 427 | ptr += 8 428 | 429 | buffer[ptr+u] = hi_x 430 | buffer[ptr+v] = hi_y 431 | buffer[ptr+d] = z 432 | buffer[ptr+3] = a11 433 | buffer[ptr+4] = packed_normal 434 | buffer[ptr+5] = tex_size 435 | buffer[ptr+6] = hi_tex_id 436 | buffer[ptr+7] = lo_tex_id 437 | 438 | ptr += 8 439 | 440 | buffer[ptr+u] = hi_x 441 | buffer[ptr+v] = hi_y 442 | buffer[ptr+d] = z 443 | buffer[ptr+3] = a11 444 | buffer[ptr+4] = packed_normal 445 | buffer[ptr+5] = tex_size 446 | buffer[ptr+6] = hi_tex_id 447 | buffer[ptr+7] = lo_tex_id 448 | 449 | ptr += 8 450 | 451 | buffer[ptr+u] = hi_x 452 | buffer[ptr+v] = lo_y 453 | buffer[ptr+d] = z 454 | buffer[ptr+3] = a10 455 | buffer[ptr+4] = packed_normal 456 | buffer[ptr+5] = tex_size 457 | buffer[ptr+6] = hi_tex_id 458 | buffer[ptr+7] = lo_tex_id 459 | 460 | ptr += 8 461 | 462 | buffer[ptr+u] = lo_x 463 | buffer[ptr+v] = lo_y 464 | buffer[ptr+d] = z 465 | buffer[ptr+3] = a00 466 | buffer[ptr+4] = packed_normal 467 | buffer[ptr+5] = tex_size 468 | buffer[ptr+6] = hi_tex_id 469 | buffer[ptr+7] = lo_tex_id 470 | 471 | ptr += 8 472 | } 473 | } 474 | 475 | this.ptr = ptr 476 | } 477 | 478 | var meshBuilder = new MeshBuilder() 479 | 480 | //Compile mesher 481 | var meshSlice = compileMesher({ 482 | order: [1, 0], 483 | append: MeshBuilder.prototype.append.bind(meshBuilder) 484 | }) 485 | 486 | //Compute a mesh 487 | function computeMesh(array, voxelSideTextureIDs, voxelSideTextureSizes) { 488 | var shp = array.shape.slice(0) 489 | var nx = (shp[0]-2)|0 490 | var ny = (shp[1]-2)|0 491 | var nz = (shp[2]-2)|0 492 | var sz = nx * ny * nz 493 | var scratch0 = pool.mallocInt32(sz) 494 | var scratch1 = pool.mallocInt32(sz) 495 | var scratch2 = pool.mallocInt32(sz) 496 | var rshp = [nx, ny, nz] 497 | var ao0 = ndarray(scratch0, rshp) 498 | var ao1 = ndarray(scratch1, rshp) 499 | var ao2 = ndarray(scratch2, rshp) 500 | 501 | //Calculate ao fields 502 | surfaceStencil(ao0, ao1, ao2, array) 503 | 504 | //Build mesh slices 505 | meshBuilder.ptr = 0 506 | meshBuilder.voxelSideTextureIDs = voxelSideTextureIDs 507 | meshBuilder.voxelSideTextureSizes = voxelSideTextureSizes 508 | 509 | var buffers = [ao0, ao1, ao2] 510 | for(var d=0; d<3; ++d) { 511 | var u = (d+1)%3 512 | var v = (d+2)%3 513 | 514 | //Create slice 515 | var st = buffers[d].transpose(d, u, v) 516 | var slice = st.pick(0) 517 | var n = rshp[d]|0 518 | 519 | meshBuilder.d = d 520 | meshBuilder.u = v 521 | meshBuilder.v = u 522 | 523 | //Generate slices 524 | for(var i=0; i" 47 | ], 48 | "license": "MIT", 49 | "readmeFilename": "README.md", 50 | "bugs": { 51 | "url": "https://github.com/voxel/voxel-mesher/issues" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var computeMesh = require("../mesh.js") 2 | var ndarray = require("ndarray") 3 | var fill = require("ndarray-fill") 4 | var voxel = require("voxel") 5 | 6 | var ndseg = require("ndarray-segment") 7 | 8 | var x = ndarray(new Int32Array(33*33*33), [33,33,33]) 9 | 10 | fill(x, function(i,j,k) { 11 | var a = i-16 12 | var b = i-16 13 | var c = i-16 14 | return (a*a + b*b + c*c) ? 1<<15 : 0 15 | }) 16 | 17 | for(var j=0; j<10; ++j) { 18 | computeMesh(x) 19 | } 20 | 21 | setTimeout(function(){ 22 | var start = Date.now() 23 | for(var i=0; i<1000; ++i) { 24 | computeMesh(x) 25 | //voxel.meshers.greedy(x.data, x.shape) 26 | } 27 | console.log(Date.now() - start) 28 | }, 10) --------------------------------------------------------------------------------