smoothvoxels
Advanced tools
Comparing version 1.2.6 to 1.2.7
{ | ||
"name": "smoothvoxels", | ||
"version": "1.2.6", | ||
"version": "1.2.7", | ||
"description": "Smooth Voxels", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -5,2 +5,6 @@ # Smooth Voxels | ||
Command line tools for converting VOX to SVOX and SVOX to GLTF are now available here: https://github.com/jel-app/svox-tools | ||
NPM Package: https://www.npmjs.com/package/smoothvoxels | ||
This is an optimized and npm packaged fork of the [Smooth Voxels](https://svox.glitch.me/) library by Samuel van Egmond. | ||
@@ -7,0 +11,0 @@ |
@@ -9,2 +9,3 @@ import BaseMaterial from './smoothvoxels/basematerial' | ||
import SvoxMeshGenerator from './smoothvoxels/svoxmeshgenerator' | ||
import SvoxBufferGeometry from './smoothvoxels/svoxbuffergeometry' | ||
import SvoxToThreeMeshConverter from './smoothvoxels/svoxtothreemeshconverter' | ||
@@ -16,3 +17,3 @@ import Model from './smoothvoxels/model' | ||
import Noise from './smoothvoxels/noise' | ||
import Voxels, { xyzRangeForSize, shiftForSize, voxColorForRGBT, voxBGRForHex, rgbtForVoxColor } from './smoothvoxels/voxels' | ||
import Voxels, { VOXEL_FILTERS, MAX_SIZE, xyzRangeForSize, shiftForSize, voxColorForRGBT, voxBGRForHex, rgbtForVoxColor, REMOVE_VOXEL_COLOR } from './smoothvoxels/voxels' | ||
@@ -32,2 +33,3 @@ import voxToSvox from './vox-to-svox' | ||
SvoxMeshGenerator, | ||
SvoxBufferGeometry, | ||
SvoxToThreeMeshConverter, | ||
@@ -45,3 +47,6 @@ Model, | ||
voxToSvox, | ||
imgToSvox | ||
imgToSvox, | ||
REMOVE_VOXEL_COLOR, | ||
MAX_SIZE, | ||
VOXEL_FILTERS | ||
} |
@@ -13,2 +13,3 @@ // Material type constants | ||
export const MODEL = 'model' // Resize the model to fit the bounds | ||
export const SKIP = 'skip' // skip all resizing | ||
@@ -15,0 +16,0 @@ // Material lighting constants |
@@ -9,2 +9,3 @@ import Planar from './planar' | ||
import AOCalculator from './aocalculator' | ||
import BoundingBox from './boundingbox' | ||
import UVAssigner from './uvassigner' | ||
@@ -14,5 +15,5 @@ import ColorCombiner from './colorcombiner' | ||
import FaceAligner from './facealigner' | ||
import { BOUNDS, MODEL, _VERTEX_OFFSETS, _FACEINDEXUV_MULTIPLIERS, _FACES, _NEIGHBORS } from './constants' | ||
import { SKIP, MODEL, _VERTEX_OFFSETS, _FACEINDEXUV_MULTIPLIERS, _FACES, _NEIGHBORS } from './constants' | ||
import { shiftForSize, xyzRangeForSize } from './voxels' | ||
import { shiftForSize, xyzRangeForSize, EMPTY_VOXEL_PALETTE_INDEX } from './voxels' | ||
@@ -103,4 +104,48 @@ const SORT_NUMBERS = (a, b) => a - b | ||
this.nonCulledFaceCount = 0 | ||
this.bounds = new BoundingBox() | ||
} | ||
recomputeModelAndMaterialBounds () { | ||
const { voxels, bounds, materials: { materials } } = this | ||
const materialBounds = [] | ||
bounds.reset() | ||
for (const material of materials) { | ||
const bounds = material.bounds | ||
bounds.reset() | ||
materialBounds.push(bounds) | ||
} | ||
const maxMaterialIndex = materials.length - 1 | ||
if (!voxels) return | ||
const [minX, maxX, minY, maxY, minZ, maxZ] = xyzRangeForSize(voxels.size) | ||
const shiftX = shiftForSize(voxels.size[0]) | ||
const shiftY = shiftForSize(voxels.size[1]) | ||
const shiftZ = shiftForSize(voxels.size[2]) | ||
for (let x = minX; x <= maxX; x++) { | ||
for (let y = minY; y <= maxY; y++) { | ||
for (let z = minZ; z <= maxZ; z++) { | ||
const paletteIndex = voxels.getPaletteIndexAt(x, y, z) | ||
if (paletteIndex === EMPTY_VOXEL_PALETTE_INDEX) continue | ||
const vx = x + shiftX | ||
const vy = y + shiftY | ||
const vz = z + shiftZ | ||
const voxColor = voxels.colorForPaletteIndex(paletteIndex) | ||
const materialIndex = (voxColor & 0xff000000) >> 24 | ||
if (materialIndex <= maxMaterialIndex) { | ||
materialBounds[materialIndex].set(vx, vy, vz) | ||
} | ||
bounds.set(vx, vy, vz) | ||
} | ||
} | ||
} | ||
} | ||
prepareForRender (buffers) { | ||
@@ -112,2 +157,4 @@ const { tmpVertIndexLookup, tmpVoxelXZYFaceIndices, tmpVoxelXYZFaceIndices, tmpVoxelYZXFaceIndices } = buffers | ||
this.recomputeModelAndMaterialBounds() | ||
this.faceCount = 0 | ||
@@ -122,2 +169,4 @@ this.vertCount = 0 | ||
tmpVertIndexLookup.clear() | ||
const materials = this.materials.materials | ||
@@ -132,3 +181,3 @@ const xShift = shiftForSize(voxels.size[0]) | ||
const paletteIndex = voxels.getPaletteIndexAt(vx, vy, vz) | ||
if (paletteIndex === 0) continue | ||
if (paletteIndex === EMPTY_VOXEL_PALETTE_INDEX) continue | ||
@@ -243,8 +292,22 @@ // Shift to positive values | ||
determineBoundsOffsetAndRescale (resize, buffers) { | ||
const bos = { bounds: null, offset: null, rescale: 1 } | ||
const bos = { offset: { x: 0, y: 0, z: 0 }, rescale: 1 } | ||
if (resize === SKIP) return bos | ||
let minX, minY, minZ, maxX, maxY, maxZ | ||
const { vertX, vertY, vertZ } = buffers | ||
const { faceVertIndices, faceCulled, vertX, vertY, vertZ } = buffers | ||
if (resize === BOUNDS || resize === MODEL) { | ||
let shiftX = shiftForSize(this.voxels.size[0]) | ||
let shiftY = shiftForSize(this.voxels.size[1]) | ||
let shiftZ = shiftForSize(this.voxels.size[2]) | ||
if (!resize) { | ||
// Just use it's original bounds | ||
minX = this.bounds.minX | ||
maxX = this.bounds.maxX + 1 | ||
minY = this.bounds.minY | ||
maxY = this.bounds.maxY + 1 | ||
minZ = this.bounds.minZ | ||
maxZ = this.bounds.maxZ + 1 | ||
} else { | ||
// Determine the actual model size if resize is set (to model or bounds) | ||
@@ -257,24 +320,41 @@ minX = Number.POSITIVE_INFINITY | ||
maxZ = Number.NEGATIVE_INFINITY | ||
shiftX = shiftY = shiftZ = 0 | ||
// Skip the skipped faces when determining the bounds | ||
for (let vertIndex = 0, c = this.vertCount; vertIndex < c; vertIndex++) { | ||
const vx = vertX[vertIndex] | ||
const vy = vertY[vertIndex] | ||
const vz = vertZ[vertIndex] | ||
for (let faceIndex = 0, c = this.faceCount; faceIndex < c; faceIndex++) { | ||
if (faceCulled.get(faceIndex) === 1) continue | ||
if (vx < minX) minX = vx | ||
if (vy < minY) minY = vy | ||
if (vz < minZ) minZ = vz | ||
if (vx > maxX) maxX = vx | ||
if (vy > maxY) maxY = vy | ||
if (vz > maxZ) maxZ = vz | ||
const faceVertOffset = faceIndex * 4 | ||
const vert0Index = faceVertIndices[faceVertOffset] | ||
const vert1Index = faceVertIndices[faceVertOffset + 1] | ||
const vert2Index = faceVertIndices[faceVertOffset + 2] | ||
const vert3Index = faceVertIndices[faceVertOffset + 3] | ||
const vert0X = vertX[vert0Index] | ||
const vert0Y = vertY[vert0Index] | ||
const vert0Z = vertZ[vert0Index] | ||
const vert1X = vertX[vert1Index] | ||
const vert1Y = vertY[vert1Index] | ||
const vert1Z = vertZ[vert1Index] | ||
const vert2X = vertX[vert2Index] | ||
const vert2Y = vertY[vert2Index] | ||
const vert2Z = vertZ[vert2Index] | ||
const vert3X = vertX[vert3Index] | ||
const vert3Y = vertY[vert3Index] | ||
const vert3Z = vertZ[vert3Index] | ||
minX = Math.min(minX, vert0X, vert1X, vert2X, vert3X) | ||
minY = Math.min(minY, vert0Y, vert1Y, vert2Y, vert3Y) | ||
minZ = Math.min(minZ, vert0Z, vert1Z, vert2Z, vert3Z) | ||
maxX = Math.max(maxX, vert0X, vert1X, vert2X, vert3X) | ||
maxY = Math.max(maxY, vert0Y, vert1Y, vert2Y, vert3Y) | ||
maxZ = Math.max(maxZ, vert0Z, vert1Z, vert2Z, vert3Z) | ||
} | ||
if (resize === MODEL) { | ||
const [minX, maxX, minY, maxY, minZ, maxZ] = xyzRangeForSize(this.voxels.size) | ||
const [vminX, vmaxX, vminY, vmaxY, vminZ, vmaxZ] = xyzRangeForSize(this.voxels.size) | ||
// Resize the actual model to the original voxel bounds | ||
const scaleX = (maxX - minX + 1) / (maxX - minX) | ||
const scaleY = (maxY - minY + 1) / (maxY - minY) | ||
const scaleZ = (maxZ - minZ + 1) / (maxZ - minZ) | ||
const scaleX = (vmaxX - vminX + 1) / (maxX - minX) | ||
const scaleY = (vmaxY - vminY + 1) / (maxY - minY) | ||
const scaleZ = (vmaxZ - vminZ + 1) / (maxZ - minZ) | ||
bos.rescale = Math.min(scaleX, scaleY, scaleZ) | ||
@@ -284,12 +364,2 @@ } | ||
if (!resize) { | ||
// Just use it's original bounds | ||
minX = this.bounds.minX | ||
maxX = this.bounds.maxX + 1 | ||
minY = this.bounds.minY | ||
maxY = this.bounds.maxY + 1 | ||
minZ = this.bounds.minZ | ||
maxZ = this.bounds.maxZ + 1 | ||
} | ||
let offsetX = -(minX + maxX) / 2 | ||
@@ -306,4 +376,5 @@ let offsetY = -(minY + maxY) / 2 | ||
bos.bounds = { minX, minY, minZ, maxX, maxY, maxZ } | ||
bos.offset = { x: offsetX, y: offsetY, z: offsetZ } | ||
bos.offset.x = offsetX + shiftX | ||
bos.offset.y = offsetY + shiftY | ||
bos.offset.z = offsetZ + shiftZ | ||
@@ -321,3 +392,3 @@ return bos | ||
return false | ||
} else if (neighborPaletteIndex === 0) { | ||
} else if (neighborPaletteIndex === EMPTY_VOXEL_PALETTE_INDEX) { | ||
// The voxel is next to an empty voxel, so create a face | ||
@@ -446,3 +517,3 @@ } else { | ||
} else { | ||
vertIndex = this.vertCount | ||
vertIndex = this.vertCount++ | ||
vertIndexLookup.set(key, vertIndex) | ||
@@ -500,4 +571,2 @@ | ||
this.vertCount++ | ||
return vertIndex | ||
@@ -504,0 +573,0 @@ } |
@@ -40,6 +40,6 @@ import Model from './model' | ||
*/ | ||
static readFromString (modelString) { | ||
static readFromString (modelString, extraOptionalModelFields = {}, skipVoxels = false) { | ||
const modelData = this._parse(modelString) | ||
this._validateModel(modelData) | ||
return this._createModel(modelData) | ||
this._validateModel(modelData, extraOptionalModelFields) | ||
return this._createModel(modelData, extraOptionalModelFields, skipVoxels) | ||
} | ||
@@ -116,3 +116,3 @@ | ||
*/ | ||
static _createModel (modelData) { | ||
static _createModel (modelData, extraOptionalModelFields, skipVoxels) { | ||
const model = new Model() | ||
@@ -149,4 +149,2 @@ | ||
model.shell = this._parseShell(modelData.shell) | ||
if (modelData.lights.some((light) => light.size)) { | ||
@@ -179,2 +177,4 @@ // There are visible lights, so create a basic material for them | ||
model.shell = this._parseShell(modelData.shell) | ||
// Find the color (& material) for the shell(s) | ||
@@ -186,5 +186,34 @@ this._resolveShellColors(model.shell, model, colorIdToVoxBgr, colorIdToMaterialIndex) | ||
// Create all voxels | ||
this._createVoxels(model, modelData.voxels, colorIdToVoxBgr, colorIdToMaterialIndex) | ||
if (!skipVoxels) { | ||
// Create all voxels | ||
this._createVoxels(model, modelData.voxels, colorIdToVoxBgr, colorIdToMaterialIndex) | ||
} | ||
if (extraOptionalModelFields) { | ||
for (const [field, type] of Object.entries(extraOptionalModelFields)) { | ||
if (modelData[field]) { | ||
switch (type) { | ||
case 'int': | ||
model[field] = parseInt(modelData[field], 10) | ||
break | ||
case 'float': | ||
model[field] = parseFloat(modelData[field]) | ||
break | ||
case 'string': | ||
model[field] = modelData[field] | ||
break | ||
case 'boolean': | ||
model[field] = modelData[field] === 'true' | ||
break | ||
default: | ||
throw new Error(`Unknown type ${type} for field ${field}`) | ||
} | ||
} | ||
} | ||
} | ||
return model | ||
@@ -786,4 +815,4 @@ } | ||
*/ | ||
static _validateModel (modelData) { | ||
this._validateProperties(modelData, MANDATORY_MODEL_FIELDS, OPTIONAL_MODEL_FIELDS, 'model') | ||
static _validateModel (modelData, extraOptionalModelFields) { | ||
this._validateProperties(modelData, MANDATORY_MODEL_FIELDS, [...Object.keys(extraOptionalModelFields), ...OPTIONAL_MODEL_FIELDS], 'model') | ||
@@ -790,0 +819,0 @@ for (const lightData of modelData.lights) { |
@@ -30,3 +30,3 @@ import { MATSTANDARD, FLAT, FRONT } from './constants.js' | ||
*/ | ||
static writeToString (model, compressed, repeat, modelLine = null, materialLine = null) { | ||
static writeToString (model, compressed, repeat, modelLine = null, materialLine = null, extraOptionalModelFields = {}) { | ||
repeat = Math.round(repeat || 1) | ||
@@ -39,2 +39,23 @@ | ||
// Include all shell materials | ||
for (const shell of (model.shell || [])) { | ||
for (const voxColor of voxColorToColorId.keys()) { | ||
const materialIndex = (voxColor >> 24) & 0xff | ||
if (materialIndex === shell.materialIndex) { | ||
voxColorToCount.set(voxColor, 1) | ||
} | ||
} | ||
} | ||
model.materials.forEach(function (material, index) { | ||
for (const shell of (material.shell || [])) { | ||
for (const voxColor of voxColorToColorId.keys()) { | ||
const materialIndex = (voxColor >> 24) & 0xff | ||
if (materialIndex === shell.materialIndex) { | ||
voxColorToCount.set(voxColor, 1) | ||
} | ||
} | ||
} | ||
}) | ||
for (const [voxColor, count] of voxels.getVoxColorCounts()) { | ||
@@ -61,3 +82,18 @@ if (!voxColorToCount.has(voxColor)) { | ||
// Sort the vox colors on (usage) count | ||
for (const voxColor of voxColorToCount.keys()) { | ||
const r = voxColor & 0xff | ||
const g = (voxColor >> 8) & 0xff | ||
const b = (voxColor >> 16) & 0xff | ||
let hex | ||
if (SINGLE_HEX_VALUES.has(r) && SINGLE_HEX_VALUES.has(g) && SINGLE_HEX_VALUES.has(b)) { | ||
hex = '#' + (SINGLE_HEX_VALUES.get(b) + SINGLE_HEX_VALUES.get(g) * 16 + SINGLE_HEX_VALUES.get(r) * 256).toString(16).padStart(3, '0') | ||
} else { | ||
hex = '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0') | ||
} | ||
voxColorToHex.set(voxColor, hex.toUpperCase()) | ||
} | ||
const sortedVoxColors = [...voxColorToCount.keys()].sort((a, b) => { | ||
@@ -77,3 +113,3 @@ return voxColorToCount.get(b) - voxColorToCount.get(a) | ||
colorId = this._colorIdForIndex(index++) | ||
} while (voxColorToColorId.has(colorId)) | ||
} while ([...voxColorToColorId.values()].includes(colorId)) | ||
@@ -100,2 +136,8 @@ voxColorToColorId.set(voxColor, colorId) | ||
} else { | ||
for (const key of Object.keys(extraOptionalModelFields)) { | ||
if (model[key]) { | ||
result += `${key} = ${model[key]}\r\n` | ||
} | ||
} | ||
// Add the size to the result | ||
@@ -102,0 +144,0 @@ const [sizeX, sizeY, sizeZ] = model.voxels.size |
@@ -26,2 +26,3 @@ import { DOUBLE, FRONT, MATNORMAL, BACK, SMOOTH, BOTH, QUAD } from './constants' | ||
uvs: new Float32Array(nonCulledFaceCount * 4 * 2 * maxShellCount), | ||
bounds: { minX: Infinity, minY: Infinity, minZ: Infinity, maxX: -Infinity, maxY: -Infinity, maxZ: -Infinity, centerX: 0, centerY: 0, centerZ: 0, radius: 0 }, | ||
data: null | ||
@@ -214,2 +215,46 @@ } | ||
let minX = Infinity; let minY = Infinity; let minZ = Infinity | ||
let maxX = -Infinity; let maxY = -Infinity; let maxZ = -Infinity | ||
for (let i = 0, l = vertCount * 3; i < l; i += 3) { | ||
const x = mesh.positions[i] | ||
const y = mesh.positions[i + 1] | ||
const z = mesh.positions[i + 2] | ||
minX = Math.min(minX, x) | ||
minY = Math.min(minY, y) | ||
minZ = Math.min(minZ, z) | ||
maxX = Math.max(maxX, x) | ||
maxY = Math.max(maxY, y) | ||
maxZ = Math.max(maxZ, z) | ||
} | ||
const centerX = (minX + maxX) / 2 | ||
const centerY = (minY + maxY) / 2 | ||
const centerZ = (minZ + maxZ) / 2 | ||
let maxRadiusSq = -Infinity | ||
for (let i = 0, l = vertCount * 3; i < l; i += 3) { | ||
const x = mesh.positions[i] | ||
const y = mesh.positions[i + 1] | ||
const z = mesh.positions[i + 2] | ||
const dx = x - centerX | ||
const dy = y - centerY | ||
const dz = z - centerZ | ||
maxRadiusSq = Math.max(maxRadiusSq, dx * dx + dy * dy + dz * dz) | ||
} | ||
const radius = Math.sqrt(maxRadiusSq) | ||
mesh.bounds.minX = minX | ||
mesh.bounds.minY = minY | ||
mesh.bounds.minZ = minZ | ||
mesh.bounds.maxX = maxX | ||
mesh.bounds.maxY = maxY | ||
mesh.bounds.maxZ = maxZ | ||
mesh.bounds.centerX = centerX | ||
mesh.bounds.centerY = centerY | ||
mesh.bounds.centerZ = centerZ | ||
mesh.bounds.radius = radius | ||
mesh.indices = new Uint32Array(mesh.indices.buffer, mesh.indices.byteOffset, mesh.indicesIndex) | ||
@@ -482,3 +527,3 @@ mesh.positions = new Float32Array(mesh.positions.buffer, mesh.positions.byteOffset, vertCount * 3) | ||
static _generateShellFace (model, buffers, faceIndex, mesh, distanceX, distanceY, distanceZ, colorR, colorG, colorB, material) { | ||
const { faceVertIndices, faceVertBothNormalX, faceVertBothNormalY, faceVertBothNormalZ, faceVertSmoothNormalX, faceVertSmoothNormalY, faceVertSmoothNormalZ, faceVertFlatNormalX, faceVertFlatNormalY, faceVertFlatNormalZ, faceVertNormalX, faceVertNormalY, faceVertNormalZ, vertX, vertY, vertZ, faceVertColorR, faceVertColorG, faceVertColorB, faceVertUs, faceVertVs, faceSmooth } = buffers | ||
const { faceVertIndices, faceVertBothNormalX, faceVertBothNormalY, faceVertBothNormalZ, faceVertSmoothNormalX, faceVertSmoothNormalY, faceVertSmoothNormalZ, faceVertFlatNormalX, faceVertFlatNormalY, faceVertFlatNormalZ, vertX, vertY, vertZ, faceVertUs, faceVertVs, faceSmooth } = buffers | ||
@@ -485,0 +530,0 @@ const vert0Index = faceVertIndices[faceIndex * 4] |
/* global THREE */ | ||
export default class SvoxToThreeMeshConverter { | ||
static generate (model) { | ||
static generate (svoxMesh) { | ||
const materials = [] | ||
model.materials.forEach(function (material) { | ||
svoxMesh.materials.forEach(function (material) { | ||
materials.push(SvoxToThreeMeshConverter._generateMaterial(material)) | ||
@@ -12,39 +12,61 @@ }, this) | ||
const geometry = new THREE.BufferGeometry() | ||
SvoxToThreeMeshConverter.updateGeometry(svoxMesh, geometry, true) | ||
const mesh = new THREE.Mesh(geometry, materials) | ||
// return new THREE.VertexNormalsHelper(mesh, 0.1); | ||
// return new THREE.FaceNormalsHelper(mesh, 0.1); | ||
return mesh | ||
} | ||
static updateGeometry (svoxMesh, geometry, addGroups = true) { | ||
for (const attribute of Object.keys(geometry.attributes)) { | ||
geometry.deleteAttribute(attribute) | ||
} | ||
let boundingBox = geometry.boundingBox | ||
let boundingSphere = geometry.boundingSphere | ||
const { positions, normals, colors, bounds, uvs, data, indices } = svoxMesh | ||
if (!boundingBox) { | ||
boundingBox = geometry.boundingBox = new THREE.Box3() | ||
} | ||
if (!boundingSphere) { | ||
boundingSphere = geometry.boundingSphere = new THREE.Sphere() | ||
} | ||
boundingBox.min.set(bounds.minX, bounds.minY, bounds.minZ) | ||
boundingBox.max.set(bounds.minX, bounds.minY, bounds.minZ) | ||
boundingSphere.center.set(bounds.centerX, bounds.centerY, bounds.centerZ) | ||
boundingSphere.radius = bounds.radius | ||
// Set the geometry attribute buffers from the model | ||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(model.positions, 3)) | ||
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(model.normals, 3)) | ||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(model.colors, 3)) | ||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)) | ||
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3)) | ||
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)) | ||
if (model.uvs) { geometry.setAttribute('uv', new THREE.Float32BufferAttribute(model.uvs, 2)) } | ||
if (uvs) { | ||
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2)) | ||
} | ||
if (model.data) { | ||
for (let d = 0; d < model.data.length; d++) { | ||
geometry.setAttribute(model.data[d].name, new THREE.Float32BufferAttribute(model.data[d].values, model.data[d].width)) | ||
if (data) { | ||
for (let d = 0; d < data.length; d++) { | ||
geometry.setAttribute(data[d].name, new THREE.Float32BufferAttribute(data[d].values, data[d].width)) | ||
} | ||
} | ||
geometry.setIndex(new THREE.BufferAttribute(model.indices, 1)) | ||
geometry.setIndex(new THREE.BufferAttribute(indices, 1)) | ||
geometry.clearGroups() | ||
// let indices = []; | ||
// for (let i = 0; i < model.positions.length / 3; i++) { | ||
// indices.push(i); | ||
// } | ||
// geometry.setIndex(indices); | ||
if (addGroups) { | ||
// Add the groups for each material | ||
model.groups.forEach(function (group) { | ||
geometry.addGroup(group.start, group.count, group.materialIndex) | ||
}, this) | ||
svoxMesh.groups.forEach(function (group) { | ||
geometry.addGroup(group.start, group.count, group.materialIndex) | ||
}, this) | ||
} else { | ||
geometry.setDrawRange(0, indices.length) | ||
} | ||
geometry.computeBoundingBox() | ||
geometry.uvsNeedUpdate = true | ||
// geometry = THREE.BufferGeometryUtils.mergeVertices(geometry); | ||
const mesh = new THREE.Mesh(geometry, materials) | ||
// return new THREE.VertexNormalsHelper(mesh, 0.1); | ||
// return new THREE.FaceNormalsHelper(mesh, 0.1); | ||
return mesh | ||
} | ||
@@ -51,0 +73,0 @@ |
@@ -7,5 +7,5 @@ import Bits from './bits' | ||
const VERSION = 0 | ||
const EMPTY_VOXEL_PALETTE_INDEX = 0 | ||
export const EMPTY_VOXEL_PALETTE_INDEX = 0 | ||
// Max size allowed for chunks by default. | ||
// Max size allowed for voxels by default. | ||
export const MAX_SIZE = 128 | ||
@@ -20,9 +20,9 @@ | ||
const VOXEL_TYPE_REMOVE = 0xff | ||
const REMOVE_VOXEL_COLOR = (VOXEL_TYPE_REMOVE << 24) >>> 0 | ||
export const REMOVE_VOXEL_COLOR = (VOXEL_TYPE_REMOVE << 24) >>> 0 | ||
const VOX_CHUNK_FILTERS = { | ||
export const VOXEL_FILTERS = { | ||
NONE: 0, | ||
// Filters out cells in the provided chunk that are not also in the target chunk | ||
// Filters out cells in the provided voxels that are not also in the target voxels | ||
PAINT: 1, | ||
// Filters out cells in the provided chunk that are in the target chunk | ||
// Filters out cells in the provided voxels that are in the target voxels | ||
KEEP: 2 | ||
@@ -291,6 +291,6 @@ } | ||
filterByChunk (targetChunk, offsetX, offsetY, offsetZ, filter) { | ||
if (filter === VOX_CHUNK_FILTERS.NONE) return | ||
filterByVoxels (targetVoxels, offsetX, offsetY, offsetZ, filter) { | ||
if (filter === VOXEL_FILTERS.NONE) return | ||
const targetSize = targetChunk.size | ||
const targetSize = targetVoxels.size | ||
const [targetMinX, targetMaxX, targetMinY, targetMaxY, targetMinZ, targetMaxZ] = xyzRangeForSize(targetSize) | ||
@@ -321,7 +321,7 @@ | ||
const targetHasVoxel = !targetOutOfRange && targetChunk.hasVoxelAt(targetX, targetY, targetZ) | ||
const targetHasVoxel = !targetOutOfRange && targetVoxels.hasVoxelAt(targetX, targetY, targetZ) | ||
if ( | ||
(filter === VOX_CHUNK_FILTERS.PAINT && !targetHasVoxel) || | ||
(filter === VOX_CHUNK_FILTERS.KEEP && targetHasVoxel) | ||
(filter === VOXEL_FILTERS.PAINT && !targetHasVoxel) || | ||
(filter === VOXEL_FILTERS.KEEP && targetHasVoxel) | ||
) { | ||
@@ -382,8 +382,8 @@ this.setEmptyAt(x, y, z) | ||
// Resizes this chunk to the specified size. | ||
// Resizes this voxels to the specified size. | ||
resizeTo (size) { | ||
if (this.size[0] >= size[0] && this.size[1] >= size[1] && this.size[2] >= size[2]) return | ||
// Create a new chunk and cpoy this chunk into it. | ||
const chunk = new Voxels(size) | ||
// Create a new voxels and cpoy these voxels into it. | ||
const voxels = new Voxels(size) | ||
const [minX, maxX, minY, maxY, minZ, maxZ] = xyzRangeForSize(this.size) | ||
@@ -397,3 +397,3 @@ | ||
if (idx !== 0) { | ||
chunk.setColorAt(x, y, z, this.getColorAt(x, y, z)) | ||
voxels.setColorAt(x, y, z, this.getColorAt(x, y, z)) | ||
} | ||
@@ -404,6 +404,6 @@ } | ||
// Reach in and grab buffer from the new chunk and assign it to this chunk. | ||
const { buffer } = chunk.palette; | ||
// Reach in and grab buffer from the new voxels and assign it to this voxels. | ||
const { buffer } = voxels.palette; | ||
[this.size[0], this.size[1], this.size[2]] = size | ||
const { bitsPerIndex } = chunk | ||
const { bitsPerIndex } = voxels | ||
this.bitsPerIndex = bitsPerIndex | ||
@@ -415,3 +415,3 @@ | ||
this.indices = indices | ||
this._refCounts = chunk._refCounts | ||
this._refCounts = voxels._refCounts | ||
this._recomputeSizeFieldsForBuffer(buffer) | ||
@@ -425,3 +425,3 @@ } | ||
// Slow, only use for tests + debugging | ||
const chunk = new Voxels(size) | ||
const voxels = new Voxels(size) | ||
@@ -436,5 +436,5 @@ // Set colors in palette order, so palette ends up matching order specified. | ||
const color = palette[paletteIndex - RESERVED_PALETTE_INDEXES] | ||
chunk.setColorAtOffset(j, color) | ||
voxels.setColorAtOffset(j, color) | ||
} else if (paletteIndex === i) { | ||
chunk.setPaletteIndexAtOffset(j, paletteIndex) | ||
voxels.setPaletteIndexAtOffset(j, paletteIndex) | ||
} | ||
@@ -445,3 +445,3 @@ } | ||
return chunk | ||
return voxels | ||
} | ||
@@ -478,5 +478,5 @@ | ||
this.size, | ||
this.bitsPerIndex, | ||
this.palette.buffer.slice(0), | ||
this.indices.view.buffer.slice(0), | ||
this.bitsPerIndex, | ||
this.palette.byteOffset, | ||
@@ -508,6 +508,6 @@ this.palette.byteLength, | ||
applyToChunk (targetChunk, offsetX = 0, offsetY = 0, offsetZ = 0) { | ||
applyToVoxels (targetVoxels, offsetX = 0, offsetY = 0, offsetZ = 0) { | ||
iPalOpToIPalSnap.clear() | ||
let targetSize = targetChunk.size | ||
let targetSize = targetVoxels.size | ||
let [targetMinX, targetMaxX, targetMinY, targetMaxY, targetMinZ, targetMaxZ] = xyzRangeForSize(targetSize) | ||
@@ -534,3 +534,3 @@ | ||
// Check for chunk resize | ||
// Check for voxels resize | ||
// If target we need to write to is out of range, resize frame. | ||
@@ -567,4 +567,4 @@ if (targetX > targetMaxX) { | ||
if (targetSize[0] < requiredSizeX || targetSize[1] < requiredSizeY || targetSize[2] < requiredSizeZ) { | ||
targetChunk.resizeTo([requiredSizeX, requiredSizeY, requiredSizeZ]) | ||
targetSize = targetChunk.size; | ||
targetVoxels.resizeTo([requiredSizeX, requiredSizeY, requiredSizeZ]) | ||
targetSize = targetVoxels.size; | ||
@@ -584,6 +584,6 @@ [targetMinX, targetMaxX, targetMinY, targetMaxY, targetMinZ, targetMaxZ] = xyzRangeForSize(targetSize) | ||
// For remove, set target to empty cell | ||
targetChunk.setEmptyAt(targetX, targetY, targetZ) | ||
targetVoxels.setEmptyAt(targetX, targetY, targetZ) | ||
} else { | ||
// Otherwise, set the color and potentially expand target palette. | ||
const iPalSnap = targetChunk.setColorAt(targetX, targetY, targetZ, color) | ||
const iPalSnap = targetVoxels.setColorAt(targetX, targetY, targetZ, color) | ||
@@ -593,6 +593,6 @@ iPalOpToIPalSnap.set(iPalOp, iPalSnap) | ||
} else { | ||
const currentIPalSnap = targetChunk.getPaletteIndexAt(targetX, targetY, targetZ) | ||
const currentIPalSnap = targetVoxels.getPaletteIndexAt(targetX, targetY, targetZ) | ||
if (currentIPalSnap !== newIPalSnap) { | ||
targetChunk.setPaletteIndexAt(targetX, targetY, targetZ, newIPalSnap) | ||
targetVoxels.setPaletteIndexAt(targetX, targetY, targetZ, newIPalSnap) | ||
} | ||
@@ -606,6 +606,6 @@ } | ||
createInverse = (targetChunk, offset) => { | ||
createInverse = (targetVoxels, offset) => { | ||
iPalOpToIPalSnap.clear() | ||
const targetSize = targetChunk.size | ||
const targetSize = targetVoxels.size | ||
const [targetMinX, targetMaxX, targetMinY, targetMaxY, targetMinZ, targetMaxZ] = xyzRangeForSize(targetSize) | ||
@@ -637,7 +637,7 @@ | ||
targetZ < targetMinZ || | ||
!targetChunk.hasVoxelAt(targetX, targetY, targetZ) | ||
!targetVoxels.hasVoxelAt(targetX, targetY, targetZ) | ||
) { | ||
inverse.setColorAt(x, y, z, REMOVE_VOXEL_COLOR) | ||
} else { | ||
const currentColor = targetChunk.getColorAt(targetX, targetY, targetZ) | ||
const currentColor = targetVoxels.getColorAt(targetX, targetY, targetZ) | ||
inverse.setColorAt(x, y, z, currentColor) | ||
@@ -652,6 +652,6 @@ } | ||
// Given the target chunk, merge this chunk with it, where the merge operation resolves cell-level | ||
// Given the target voxels, merge this voxels with it, where the merge operation resolves cell-level | ||
// conflicts by removing voxels of this patch. If targetAlwaysWins is true, then any voxel in the target | ||
// chunk will be removed from this patch, otherwise it will be removed if the target has a higher value. | ||
mergeWith (targetChunk, offset, targetOffset, targetAlwaysWins = false) { | ||
// voxels will be removed from this patch, otherwise it will be removed if the target has a higher value. | ||
mergeWith (targetVoxels, offset, targetOffset, targetAlwaysWins = false) { | ||
iPalOpToIPalSnap.clear() | ||
@@ -666,3 +666,3 @@ | ||
const targetSize = targetChunk.size | ||
const targetSize = targetVoxels.size | ||
const [targetMinX, targetMaxX, targetMinY, targetMaxY, targetMinZ, targetMaxZ] = xyzRangeForSize(targetSize) | ||
@@ -692,3 +692,3 @@ | ||
const targetHasVoxel = !targetOutOfRange && targetChunk.hasVoxelAt(targetX, targetY, targetZ) | ||
const targetHasVoxel = !targetOutOfRange && targetVoxels.hasVoxelAt(targetX, targetY, targetZ) | ||
@@ -704,3 +704,3 @@ // If the target doesn't have a voxel here, no chance of a conflict | ||
// just remove this voxel from this patch. | ||
if (targetAlwaysWins || targetChunk.getColorAt(targetX, targetY, targetZ) > this.getColorAt(x, y, z)) { | ||
if (targetAlwaysWins || targetVoxels.getColorAt(targetX, targetY, targetZ) > this.getColorAt(x, y, z)) { | ||
this.removeVoxelAt(x, y, z) | ||
@@ -707,0 +707,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1917857
69
17890
20