forked from mirror/ObjToSchematic
Improved block selection, more robust .mtl parser
This commit is contained in:
parent
251520afad
commit
a77c24bab2
Binary file not shown.
Before Width: | Height: | Size: 529 KiB After Width: | Height: | Size: 600 KiB |
@ -29,14 +29,15 @@ class AppContext {
|
||||
|
||||
const file = files[0];
|
||||
if (!file.name.endsWith(".obj") && !file.name.endsWith(".OBJ")) {
|
||||
this._showToast(`Could not load ${file.name}`, 'danger');
|
||||
this._showToast("Files must be .obj format", 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.loadedMesh = new Mesh(files[0].path);
|
||||
} catch (err) {
|
||||
this._showToast(`Could not load ${file.name}`, 'danger');
|
||||
//this._showToast(`Could not load ${file.name}`, 'danger');
|
||||
this._showToast(err.message, 'danger');
|
||||
console.log(err);
|
||||
return;
|
||||
}
|
||||
@ -112,6 +113,7 @@ class AppContext {
|
||||
$("#toast").addClass(`bg-${style}`);
|
||||
|
||||
$("#toastText").html(text);
|
||||
$("#toast").toast({ delay: 3000 });
|
||||
$("#toast").toast('show');
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,85 @@
|
||||
const twgl = require('twgl.js');
|
||||
const fs = require('fs');
|
||||
const { Vector3 } = require('./vector');
|
||||
const { HashMap } = require('./hash_map.js');
|
||||
|
||||
class BlockAtlas {
|
||||
|
||||
constructor(gl) {
|
||||
constructor() {
|
||||
const blocksJSONPath = "./resources/blocks.json";
|
||||
const blocksTexturePath = "./resources/blocks.png";
|
||||
|
||||
const blocksJSONString = fs.readFileSync(blocksJSONPath);
|
||||
this._blocks = JSON.parse(blocksJSONString);
|
||||
|
||||
/*
|
||||
this._blocksWebGLTexture = twgl.createTexture(gl, {
|
||||
src: blocksTexturePath,
|
||||
mag: gl.NEAREST
|
||||
});
|
||||
*/
|
||||
this._cachedBlocks = new HashMap(1024);
|
||||
//this._buildLookupTable();
|
||||
}
|
||||
|
||||
/*
|
||||
_buildLookupTable() {
|
||||
const numBlocks = Object.keys(this._blocks).length;
|
||||
const numColours = 255 * 255 * 255;
|
||||
|
||||
if (numBlocks > 65535) {
|
||||
throw Error(`Cannot pack ${numBlocks} blocks into 16-bit buffer`);
|
||||
}
|
||||
|
||||
this.blockLookupTable = new Uint16Array(numColours); // 33.16Mb
|
||||
let bufferIndex = 0;
|
||||
|
||||
let redSquaredDistances = {};
|
||||
let greenSquaredDistances = {};
|
||||
let squaredDistance = null;
|
||||
let blockChoice = null;
|
||||
let minimumSquaredDistance = null;
|
||||
|
||||
let blockIndices = {};
|
||||
let index = 0;
|
||||
for (const block in this._blocks) {
|
||||
blockIndices[block] = index;
|
||||
++index;
|
||||
}
|
||||
|
||||
for (let r = 0; r < 256; ++r) {
|
||||
|
||||
// Cache the red distances to avoid recalculation for each G and B value.
|
||||
for (const block in this._blocks) {
|
||||
redSquaredDistances[block] = Math.pow(r/255 - this._blocks[block].colour.r, 2);
|
||||
}
|
||||
|
||||
for (let g = 0; g < 256; ++g) {
|
||||
|
||||
// Cache the green distances to avoid recalculation for each B value.
|
||||
for (const block in this._blocks) {
|
||||
greenSquaredDistances[block] = Math.pow(g/255 - this._blocks[block].colour.g, 2);
|
||||
}
|
||||
|
||||
for (let b = 0; b < 256; ++b) {
|
||||
|
||||
minimumSquaredDistance = Infinity;
|
||||
blockChoice = null;
|
||||
|
||||
for (const block in this._blocks) {
|
||||
squaredDistance = redSquaredDistances[block] +
|
||||
greenSquaredDistances[block] +
|
||||
Math.pow(b/255 - this._blocks[block].colour.b, 2);
|
||||
if (squaredDistance < minimumSquaredDistance) {
|
||||
minimumSquaredDistance = squaredDistance;
|
||||
blockChoice = block;
|
||||
}
|
||||
}
|
||||
|
||||
this.blockLookupTable[bufferIndex] = blockIndices[blockChoice];
|
||||
++bufferIndex;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
getTexcoord(voxelColour) {
|
||||
const block = this._getBlock(voxelColour);
|
||||
|
||||
@ -30,6 +91,11 @@ class BlockAtlas {
|
||||
_getBlock(voxelColour) {
|
||||
voxelColour = new Vector3(voxelColour[0], voxelColour[1], voxelColour[2]);
|
||||
|
||||
let cachedBlock = this._cachedBlocks.get(voxelColour);
|
||||
if (cachedBlock) {
|
||||
return cachedBlock;
|
||||
}
|
||||
|
||||
let minDistance = Infinity;
|
||||
let blockChoice = null;
|
||||
|
||||
@ -41,15 +107,14 @@ class BlockAtlas {
|
||||
blockAvgColour.b
|
||||
);
|
||||
const distance = Vector3.sub(blockAvgColourVector, voxelColour).magnitude();
|
||||
//console.log(block, blockAvgColourVector);
|
||||
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
blockChoice = block;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(voxelColour, "choosing", blockChoice);
|
||||
|
||||
this._cachedBlocks.add(voxelColour, blockChoice);
|
||||
return blockChoice;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class ArcballCamera {
|
||||
this.zNear = zNear;
|
||||
this.zFar = zFar;
|
||||
|
||||
this.actualDistance = 6.0;
|
||||
this.actualDistance = 18.0;
|
||||
this.actualAzimuth = -1.0;
|
||||
this.actualElevation = 1.3;
|
||||
|
||||
|
@ -34,4 +34,45 @@ class HashSet {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.HashSet = HashSet;
|
||||
|
||||
class HashMap {
|
||||
|
||||
constructor(numBins) {
|
||||
this.numBins = numBins;
|
||||
this.bins = new Array(numBins);
|
||||
}
|
||||
|
||||
_getBin(key) {
|
||||
const hash = key.hash(); // A bit naughty
|
||||
return Math.abs(hash) % this.numBins;
|
||||
}
|
||||
|
||||
add(key, value) {
|
||||
const binIndex = this._getBin(key);
|
||||
//console.log(binIndex);
|
||||
|
||||
if (!this.bins[binIndex]) {
|
||||
this.bins[binIndex] = [ {key: key, value: value} ];
|
||||
} else {
|
||||
this.bins[binIndex].push({key: key, value: value});
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const binIndex = this._getBin(key);
|
||||
|
||||
if (!this.bins[binIndex]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const list = this.bins[binIndex];
|
||||
for (const item of list) {
|
||||
if (item.key.equals(key)) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.HashSet = HashSet;
|
||||
module.exports.HashMap = HashMap;
|
88
src/mesh.js
88
src/mesh.js
@ -21,7 +21,6 @@ class Mesh {
|
||||
//const mtl_path = obj_path.substring(0, obj_path.length - 3) + "mtl";
|
||||
|
||||
// Parse .obj
|
||||
console.log(objPathString);
|
||||
const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
|
||||
const parsedJSON = this._parseWavefrontObj(wavefrontString);
|
||||
|
||||
@ -33,7 +32,6 @@ class Mesh {
|
||||
}
|
||||
|
||||
// Parse .mtl
|
||||
console.log(this.mtlPath);
|
||||
const materialString = fs.readFileSync(this.mtlPath).toString('utf8');
|
||||
this._materials = this._parseMaterial(materialString);
|
||||
|
||||
@ -69,34 +67,57 @@ class Mesh {
|
||||
let currentMaterialName = null;
|
||||
let currentMaterialData = {};
|
||||
|
||||
lines.forEach((line) => {
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
const line = lines[i];
|
||||
const lineTokens = line.trim().split(/\s+/);
|
||||
|
||||
switch (lineTokens[0]) {
|
||||
case "newmtl":
|
||||
if (currentMaterialName) {
|
||||
if (!("diffuseTexturePath" in currentMaterialData) && !("diffuseColour" in currentMaterialData)) {
|
||||
currentMaterialData.diffuseColour = [1.0, 1.0, 1.0];
|
||||
}
|
||||
materialJSON[currentMaterialName] = currentMaterialData;
|
||||
}
|
||||
currentMaterialName = lineTokens[1];
|
||||
currentMaterialData = {};
|
||||
break;
|
||||
|
||||
case "Kd":
|
||||
currentMaterialData.diffuseColour = lineTokens.slice(1).map(x => parseFloat(x));
|
||||
if (!currentMaterialData.diffuseColour || currentMaterialData.diffuseColour.length != 3) {
|
||||
throw Error(`Could not parse .mtl file. (Line ${i+1})`);
|
||||
}
|
||||
if (currentMaterialData.diffuseColour.some(x => Number.isNaN(x))) {
|
||||
throw Error(`Could not parse .mtl file. (Line ${i+1})`);
|
||||
}
|
||||
break;
|
||||
|
||||
case "map_Kd":
|
||||
if (!lineTokens[1]) {
|
||||
throw Error(`No valid path to texture in .mtl file. (Line ${i+1})`);
|
||||
}
|
||||
let texturePath = lineTokens[1];
|
||||
if (!path.isAbsolute(texturePath)) {
|
||||
texturePath = path.join(this.objPath.dir, texturePath);
|
||||
} else {
|
||||
texturePath = path.parse(texturePath);
|
||||
}
|
||||
if (!fs.existsSync(texturePath)) {
|
||||
console.error(texturePath);
|
||||
throw Error(`Cannot load texture`);
|
||||
throw Error(`Cannot load texture ${texturePath}`);
|
||||
}
|
||||
const _path = path.parse(texturePath);
|
||||
if (_path.ext.toLowerCase() != ".png") {
|
||||
throw Error(`Can only load .png textures`);
|
||||
}
|
||||
|
||||
currentMaterialData.diffuseTexturePath = texturePath;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!("diffuseTexturePath" in currentMaterialData) && !("diffuseColour" in currentMaterialData)) {
|
||||
currentMaterialData.diffuseColour = [1.0, 1.0, 1.0];
|
||||
}
|
||||
materialJSON[currentMaterialName] = currentMaterialData;
|
||||
|
||||
return materialJSON;
|
||||
@ -136,34 +157,33 @@ class Mesh {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentLineTokens[0] === 'mtllib') {
|
||||
this.mtlPath = currentLineTokens[1];
|
||||
}
|
||||
|
||||
if (currentLineTokens[0] === 'usemtl') {
|
||||
currentMaterial = currentLineTokens[1];
|
||||
}
|
||||
|
||||
if (currentLineTokens[0] === 'f') {
|
||||
// Get our 4 sets of vertex, uv, and normal indices for this face
|
||||
for (let k = 1; k < 5; k++) {
|
||||
// If there is no fourth face entry then this is specifying a triangle
|
||||
// in this case we push `-1`
|
||||
// Consumers of this module should check for `-1` before expanding face data
|
||||
if (k === 4 && !currentLineTokens[4]) {
|
||||
parsedJSON.vertexPositionIndices.push(-1);
|
||||
parsedJSON.vertexUVIndices.push(-1);
|
||||
parsedJSON.vertexNormalIndices.push(-1);
|
||||
//parsedJSON.vertexMaterial.push(currentMaterial);
|
||||
} else {
|
||||
var indices = currentLineTokens[k].split('/');
|
||||
parsedJSON.vertexPositionIndices.push(parseInt(indices[0], 10) - 1); // We zero index
|
||||
parsedJSON.vertexUVIndices.push(parseInt(indices[1], 10) - 1); // our face indices
|
||||
parsedJSON.vertexNormalIndices.push(parseInt(indices[2], 10) - 1); // by subtracting 1
|
||||
parsedJSON.vertexMaterial.push(currentMaterial);
|
||||
switch (currentLineTokens[0]) {
|
||||
case "mtllib":
|
||||
this.mtlPath = currentLineTokens[1];
|
||||
break;
|
||||
case "usemtl":
|
||||
currentMaterial = currentLineTokens[1];
|
||||
break;
|
||||
case "f":
|
||||
// Get our 4 sets of vertex, uv, and normal indices for this face
|
||||
for (let k = 1; k < 5; k++) {
|
||||
// If there is no fourth face entry then this is specifying a triangle
|
||||
// in this case we push `-1`
|
||||
// Consumers of this module should check for `-1` before expanding face data
|
||||
if (k === 4 && !currentLineTokens[4]) {
|
||||
parsedJSON.vertexPositionIndices.push(-1);
|
||||
parsedJSON.vertexUVIndices.push(-1);
|
||||
parsedJSON.vertexNormalIndices.push(-1);
|
||||
//parsedJSON.vertexMaterial.push(currentMaterial);
|
||||
} else {
|
||||
var indices = currentLineTokens[k].split('/');
|
||||
parsedJSON.vertexPositionIndices.push(parseInt(indices[0], 10) - 1); // We zero index
|
||||
parsedJSON.vertexUVIndices.push(parseInt(indices[1], 10) - 1); // our face indices
|
||||
parsedJSON.vertexNormalIndices.push(parseInt(indices[2], 10) - 1); // by subtracting 1
|
||||
parsedJSON.vertexMaterial.push(currentMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsedJSON;
|
||||
|
@ -49,7 +49,7 @@ class Renderer {
|
||||
this._registerData(data);
|
||||
}
|
||||
|
||||
_registerVoxel(centre, voxelManager, blockTexcoord) {
|
||||
_registerVoxel(centre, voxelManager, blockTexcoord) {
|
||||
let occlusions = new Array(6);
|
||||
// For each face
|
||||
for (let f = 0; f < 6; ++f) {
|
||||
@ -105,7 +105,7 @@ class Renderer {
|
||||
//console.log(data);
|
||||
materialBuffer.add(data);
|
||||
});
|
||||
console.log(mesh._materials[material]);
|
||||
//console.log(mesh._materials[material]);
|
||||
this._materialBuffers.push({
|
||||
buffer: materialBuffer,
|
||||
texture: mesh._materials[material].texture,
|
||||
@ -319,7 +319,6 @@ class Renderer {
|
||||
}
|
||||
|
||||
_getNewBuffers() {
|
||||
console.log("Getting new buffer");
|
||||
const bufferSize = 16384 * 16;
|
||||
this._registerDebug = new SegmentedBuffer(bufferSize, [
|
||||
{name: 'position', numComponents: 3},
|
||||
|
Loading…
Reference in New Issue
Block a user