Skip to content

Commit

Permalink
Updated OtS_BlockMeshConverter, refactored contextual averaging
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasDower committed Oct 15, 2023
1 parent 0766f62 commit 7b61bff
Show file tree
Hide file tree
Showing 12 changed files with 636 additions and 27 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@
/Editor/build

# Sandbox
/Sandbox/node_modules
/Sandbox/node_modules
/Sandbox/lib
4 changes: 3 additions & 1 deletion Core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
"dependencies": {
"@loaders.gl/core": "^3.4.14",
"@loaders.gl/gltf": "^3.4.14",
"@types/jest": "^29.5.5",
"pako": "^2.1.0",
"prismarine-nbt": "^2.2.1",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"devDependencies": {
"@types/jest": "^29.5.5",
"@types/pako": "^2.0.1",
"jest": "^29.7.0"
}
Expand Down
255 changes: 242 additions & 13 deletions Core/src/ots_block_mesh_converter.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,53 @@
import { OtS_ReplaceMode, OtS_VoxelMesh } from './ots_voxel_mesh';
import { TAxis } from './util/type_util';
import { Vector3 } from './vector';
import { Triangle } from './triangle';
import { LinearAllocator } from './linear_allocator';
import { Axes, Ray, rayIntersectTriangle } from './ray';
import { Bounds } from './bounds';
import { RGBA, RGBAColours, RGBAUtil } from './colour';
import { OtS_Mesh, OtS_Triangle } from './ots_mesh';
import { UV } from './util';
import { OtS_VoxelMesh } from './ots_voxel_mesh';
import { OtS_BlockMesh } from './ots_block_mesh';
import { BlockMesh } from './block_mesh';
import { RGBA, RGBAColours, RGBAUtil } from './colour';
import { OtS_FaceVisibility, OtS_VoxelMesh_Neighbourhood } from './ots_voxel_mesh_neighbourhood';
import { ASSERT } from './util/error_util';
import { Vector3 } from './vector';

export type OtS_BlockData_PerBlock<T> = { name: string, colour: T }[];

export type OtS_BlockTextureData_Block = {
name: string,
textures: {
up: string,
down: string,
north: string,
south: string,
east: string,
west: string,
}
}

export type OtS_BlockData_PerFace<T> = {
blocks: OtS_BlockTextureData_Block[],
textures: { [name: string]: T },
}

type OtS_BlockMesh_DataMode<T> =
| { type: 'per-block', data: OtS_BlockData_PerBlock<T> }
| { type: 'per-face', data: OtS_BlockData_PerFace<T> }

export type OtS_FallableBehaviour = 'replace-falling' | 'replace-fallable' | 'place-string';

export type OtS_BlockMesh_ConverterConfig = {
mode: OtS_BlockMesh_DataMode<RGBA>,
dithering?: { mode: 'random' | 'ordered', magnitude: number },
fallable?: OtS_FallableBehaviour,
smoothness?: OtS_BlockMesh_DataMode<number> & { weight: number },
resolution?: number, // [1, 255]
}

export class OtS_BlockMesh_Converter {
private _config: OtS_BlockMesh_ConverterConfig;

public constructor() {
this._config = {
mode: {
type: 'per-block', data: [
{ name: 'minecraft:stone', colour: RGBAUtil.copy(RGBAColours.WHITE) }
]
}
};
}

Expand All @@ -29,6 +58,8 @@ export class OtS_BlockMesh_Converter {
public setConfig(config: OtS_BlockMesh_ConverterConfig): boolean {
// TODO: Validate

// TODO: Validata per-face data has colours for each

// TODO: Copy config

this._config = config;
Expand All @@ -38,10 +69,208 @@ export class OtS_BlockMesh_Converter {
public process(voxelMesh: OtS_VoxelMesh): OtS_BlockMesh {
const blockMesh = new OtS_BlockMesh();

for (const { position } of voxelMesh.getVoxels()) {
blockMesh.addBlock(position.x, position.y, position.z, 'minecraft:stone', true);
// TODO: Fallable
// TODO: Smoothness
// TODO: Resolution
// TODO: Dithering

let neighbourhood: OtS_VoxelMesh_Neighbourhood | undefined;
if (this._config.mode.type === 'per-face') {
neighbourhood = new OtS_VoxelMesh_Neighbourhood();
neighbourhood.process(voxelMesh, 'cardinal');
}

for (const { position, colour } of voxelMesh.getVoxels()) {
let block: (string | null) = null;
if (this._config.mode.type === 'per-block') {
block = this._findClosestBlock_PerBlock(colour);
} else {
block = this._findClosestBlock_PerFace(colour, position, neighbourhood!);
}

if (block === null) {
continue;
}

blockMesh.addBlock(position.x, position.y, position.z, block, true);
}

return blockMesh;
}

private _getBlockNames(): string[] {
if (this._config.mode.type === 'per-block') {
return Object.keys(this._config.mode.data);
} else {
return this._config.mode.data.blocks.map((block) => block.name );
}
}

private _findClosestBlock_PerBlock(desiredColour: RGBA) {
ASSERT(this._config.mode.type === 'per-block');

let bestDistance = Infinity;
let bestCandidate: (string | null) = null;

for (const { name, colour } of this._config.mode.data) {
const distance = RGBAUtil.squaredDistance(colour, desiredColour);
if (distance < bestDistance) {
bestDistance = distance;
bestCandidate = name;
}
}

return bestCandidate;
}

private _findClosestBlock_PerFace(desiredColour: RGBA, position: Vector3, neighbourhood: OtS_VoxelMesh_Neighbourhood) {
ASSERT(this._config.mode.type === 'per-face');

let bestDistance = Infinity;
let bestCandidate: (string | null) = null;

for (const block of this._config.mode.data.blocks) {
const visibility = neighbourhood.getFaceVisibility(position.x, position.y, position.z);
if (visibility === null) {
return null;
}

const averageColour: RGBA = { r: 0, g: 0, b: 0, a: 0 };
{
let count = 0;

if (visibility & OtS_FaceVisibility.Up) {
const faceTexture = block.textures.up;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.Down) {
const faceTexture = block.textures.down;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.North) {
const faceTexture = block.textures.north;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.East) {
const faceTexture = block.textures.east;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.South) {
const faceTexture = block.textures.south;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.West) {
const faceTexture = block.textures.west;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}

averageColour.r /= count;
averageColour.g /= count;
averageColour.b /= count;
averageColour.a /= count;
}

const distance = RGBAUtil.squaredDistance(averageColour, desiredColour);
if (distance < bestDistance) {
bestDistance = distance;
bestCandidate = block.name;
}
}

return bestCandidate;
}

/*
private _getBlocks(visibility: OtS_FaceVisibility): IterableIterator<{ name: string, colour: RGBA }> {
let currentIndex = 0;
const blockCount = this._config.mode.type === 'per-block'
? this._config.mode.data.length
: this._config.mode.data.blocks.length;
const getBlockColour = (index: number): { name: string, colour: RGBA } => {
if (this._config.mode.type === 'per-block') {
return this._config.mode.data[index];
} else {
const block = this._config.mode.data.blocks[index];
const averageColour: RGBA = { r: 0, g: 0, b: 0, a: 0 };
let count = 0;
if (visibility & OtS_FaceVisibility.Up) {
const faceTexture = block.textures.up;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.Down) {
const faceTexture = block.textures.down;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.North) {
const faceTexture = block.textures.north;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.East) {
const faceTexture = block.textures.east;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.South) {
const faceTexture = block.textures.south;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
if (visibility & OtS_FaceVisibility.West) {
const faceTexture = block.textures.west;
const faceColour = this._config.mode.data.textures[faceTexture];
RGBAUtil.add(averageColour, faceColour);
++count;
}
averageColour.r /= count;
averageColour.g /= count;
averageColour.b /= count;
averageColour.a /= count;
return {
name: block.name,
colour: averageColour,
}
}
}
return {
[Symbol.iterator]: function () {
return this;
},
next: () => {
if (currentIndex < blockCount) {
const block = getBlockColour(currentIndex);
++currentIndex;
return { done: false, value: block };
} else {
return { done: true, value: undefined };
}
},
};
}
*/
}
33 changes: 33 additions & 0 deletions Core/tests/ots_block_mesh_converter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { RGBAColours } from '../src/colour';
import { OtS_VoxelMesh } from '../src/ots_voxel_mesh';
import { RGBAUtil } from '../src/colour';
import { OtS_BlockMesh_Converter } from '../src/ots_block_mesh_converter';

test('Per-block', () => {
const voxelMesh = new OtS_VoxelMesh();
voxelMesh.addVoxel(0, 0, 0, RGBAUtil.copy(RGBAColours.RED));
voxelMesh.addVoxel(1, 0, 0, RGBAUtil.copy(RGBAColours.GREEN));
voxelMesh.addVoxel(2, 0, 0, RGBAUtil.copy(RGBAColours.BLUE));

const converter = new OtS_BlockMesh_Converter();
converter.setConfig({
mode: {
type: 'per-block', data: [
{ name: 'RED-BLOCK', colour: RGBAUtil.copy(RGBAColours.RED) },
{ name: 'GREEN-BLOCK', colour: RGBAUtil.copy(RGBAColours.GREEN) },
{ name: 'BLUE-BLOCK', colour: RGBAUtil.copy(RGBAColours.BLUE) }
]
},
});

const blockMesh = converter.process(voxelMesh);

expect(blockMesh.isBlockAt(0, 0, 0)).toBe(true);
expect(blockMesh.getBlockAt(0, 0, 0)?.name).toBe('RED-BLOCK');

expect(blockMesh.isBlockAt(1, 0, 0)).toBe(true);
expect(blockMesh.getBlockAt(1, 0, 0)?.name).toBe('GREEN-BLOCK');

expect(blockMesh.isBlockAt(2, 0, 0)).toBe(true);
expect(blockMesh.getBlockAt(2, 0, 0)?.name).toBe('BLUE-BLOCK');
});
3 changes: 2 additions & 1 deletion Core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,

"resolveJsonModule": true,

"outDir": "lib"
},
"include": ["src/**/*", "index.ts"]
Expand Down
2 changes: 2 additions & 0 deletions Editor/src/worker/worker_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,10 @@ export class WorkerClient {
this._bufferGenerator_BlockMesh = undefined;

const converter = new OtS_BlockMesh_Converter();
/*
converter.setConfig({
});
*/

this._loadedBlockMesh = converter.process(this._loadedVoxelMesh);

Expand Down
Loading

0 comments on commit 7b61bff

Please sign in to comment.