Refactor mesh

This commit is contained in:
Lucas Dower 2022-03-19 02:24:12 +00:00
parent 3ca4c3511e
commit 1dd7a5089d
5 changed files with 118 additions and 98 deletions

View File

@ -299,10 +299,11 @@ export class DebugGeometryTemplates {
{ name: 'colour', numComponents: 3 }, { name: 'colour', numComponents: 3 },
]); ]);
for (const tri of mesh.tris) { let v0: Vector3 = new Vector3(0, 0, 0);
const v0 = mesh.vertices[tri.positionIndices.x]; let v1: Vector3 = new Vector3(0, 0, 0);
const v1 = mesh.vertices[tri.positionIndices.y]; let v2: Vector3 = new Vector3(0, 0, 0);
const v2 = mesh.vertices[tri.positionIndices.z]; for (let triIndex = 0; triIndex < mesh.getTriangleCount(); ++triIndex) {
({ v0, v1, v2 } = mesh.getVertices(triIndex));
buffer.add(DebugGeometryTemplates.line( buffer.add(DebugGeometryTemplates.line(
v0, v1, colour, v0, v1, colour,
)); ));

View File

@ -28,26 +28,26 @@ export interface TexturedMaterial { path: string; type: MaterialType.textured }
export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)}; export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)};
export class Mesh extends Warnable { export class Mesh extends Warnable {
public vertices: Vector3[];
public normals!: Vector3[];
public uvs!: UV[];
public tris!: Tri[];
public materials!: MaterialMap;
public readonly id: string; public readonly id: string;
private _vertices: Vector3[];
private _normals!: Vector3[];
private _uvs!: UV[];
private _tris!: Tri[];
private _materials!: MaterialMap;
private _loadedTextures: { [materialName: string]: Texture }; private _loadedTextures: { [materialName: string]: Texture };
public static desiredHeight = 8.0; public static desiredHeight = 8.0;
constructor(vertices: Vector3[], normals: Vector3[], uvs: UV[], tris: Tri[], materials: MaterialMap) { constructor(vertices: Vector3[], normals: Vector3[], uvs: UV[], tris: Tri[], materials: MaterialMap) {
super(); super();
this.vertices = vertices;
this.normals = normals;
this.uvs = uvs;
this.tris = tris;
this.materials = materials;
this._loadedTextures = {};
this.id = getRandomID(); this.id = getRandomID();
this._vertices = vertices;
this._normals = normals;
this._uvs = uvs;
this._tris = tris;
this._materials = materials;
this._loadedTextures = {};
} }
public processMesh() { public processMesh() {
@ -55,58 +55,58 @@ export class Mesh extends Warnable {
this._checkMaterials(); this._checkMaterials();
this._centreMesh(); this._centreMesh();
this._scaleMesh(); this._normaliseMesh();
this._loadTextures(); this._loadTextures();
} }
public getBounds() { public getBounds() {
const bounds = Bounds.getInfiniteBounds(); const bounds = Bounds.getInfiniteBounds();
for (const vertex of this.vertices) { for (const vertex of this._vertices) {
bounds.extendByPoint(vertex); bounds.extendByPoint(vertex);
} }
return bounds; return bounds;
} }
public translateMesh(offset: Vector3) {
this._vertices.forEach((vertex) => {
vertex.add(offset);
});
}
public scaleMesh(scaleFactor: number) {
this._vertices.forEach((vertex) => {
vertex.mulScalar(scaleFactor);
});
}
private _checkMesh() { private _checkMesh() {
// TODO: Check indices exist // TODO: Check indices exist
if (this.vertices.length === 0) { if (this._vertices.length === 0) {
throw new CustomError('Loaded mesh has no vertices'); throw new CustomError('No verticies were loaded');
} }
if (this.tris.length === 0) { if (this._tris.length === 0) {
throw new CustomError('Loaded mesh has no triangles'); throw new CustomError('No triangles were loaded');
} }
// Check UVs are inside [0, 1]
/*
for (const uv of this.uvs) {
if (uv.u < 0.0 || uv.u > 1.0) {
uv.u = Math.abs(uv.u % 1);
}
if (uv.v < 0.0 || uv.v > 1.0) {
uv.v = Math.abs(uv.v % 1);
}
}
*/
} }
private _checkMaterials() { private _checkMaterials() {
if (Object.keys(this.materials).length === 0) { if (Object.keys(this._materials).length === 0) {
throw new CustomError('Loaded mesh has no materials'); throw new CustomError('Loaded mesh has no materials');
} }
// Check used materials exist // Check used materials exist
let wasRemapped = false; let wasRemapped = false;
let debugName = (Math.random() + 1).toString(36).substring(7); let debugName = (Math.random() + 1).toString(36).substring(7);
while (debugName in this.materials) { while (debugName in this._materials) {
debugName = (Math.random() + 1).toString(36).substring(7); debugName = (Math.random() + 1).toString(36).substring(7);
} }
const missingMaterials = new Set<string>(); const missingMaterials = new Set<string>();
for (const tri of this.tris) { for (const tri of this._tris) {
if (!(tri.material in this.materials)) { if (!(tri.material in this._materials)) {
missingMaterials.add(tri.material); missingMaterials.add(tri.material);
wasRemapped = true; wasRemapped = true;
tri.material = debugName; tri.material = debugName;
@ -115,21 +115,21 @@ export class Mesh extends Warnable {
if (wasRemapped) { if (wasRemapped) {
LOG_WARN('Triangles use these materials but they were not found', missingMaterials); LOG_WARN('Triangles use these materials but they were not found', missingMaterials);
this.addWarning('Some materials were not loaded correctly'); this.addWarning('Some materials were not loaded correctly');
this.materials[debugName] = { this._materials[debugName] = {
type: MaterialType.solid, type: MaterialType.solid,
colour: RGB.white, colour: RGB.white,
}; };
} }
// Check texture paths are absolute and exist // Check texture paths are absolute and exist
for (const materialName in this.materials) { for (const materialName in this._materials) {
const material = this.materials[materialName]; const material = this._materials[materialName];
if (material.type === MaterialType.textured) { if (material.type === MaterialType.textured) {
ASSERT(path.isAbsolute(material.path), 'Material texture path not absolute'); ASSERT(path.isAbsolute(material.path), 'Material texture path not absolute');
if (!fs.existsSync(material.path)) { if (!fs.existsSync(material.path)) {
this.addWarning(`Could not find ${material.path}`); this.addWarning(`Could not find ${material.path}`);
LOG_WARN(`Could not find ${material.path} for material ${materialName}, changing to solid-white material`); LOG_WARN(`Could not find ${material.path} for material ${materialName}, changing to solid-white material`);
this.materials[materialName] = { this._materials[materialName] = {
type: MaterialType.solid, type: MaterialType.solid,
colour: RGB.white, colour: RGB.white,
}; };
@ -162,12 +162,10 @@ export class Mesh extends Warnable {
LOG('Centre', centre); LOG('Centre', centre);
// Translate each triangle // Translate each triangle
this.vertices.forEach((vertex) => { this.translateMesh(centre.negate());
vertex.sub(centre);
});
} }
private _scaleMesh() { private _normaliseMesh() {
const bounds = this.getBounds(); const bounds = this.getBounds();
const size = Vector3.sub(bounds.max, bounds.min); const size = Vector3.sub(bounds.max, bounds.min);
const scaleFactor = Mesh.desiredHeight / size.y; const scaleFactor = Mesh.desiredHeight / size.y;
@ -175,16 +173,14 @@ export class Mesh extends Warnable {
if (isNaN(scaleFactor) || !isFinite(scaleFactor)) { if (isNaN(scaleFactor) || !isFinite(scaleFactor)) {
throw new CustomError('<b>Could not scale mesh correctly</b>: Mesh is likely 2D, rotate it so that it has a non-zero height'); throw new CustomError('<b>Could not scale mesh correctly</b>: Mesh is likely 2D, rotate it so that it has a non-zero height');
} else { } else {
this.vertices.forEach((vertex) => { this.scaleMesh(scaleFactor);
vertex.mulScalar(scaleFactor);
});
} }
} }
private _loadTextures() { private _loadTextures() {
this._loadedTextures = {}; this._loadedTextures = {};
for (const tri of this.tris) { for (const tri of this._tris) {
const material = this.materials[tri.material]; const material = this._materials[tri.material];
if (material.type == MaterialType.textured) { if (material.type == MaterialType.textured) {
if (!(tri.material in this._loadedTextures)) { if (!(tri.material in this._loadedTextures)) {
this._loadedTextures[tri.material] = new Texture(material.path); this._loadedTextures[tri.material] = new Texture(material.path);
@ -194,21 +190,21 @@ export class Mesh extends Warnable {
} }
public getVertices(triIndex: number) { public getVertices(triIndex: number) {
const tri = this.tris[triIndex]; const tri = this._tris[triIndex];
return { return {
v0: this.vertices[tri.positionIndices.x], v0: this._vertices[tri.positionIndices.x],
v1: this.vertices[tri.positionIndices.y], v1: this._vertices[tri.positionIndices.y],
v2: this.vertices[tri.positionIndices.z], v2: this._vertices[tri.positionIndices.z],
}; };
} }
public getUVs(triIndex: number) { public getUVs(triIndex: number) {
const tri = this.tris[triIndex]; const tri = this._tris[triIndex];
if (tri.texcoordIndices) { if (tri.texcoordIndices) {
return { return {
uv0: this.uvs[tri.texcoordIndices.x] || new UV(0.0, 0.0), uv0: this._uvs[tri.texcoordIndices.x] || new UV(0.0, 0.0),
uv1: this.uvs[tri.texcoordIndices.y] || new UV(0.0, 0.0), uv1: this._uvs[tri.texcoordIndices.y] || new UV(0.0, 0.0),
uv2: this.uvs[tri.texcoordIndices.z] || new UV(0.0, 0.0), uv2: this._uvs[tri.texcoordIndices.z] || new UV(0.0, 0.0),
}; };
} }
return { return {
@ -221,12 +217,12 @@ export class Mesh extends Warnable {
public getNormals(triIndex: number) { public getNormals(triIndex: number) {
const vertexData = this.getVertices(triIndex); const vertexData = this.getVertices(triIndex);
const faceNormal = new Triangle(vertexData.v0, vertexData.v1, vertexData.v2).getNormal(); const faceNormal = new Triangle(vertexData.v0, vertexData.v1, vertexData.v2).getNormal();
const tri = this.tris[triIndex]; const tri = this._tris[triIndex];
if (tri.normalIndices) { if (tri.normalIndices) {
return { return {
v0: this.normals[tri.normalIndices.x] || faceNormal, v0: this._normals[tri.normalIndices.x] || faceNormal,
v1: this.normals[tri.normalIndices.y] || faceNormal, v1: this._normals[tri.normalIndices.y] || faceNormal,
v2: this.normals[tri.normalIndices.z] || faceNormal, v2: this._normals[tri.normalIndices.z] || faceNormal,
}; };
} }
return { return {
@ -249,9 +245,21 @@ export class Mesh extends Warnable {
); );
} }
public getMaterialByTriangle(triIndex: number) {
return this._tris[triIndex].material;
}
public getMaterialByName(materialName: string) {
return this._materials[materialName];
}
public getMaterials() {
return this._materials;
}
public sampleMaterial(materialName: string, uv: UV, textureFiltering: TextureFiltering) { public sampleMaterial(materialName: string, uv: UV, textureFiltering: TextureFiltering) {
ASSERT(materialName in this.materials, 'Sampling material that does not exist'); ASSERT(materialName in this._materials, 'Sampling material that does not exist');
const material = this.materials[materialName]; const material = this._materials[materialName];
if (material.type === MaterialType.solid) { if (material.type === MaterialType.solid) {
return material.colour; return material.colour;
} else { } else {
@ -296,30 +304,30 @@ export class Mesh extends Warnable {
*/ */
public copy(): Mesh { public copy(): Mesh {
const newVertices = new Array<Vector3>(this.vertices.length); const newVertices = new Array<Vector3>(this._vertices.length);
for (let i = 0; i < this.vertices.length; ++i) { for (let i = 0; i < this._vertices.length; ++i) {
newVertices[i] = this.vertices[i].copy(); newVertices[i] = this._vertices[i].copy();
} }
const newNormals = new Array<Vector3>(this.normals.length); const newNormals = new Array<Vector3>(this._normals.length);
for (let i = 0; i < this.normals.length; ++i) { for (let i = 0; i < this._normals.length; ++i) {
newNormals[i] = this.normals[i].copy(); newNormals[i] = this._normals[i].copy();
} }
const newUVs = new Array<UV>(this.uvs.length); const newUVs = new Array<UV>(this._uvs.length);
for (let i = 0; i < this.uvs.length; ++i) { for (let i = 0; i < this._uvs.length; ++i) {
newUVs[i] = this.uvs[i].copy(); newUVs[i] = this._uvs[i].copy();
} }
const newTris = new Array<Tri>(this.tris.length); const newTris = new Array<Tri>(this._tris.length);
for (let i = 0; i < this.tris.length; ++i) { for (let i = 0; i < this._tris.length; ++i) {
// FIXME: Replace // FIXME: Replace
newTris[i] = JSON.parse(JSON.stringify(this.tris[i])); newTris[i] = JSON.parse(JSON.stringify(this._tris[i]));
} }
const materials: { [materialName: string]: (SolidMaterial | TexturedMaterial) } = {}; // JSON.parse(JSON.stringify(this.materials)); const materials: { [materialName: string]: (SolidMaterial | TexturedMaterial) } = {}; // JSON.parse(JSON.stringify(this.materials));
for (const materialName in this.materials) { for (const materialName in this._materials) {
const material = this.materials[materialName]; const material = this._materials[materialName];
if (material.type === MaterialType.solid) { if (material.type === MaterialType.solid) {
materials[materialName] = { materials[materialName] = {
type: MaterialType.solid, type: MaterialType.solid,
@ -337,6 +345,6 @@ export class Mesh extends Warnable {
} }
public getTriangleCount(): number { public getTriangleCount(): number {
return this.tris.length; return this._tris.length;
} }
} }

View File

@ -125,24 +125,23 @@ export class Renderer {
LOG('Using mesh'); LOG('Using mesh');
this._materialBuffers = []; this._materialBuffers = [];
for (const materialName in mesh.materials) { for (const materialName in mesh.getMaterials()) {
const materialBuffer = new RenderBuffer([ const materialBuffer = new RenderBuffer([
{ name: 'position', numComponents: 3 }, { name: 'position', numComponents: 3 },
{ name: 'texcoord', numComponents: 2 }, { name: 'texcoord', numComponents: 2 },
{ name: 'normal', numComponents: 3 }, { name: 'normal', numComponents: 3 },
]); ]);
mesh.tris.forEach((tri, triIndex) => { for (let triIndex = 0; triIndex < mesh.getTriangleCount(); ++triIndex) {
if (tri.material === materialName) { const material = mesh.getMaterialByTriangle(triIndex);
if (tri.material === materialName) { if (material === materialName) {
const uvTri = mesh.getUVTriangle(triIndex); const uvTri = mesh.getUVTriangle(triIndex);
const triGeom = GeometryTemplates.getTriangleBufferData(uvTri); const triGeom = GeometryTemplates.getTriangleBufferData(uvTri);
materialBuffer.add(triGeom); materialBuffer.add(triGeom);
}
} }
}); }
const material = mesh.materials[materialName]; const material = mesh.getMaterialByName(materialName);
if (material.type === MaterialType.solid) { if (material.type === MaterialType.solid) {
this._materialBuffers.push({ this._materialBuffers.push({
buffer: materialBuffer, buffer: materialBuffer,

View File

@ -224,6 +224,13 @@ export class Vector3 extends Hashable {
return !isNaN(this.x) && !isNaN(this.y) && !isNaN(this.z); return !isNaN(this.x) && !isNaN(this.y) && !isNaN(this.z);
} }
public negate() {
this.x = -this.x;
this.y = -this.y;
this.z = -this.z;
return this;
}
// Begin IHashable interface // Begin IHashable interface
override hash() { override hash() {
const p0 = 73856093; const p0 = 73856093;

View File

@ -7,6 +7,10 @@ import { RGB, UV } from '../util';
import { Vector3 } from '../vector'; import { Vector3 } from '../vector';
import { IVoxeliser } from './base-voxeliser'; import { IVoxeliser } from './base-voxeliser';
/**
* This voxeliser works by projecting rays onto each triangle
* on each of the principle angles and testing for intersections
*/
export class RayVoxeliser extends IVoxeliser { export class RayVoxeliser extends IVoxeliser {
private _mesh?: Mesh; private _mesh?: Mesh;
private _voxelMesh?: VoxelMesh; private _voxelMesh?: VoxelMesh;
@ -20,14 +24,15 @@ export class RayVoxeliser extends IVoxeliser {
const scale = (voxelMeshParams.desiredHeight - 1) / Mesh.desiredHeight; const scale = (voxelMeshParams.desiredHeight - 1) / Mesh.desiredHeight;
const offset = (voxelMeshParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0); const offset = (voxelMeshParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
const useMesh = mesh.copy(); const useMesh = mesh.copy();
for (let i = 0; i < useMesh.vertices.length; ++i) {
useMesh.vertices[i].mulScalar(scale).add(offset);
}
useMesh.tris.forEach((tri, index) => { useMesh.scaleMesh(scale);
const uvTriangle = useMesh.getUVTriangle(index); useMesh.translateMesh(offset);
this._voxeliseTri(uvTriangle, tri.material);
}); for (let triIndex = 0; triIndex < useMesh.getTriangleCount(); ++triIndex) {
const uvTriangle = useMesh.getUVTriangle(triIndex);
const material = useMesh.getMaterialByTriangle(triIndex);
this._voxeliseTri(uvTriangle, material);
}
return this._voxelMesh; return this._voxelMesh;
} }