forked from mirror/ObjToSchematic
Update
This commit is contained in:
parent
fd892fd4c0
commit
dd5683d728
@ -22,16 +22,12 @@
|
||||
},
|
||||
"homepage": "https://github.com/LucasDower/ObjToSchematic#readme",
|
||||
"devDependencies": {
|
||||
"@types/jquery": "^3.5.6",
|
||||
"@types/pngjs": "^6.0.1",
|
||||
"electron": "^13.1.4",
|
||||
"electron-packager": "^15.2.0",
|
||||
"expand-vertex-data": "^1.1.2",
|
||||
"pngjs": "^6.0.0",
|
||||
"prismarine-nbt": "^1.6.0",
|
||||
"ts-node": "^10.1.0",
|
||||
"twgl.js": "^4.19.1",
|
||||
"typescript": "^4.3.5",
|
||||
"wavefront-obj-parser": "^2.0.1"
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"twgl.js": "^4.19.1",
|
||||
|
@ -1,9 +1,9 @@
|
||||
const { Renderer } = require('./renderer.js');
|
||||
const { Mesh } = require('./mesh.js');
|
||||
const { VoxelManager } = require('./voxel_manager.js');
|
||||
const { Vector3 } = require('./vector.js');
|
||||
const { Schematic } = require('./schematic.js');
|
||||
const dialog = require('electron').remote.dialog;
|
||||
import { Renderer } from "./renderer";
|
||||
import { Mesh } from "./mesh.js";
|
||||
import { VoxelManager } from "./voxel_manager.js";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { Schematic } from "./schematic.js";
|
||||
//const dialog = from 'electron').remote.dialog;
|
||||
|
||||
|
||||
class AppContext {
|
@ -1,3 +1,5 @@
|
||||
import { AppContext } from "./dist/app_context.js";
|
||||
|
||||
const { AppContext } = require('./dist/app_context.js');
|
||||
|
||||
const context = new AppContext();
|
310
src/mesh.ts
Normal file
310
src/mesh.ts
Normal file
@ -0,0 +1,310 @@
|
||||
import * as twgl from "twgl.js";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import {expandVertexData} from "expand-vertex-data";
|
||||
|
||||
import { Triangle } from "./triangle";
|
||||
import { Vector3 } from "./vector";
|
||||
import { RGB } from "./util";
|
||||
|
||||
|
||||
interface objData {
|
||||
vertexNormals: Array<number>;
|
||||
vertexUVs: Array<number>;
|
||||
vertexPositions: Array<number>;
|
||||
vertexNormalIndices: Array<number>;
|
||||
vertexUVIndices: Array<number>;
|
||||
vertexPositionIndices: Array<number>;
|
||||
vertexMaterial: Array<string>;
|
||||
};
|
||||
|
||||
interface Materials {
|
||||
[materialName: string]: (FillMaterial | TextureMaterial)
|
||||
}
|
||||
|
||||
interface FillMaterial {
|
||||
readonly type: MaterialType.Fill
|
||||
diffuseColour: RGB
|
||||
}
|
||||
|
||||
interface TextureMaterial {
|
||||
readonly type: MaterialType.Texture
|
||||
texturePath: string,
|
||||
texture?: WebGLTexture
|
||||
}
|
||||
|
||||
export enum MaterialType {
|
||||
Texture,
|
||||
Fill
|
||||
}
|
||||
|
||||
interface MaterialTriangles {
|
||||
material: (FillMaterial | TextureMaterial);
|
||||
triangles: Array<Triangle>
|
||||
}
|
||||
|
||||
export class Mesh {
|
||||
|
||||
public materialTriangles: {[materialName: string]: Array<MaterialTriangles>};
|
||||
|
||||
private _gl: WebGLRenderingContext;
|
||||
private objPath: path.ParsedPath;
|
||||
private mtlPath?: string;
|
||||
//private _materials: Materials;
|
||||
private _data: {
|
||||
position: Float32Array;
|
||||
texcoord: Float32Array;
|
||||
indices: Uint16Array;
|
||||
materialNames: Array<string>;
|
||||
}
|
||||
private _materialIndices: {[materialName: string]: Array<number>}
|
||||
|
||||
constructor(objPathString: string, gl: WebGLRenderingContext) {
|
||||
|
||||
this.objPath = path.parse(objPathString);
|
||||
this.materialTriangles = {};
|
||||
this._gl = gl;
|
||||
|
||||
// Parse .obj
|
||||
const wavefrontString = fs.readFileSync(objPathString).toString('utf8');
|
||||
const parsedJSON = this._parseWavefrontObj(wavefrontString);
|
||||
|
||||
if (this.mtlPath) {
|
||||
if (!path.isAbsolute(this.mtlPath)) {
|
||||
this.mtlPath = path.join(this.objPath.dir, this.mtlPath);
|
||||
}
|
||||
} else {
|
||||
throw Error("No .mtl file found.");
|
||||
}
|
||||
|
||||
// Parse .mtl
|
||||
const materialString = fs.readFileSync(this.mtlPath).toString('utf8');
|
||||
const materials = this._parseMaterial(materialString);
|
||||
|
||||
// FIXME: Fix quad faces
|
||||
const expanded = expandVertexData(parsedJSON, {facesToTriangles: true});
|
||||
|
||||
this._data = {
|
||||
position: expanded.positions,
|
||||
texcoord: expanded.uvs,
|
||||
indices: expanded.positionIndices,
|
||||
materialNames: parsedJSON.vertexMaterial
|
||||
};
|
||||
|
||||
this._materialIndices = {};
|
||||
for (let i = 0; i < parsedJSON.vertexMaterial.length; ++i) {
|
||||
const materialName = parsedJSON.vertexMaterial[i];
|
||||
const index = expanded.positionIndices[i];
|
||||
if (this._materialIndices[materialName]) {
|
||||
this._materialIndices[materialName].push(index);
|
||||
} else {
|
||||
this._materialIndices[materialName] = [index];
|
||||
}
|
||||
}
|
||||
|
||||
this._parseTriangles(materials);
|
||||
this._loadTextures(materials);
|
||||
}
|
||||
|
||||
private _addMaterial(materialsJSON: Materials, materialName: string, materialDiffuseColour: RGB, materialDiffuseTexturePath: string) {
|
||||
if (materialDiffuseTexturePath !== "") {
|
||||
materialsJSON[materialName] = {
|
||||
texturePath: materialDiffuseTexturePath,
|
||||
type: MaterialType.Texture
|
||||
};
|
||||
} else if (materialName !== "") {
|
||||
materialsJSON[materialName] = {
|
||||
diffuseColour: materialDiffuseColour,
|
||||
type: MaterialType.Fill
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private _parseMaterial(materialString: string): Materials {
|
||||
var materialsJSON: Materials = {};
|
||||
|
||||
const lines = materialString.split('\n');
|
||||
|
||||
let materialName: string = "";
|
||||
let materialDiffuseColour: RGB = {r: 1.0, g: 1.0, b: 1.0};
|
||||
let materialDiffuseTexturePath!: string;
|
||||
|
||||
for (let i = 0; i < lines.length; ++i) {
|
||||
const line = lines[i];
|
||||
const lineTokens = line.trim().split(/\s+/);
|
||||
|
||||
switch (lineTokens[0]) {
|
||||
case "newmtl":
|
||||
this._addMaterial(materialsJSON, materialName, materialDiffuseColour, materialDiffuseTexturePath);
|
||||
materialName = lineTokens[1];
|
||||
break;
|
||||
|
||||
case "Kd":
|
||||
const diffuseColour = lineTokens.slice(1).map(x => parseFloat(x))
|
||||
if (!diffuseColour || diffuseColour.length != 3) {
|
||||
throw Error(`Could not parse .mtl file. (Line ${i+1})`);
|
||||
}
|
||||
if (diffuseColour.some(x => Number.isNaN(x))) {
|
||||
throw Error(`Could not parse .mtl file. (Line ${i+1})`);
|
||||
}
|
||||
materialDiffuseColour = {
|
||||
r: diffuseColour[0], g: diffuseColour[1], b: diffuseColour[2]
|
||||
};
|
||||
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);
|
||||
}
|
||||
if (!fs.existsSync(texturePath)) {
|
||||
console.error(texturePath);
|
||||
throw Error(`Cannot load texture ${texturePath}`);
|
||||
}
|
||||
const _path = path.parse(texturePath);
|
||||
if (_path.ext.toLowerCase() != ".png") {
|
||||
throw Error(`Can only load .png textures`);
|
||||
}
|
||||
|
||||
materialDiffuseTexturePath = texturePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._addMaterial(materialsJSON, materialName, materialDiffuseColour, materialDiffuseTexturePath);
|
||||
|
||||
return materialsJSON;
|
||||
}
|
||||
|
||||
/*
|
||||
DISCLAIMER: This is a modified version of wavefront-obj-parser
|
||||
to include .mtl data (https://www.npmjs.com/package/wavefront-obj-parser)
|
||||
*/
|
||||
// TODO: Just re-write this whole thing, wtf have I done, these types tho
|
||||
_parseWavefrontObj(wavefrontString: string): objData {
|
||||
|
||||
const vertexInfoNameMap: {[key: string]: string} = {v: 'vertexPositions', vt: 'vertexUVs', vn: 'vertexNormals'};
|
||||
|
||||
var parsedJSON: {[key: string]: Array<number>} = {
|
||||
vertexNormals: [],
|
||||
vertexUVs: [],
|
||||
vertexPositions: [],
|
||||
vertexNormalIndices: [],
|
||||
vertexUVIndices: [],
|
||||
vertexPositionIndices: [],
|
||||
};
|
||||
var vertexMaterial: Array<string> = [];
|
||||
|
||||
var linesInWavefrontObj = wavefrontString.split('\n');
|
||||
var currentMaterial: string = "";
|
||||
|
||||
// Loop through and parse every line in our .obj file
|
||||
for (let i = 0; i < linesInWavefrontObj.length; i++) {
|
||||
const currentLine = linesInWavefrontObj[i];
|
||||
// Tokenize our current line
|
||||
const currentLineTokens = currentLine.trim().split(/\s+/);
|
||||
// vertex position, vertex texture, or vertex normal
|
||||
const vertexInfoType = vertexInfoNameMap[currentLineTokens[0]];
|
||||
|
||||
if (vertexInfoType) {
|
||||
for (let k = 1; k < currentLineTokens.length; k++) {
|
||||
parsedJSON[vertexInfoType].push(parseFloat(currentLineTokens[k]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
vertexMaterial.push(currentMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
vertexNormals: parsedJSON["vertexNormals"],
|
||||
vertexUVs: parsedJSON["vertexUVs"],
|
||||
vertexPositions: parsedJSON["vertexPositions"],
|
||||
vertexNormalIndices: parsedJSON["vertexNormalIndices"],
|
||||
vertexUVIndices: parsedJSON["vertexUVIndices"],
|
||||
vertexPositionIndices: parsedJSON["vertexPositionIndices"],
|
||||
vertexMaterial: vertexMaterial
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_parseTriangles(materials: Materials) {
|
||||
this.materialTriangles = {};
|
||||
|
||||
for (const materialName in this._materialIndices) {
|
||||
let triangles = [];
|
||||
const indices = this._materialIndices[materialName];
|
||||
|
||||
for (let i = 0; i < indices.length; i += 3) {
|
||||
const i0 = indices[i];
|
||||
const i1 = indices[i + 1];
|
||||
const i2 = indices[i + 2];
|
||||
|
||||
const v0 = this._data.position.slice(3 * i0, 3 * i0 + 3);
|
||||
const v1 = this._data.position.slice(3 * i1, 3 * i1 + 3);
|
||||
const v2 = this._data.position.slice(3 * i2, 3 * i2 + 3);
|
||||
|
||||
const uv0 = this._data.texcoord.slice(2 * i0, 2 * i0 + 2);
|
||||
const uv1 = this._data.texcoord.slice(2 * i1, 2 * i1 + 2);
|
||||
const uv2 = this._data.texcoord.slice(2 * i2, 2 * i2 + 2);
|
||||
|
||||
const v0_ = new Vector3(v0[0], v0[1], v0[2]);
|
||||
const v1_ = new Vector3(v1[0], v1[1], v1[2]);
|
||||
const v2_ = new Vector3(v2[0], v2[1], v2[2]);
|
||||
|
||||
triangles.push(new Triangle(v0_, v1_, v2_, {u: uv0[0], v: uv0[1]}, {u: uv1[0], v: uv1[1]}, {u: uv2[0], v: uv2[1]}));
|
||||
}
|
||||
|
||||
return {
|
||||
material: materials[materialName],
|
||||
triangles: triangles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_loadTextures(materials: Materials) {
|
||||
for (const materialName in materials) {
|
||||
if (materials[materialName].type == MaterialType.Texture) {
|
||||
const material = <TextureMaterial> materials[materialName];
|
||||
material.texture = twgl.createTexture(this._gl, {
|
||||
src: material.texturePath,
|
||||
mag: this._gl.LINEAR
|
||||
});
|
||||
materials[materialName] = material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.Mesh = Mesh;
|
@ -8,11 +8,9 @@ const { SegmentedBuffer, BottomlessBuffer } = require('./buffer.js');
|
||||
const { GeometryTemplates } = require('./geometry.js');
|
||||
|
||||
|
||||
class Renderer {
|
||||
export class Renderer {
|
||||
|
||||
constructor(fov, backgroundColour) {
|
||||
console.log("Renderer constructor");
|
||||
|
||||
this._backgroundColour = backgroundColour;
|
||||
this._strokeColour = new Vector3(1.0, 1.0, 1.0);
|
||||
|
||||
|
346
src/voxel_manager.ts
Normal file
346
src/voxel_manager.ts
Normal file
@ -0,0 +1,346 @@
|
||||
import { AABB, CubeAABB } from "./aabb";
|
||||
import { Vector3 } from "./vector.js";
|
||||
import { HashSet, HashMap } from "./hash_map";
|
||||
import { Texture } from "./texture";
|
||||
import { BlockAtlas } from "./block_atlas";
|
||||
import { UV, RGB } from "./util";
|
||||
import { Triangle } from "./triangle";
|
||||
import { Mesh, MaterialTypw } from "./mesh";
|
||||
|
||||
interface TriangleCubeAABBs {
|
||||
triangle: Triangle;
|
||||
AABBs: Array<CubeAABB>;
|
||||
}
|
||||
|
||||
|
||||
export class VoxelManager {
|
||||
|
||||
public voxels: Array<Vector3>;
|
||||
public voxelTexcoords: Array<UV>;
|
||||
public triangleAABBs: Array<TriangleCubeAABBs>;
|
||||
|
||||
private voxelsHash: HashSet<Vector3>;
|
||||
private blockAtlas: BlockAtlas;
|
||||
private _voxelSize: number;
|
||||
private _blockMode!: MaterialTypw;
|
||||
private _currentTexture!: Texture;
|
||||
private _currentColour!: RGB;
|
||||
|
||||
public minX = Infinity; public maxX = -Infinity;
|
||||
public minY = Infinity; public maxY = -Infinity;
|
||||
public minZ = Infinity; public maxZ = -Infinity;
|
||||
|
||||
constructor(voxelSize: number) {
|
||||
this._voxelSize = voxelSize;
|
||||
this.voxels = [];
|
||||
this.voxelTexcoords = [];
|
||||
this.triangleAABBs = [];
|
||||
|
||||
this.voxelsHash = new HashSet(2048);
|
||||
this.blockAtlas = new BlockAtlas();
|
||||
}
|
||||
|
||||
public setVoxelSize(voxelSize: number) {
|
||||
this._voxelSize = voxelSize;
|
||||
}
|
||||
|
||||
private _clearVoxels() {
|
||||
this.voxels = [];
|
||||
this.voxelTexcoords = [];
|
||||
|
||||
this.minX = Infinity;
|
||||
this.minY = Infinity;
|
||||
this.minZ = Infinity;
|
||||
this.maxX = -Infinity;
|
||||
this.maxY = -Infinity;
|
||||
this.maxZ = -Infinity;
|
||||
|
||||
this.voxelsHash = new HashSet(2048);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this.triangleAABBs = [];
|
||||
this._clearVoxels();
|
||||
}
|
||||
|
||||
private _snapToVoxelGrid(vec: Vector3) {
|
||||
return vec.copy().divScalar(this._voxelSize).round().mulScalar(this._voxelSize);
|
||||
}
|
||||
|
||||
private _getTriangleCubeAABB(triangle: Triangle) {
|
||||
const triangleAABB = triangle.getAABB();
|
||||
const gridSnappedCentre = this._snapToVoxelGrid(triangleAABB.centre);
|
||||
|
||||
let cubeAABB = new CubeAABB(gridSnappedCentre, this._voxelSize);
|
||||
while (!triangle.insideAABB(cubeAABB)) {
|
||||
cubeAABB = new CubeAABB(cubeAABB.centre, cubeAABB.width * 2.0);
|
||||
}
|
||||
|
||||
return cubeAABB;
|
||||
}
|
||||
|
||||
private _toGridPosition(vec: Vector3) {
|
||||
return new Vector3(
|
||||
Math.round(vec.x / this._voxelSize),
|
||||
Math.round(vec.y / this._voxelSize),
|
||||
Math.round(vec.z / this._voxelSize)
|
||||
);
|
||||
}
|
||||
|
||||
private _toModelPosition(vec: Vector3) {
|
||||
return new Vector3(
|
||||
vec.x * this._voxelSize,
|
||||
vec.y * this._voxelSize,
|
||||
vec.z * this._voxelSize
|
||||
);
|
||||
}
|
||||
|
||||
public isVoxelAt(pos: Vector3) {
|
||||
return this.voxelsHash.contains(pos);
|
||||
}
|
||||
|
||||
addVoxel(vec: Vector3, blockTexcoord: UV) {
|
||||
|
||||
// (0.5, 0.5, 0.5) -> (0, 0, 0);
|
||||
vec = Vector3.subScalar(vec, this._voxelSize / 2);
|
||||
|
||||
// [HACK] FIXME: Fix misaligned voxels
|
||||
// Some vec data is not not grid-snapped to voxelSize-spacing
|
||||
const test = Vector3.divScalar(vec, this._voxelSize);
|
||||
if ((test.x % 1 < 0.9 && test.x % 1 > 0.1) || (test.y % 1 < 0.9 && test.y % 1 > 0.1) || (test.z % 1 < 0.9 && test.z % 1 > 0.1)) {
|
||||
console.warn("Misaligned voxel, skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to
|
||||
const pos = this._toGridPosition(vec);
|
||||
if (this.voxelsHash.contains(pos)) {
|
||||
return;
|
||||
}
|
||||
this.voxels.push(pos);
|
||||
this.voxelTexcoords.push(blockTexcoord);
|
||||
this.voxelsHash.add(pos);
|
||||
|
||||
this.minX = Math.min(this.minX, vec.x);
|
||||
this.minY = Math.min(this.minY, vec.y);
|
||||
this.minZ = Math.min(this.minZ, vec.z);
|
||||
this.maxX = Math.max(this.maxX, vec.x);
|
||||
this.maxY = Math.max(this.maxY, vec.y);
|
||||
this.maxZ = Math.max(this.maxZ, vec.z);
|
||||
}
|
||||
|
||||
// FIXME: Fix voxel meshing for AO and textures
|
||||
/*
|
||||
_findXExtent(pos) {
|
||||
let xEnd = pos.x + 1;
|
||||
|
||||
while (this.voxelsHash.contains(new Vector3(xEnd, pos.y, pos.z)) && !this.seen.contains(new Vector3(xEnd, pos.y, pos.z))) {
|
||||
//console.log("Marking:", new Vector3(xEnd, y, z));
|
||||
this.seen.add(new Vector3(xEnd, pos.y, pos.z));
|
||||
++xEnd;
|
||||
}
|
||||
|
||||
return xEnd - 1;
|
||||
}
|
||||
|
||||
_findZExtent(pos, xEnd) {
|
||||
let canMerge = true;
|
||||
let zEnd = pos.z + 1;
|
||||
|
||||
do {
|
||||
//console.log("zEnd:", z, zEnd);
|
||||
for (let i = pos.x; i <= xEnd; ++i) {
|
||||
const here = new Vector3(i, pos.y, zEnd);
|
||||
if (!this.voxelsHash.contains(here) || this.seen.contains(here)) {
|
||||
canMerge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (canMerge) {
|
||||
// Mark all as seen
|
||||
for (let i = pos.x; i <= xEnd; ++i) {
|
||||
const here = new Vector3(i, pos.y, zEnd);
|
||||
//console.log("Marking:", new Vector3(xEnd, y, z));
|
||||
this.seen.add(here);
|
||||
}
|
||||
++zEnd;
|
||||
}
|
||||
} while (canMerge);
|
||||
|
||||
return zEnd - 1;
|
||||
}
|
||||
|
||||
_findYExtent(pos, xEnd, zEnd) {
|
||||
let canMerge = true;
|
||||
let yEnd = pos.y + 1;
|
||||
|
||||
do {
|
||||
for (let i = pos.x; i <= xEnd; ++i) {
|
||||
for (let j = pos.z; j <= zEnd; ++j) {
|
||||
const here = new Vector3(i, yEnd, j);
|
||||
if (!this.voxelsHash.contains(here) || this.seen.contains(here)) {
|
||||
canMerge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canMerge) {
|
||||
// Mark all as seen
|
||||
for (let i = pos.x; i <= xEnd; ++i) {
|
||||
for (let j = pos.z; j <= zEnd; ++j) {
|
||||
const here = new Vector3(i, yEnd, j);
|
||||
this.seen.add(here);
|
||||
}
|
||||
}
|
||||
++yEnd;
|
||||
}
|
||||
} while (canMerge);
|
||||
|
||||
return yEnd - 1;
|
||||
}
|
||||
|
||||
buildMesh() {
|
||||
|
||||
this.mesh = [];
|
||||
this.seen = new HashSet(2048);
|
||||
//console.log(this.voxelsHash);
|
||||
|
||||
const minPos = this._toGridPosition(new Vector3(this.minX, this.minY, this.minZ));
|
||||
const maxPos = this._toGridPosition(new Vector3(this.maxX, this.maxY, this.maxZ));
|
||||
|
||||
for (let y = minPos.y; y <= maxPos.y; ++y) {
|
||||
for (let z = minPos.z; z <= maxPos.z; ++z) {
|
||||
for (let x = minPos.x; x <= maxPos.x; ++x) {
|
||||
|
||||
const pos = new Vector3(x, y, z);
|
||||
|
||||
if (this.seen.contains(pos) || !this.voxelsHash.contains(pos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let xEnd = this._findXExtent(pos);
|
||||
let zEnd = this._findZExtent(pos, xEnd);
|
||||
let yEnd = this._findYExtent(pos, xEnd, zEnd);
|
||||
|
||||
let centre = new Vector3((xEnd + x)/2, (yEnd + y)/2, (zEnd + z)/2);
|
||||
let size = new Vector3(xEnd - x + 1, yEnd - y + 1, zEnd - z + 1);
|
||||
|
||||
this.mesh.push({
|
||||
centre: this._toModelPosition(centre),
|
||||
size: this._toModelPosition(size)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("Mesh:", this.mesh);
|
||||
|
||||
return this.mesh;
|
||||
}
|
||||
*/
|
||||
|
||||
public splitVoxels() {
|
||||
this._voxelSize /= 2;
|
||||
this._clearVoxels();
|
||||
|
||||
const newTriangleAABBs = [];
|
||||
|
||||
for (const {triangle, AABBs} of this.triangleAABBs) {
|
||||
const triangleAABBs = [];
|
||||
for (const AABB of AABBs) {
|
||||
for (const sub of AABB.subdivide()) {
|
||||
if (triangle.intersectAABB(sub)) {
|
||||
const voxelColour = this._getVoxelColour(triangle, sub.centre);
|
||||
const blockTexcoord = this.blockAtlas.getTexcoord(voxelColour);
|
||||
|
||||
this.addVoxel(sub.centre, blockTexcoord);
|
||||
triangleAABBs.push(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
newTriangleAABBs.push({triangle: triangle, AABBs: triangleAABBs});
|
||||
}
|
||||
|
||||
this.triangleAABBs = newTriangleAABBs;
|
||||
}
|
||||
|
||||
_getVoxelColour(triangle: Triangle, centre: Vector3): RGB {
|
||||
const p1 = triangle.v0;
|
||||
const p2 = triangle.v1;
|
||||
const p3 = triangle.v2;
|
||||
|
||||
const uv1 = triangle.uv0;
|
||||
const uv2 = triangle.uv1;
|
||||
const uv3 = triangle.uv2;
|
||||
|
||||
const f1 = Vector3.sub(p1, centre);
|
||||
const f2 = Vector3.sub(p2, centre);
|
||||
const f3 = Vector3.sub(p3, centre);
|
||||
|
||||
const a = Vector3.cross(Vector3.sub(p1, p2), Vector3.sub(p1, p3)).magnitude();
|
||||
const a1 = Vector3.cross(f2, f3).magnitude() / a;
|
||||
const a2 = Vector3.cross(f3, f1).magnitude() / a;
|
||||
const a3 = Vector3.cross(f1, f2).magnitude() / a;
|
||||
|
||||
const uv = {
|
||||
u: uv1.u * a1 + uv2.u * a2 + uv3.u * a3,
|
||||
v: uv1.v * a1 + uv2.v * a2 + uv3.v * a3
|
||||
}
|
||||
|
||||
if (this._blockMode === MaterialTypw.Texture) {
|
||||
return this._currentTexture.getRGBA(uv);
|
||||
} else {
|
||||
return this._currentColour;
|
||||
}
|
||||
}
|
||||
|
||||
voxeliseTriangle(triangle: Triangle) {
|
||||
const cubeAABB = this._getTriangleCubeAABB(triangle);
|
||||
|
||||
const triangleAABBs = [];
|
||||
|
||||
let queue = [cubeAABB];
|
||||
while (true) {
|
||||
const aabb = queue.shift();
|
||||
if (!aabb) {
|
||||
break;
|
||||
}
|
||||
if (triangle.intersectAABB(aabb)) {
|
||||
if (aabb.width > this._voxelSize) {
|
||||
// Continue to subdivide
|
||||
queue.push(...aabb.subdivide());
|
||||
} else {
|
||||
// We've reached the voxel level, stop
|
||||
const voxelColour = this._getVoxelColour(triangle, aabb.centre);
|
||||
const blockTexcoord = this.blockAtlas.getTexcoord(voxelColour);
|
||||
|
||||
this.addVoxel(aabb.centre, blockTexcoord);
|
||||
triangleAABBs.push(aabb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.triangleAABBs.push({triangle: triangle, AABBs: triangleAABBs});
|
||||
}
|
||||
|
||||
voxeliseMesh(mesh: Mesh) {
|
||||
for (const materialName in mesh.materialTriangles) {
|
||||
const material = mesh.materialTriangles[materialName];
|
||||
material.
|
||||
if ("diffuseTexturePath" in mesh._materials[material]) {
|
||||
this._currentTexture = new Texture(mesh._materials[material].diffuseTexturePath);
|
||||
this._blockMode = "TEXTURE";
|
||||
} else {
|
||||
this._currentColour = mesh._materials[material].diffuseColour;
|
||||
this._blockMode = "FILL";
|
||||
}
|
||||
for (const triangle of mesh.materialTriangles[material]) {
|
||||
this.voxeliseTriangle(triangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports.VoxelManager = VoxelManager;
|
Loading…
Reference in New Issue
Block a user