diff --git a/res/shaders/block_fragment.fs b/res/shaders/block_fragment.fs index 47f7f80..70520cd 100644 --- a/res/shaders/block_fragment.fs +++ b/res/shaders/block_fragment.fs @@ -8,6 +8,82 @@ varying vec4 v_occlusion; varying vec2 v_texcoord; varying vec2 v_blockTexcoord; +float dither8x8(vec2 position, float alpha) { + int x = int(mod(position.x, 8.0)); + int y = int(mod(position.y, 8.0)); + int index = x + y * 8; + float limit = 0.0; + + if (x < 8) { + if (index == 0) limit = 0.015625; + if (index == 1) limit = 0.515625; + if (index == 2) limit = 0.140625; + if (index == 3) limit = 0.640625; + if (index == 4) limit = 0.046875; + if (index == 5) limit = 0.546875; + if (index == 6) limit = 0.171875; + if (index == 7) limit = 0.671875; + if (index == 8) limit = 0.765625; + if (index == 9) limit = 0.265625; + if (index == 10) limit = 0.890625; + if (index == 11) limit = 0.390625; + if (index == 12) limit = 0.796875; + if (index == 13) limit = 0.296875; + if (index == 14) limit = 0.921875; + if (index == 15) limit = 0.421875; + if (index == 16) limit = 0.203125; + if (index == 17) limit = 0.703125; + if (index == 18) limit = 0.078125; + if (index == 19) limit = 0.578125; + if (index == 20) limit = 0.234375; + if (index == 21) limit = 0.734375; + if (index == 22) limit = 0.109375; + if (index == 23) limit = 0.609375; + if (index == 24) limit = 0.953125; + if (index == 25) limit = 0.453125; + if (index == 26) limit = 0.828125; + if (index == 27) limit = 0.328125; + if (index == 28) limit = 0.984375; + if (index == 29) limit = 0.484375; + if (index == 30) limit = 0.859375; + if (index == 31) limit = 0.359375; + if (index == 32) limit = 0.0625; + if (index == 33) limit = 0.5625; + if (index == 34) limit = 0.1875; + if (index == 35) limit = 0.6875; + if (index == 36) limit = 0.03125; + if (index == 37) limit = 0.53125; + if (index == 38) limit = 0.15625; + if (index == 39) limit = 0.65625; + if (index == 40) limit = 0.8125; + if (index == 41) limit = 0.3125; + if (index == 42) limit = 0.9375; + if (index == 43) limit = 0.4375; + if (index == 44) limit = 0.78125; + if (index == 45) limit = 0.28125; + if (index == 46) limit = 0.90625; + if (index == 47) limit = 0.40625; + if (index == 48) limit = 0.25; + if (index == 49) limit = 0.75; + if (index == 50) limit = 0.125; + if (index == 51) limit = 0.625; + if (index == 52) limit = 0.21875; + if (index == 53) limit = 0.71875; + if (index == 54) limit = 0.09375; + if (index == 55) limit = 0.59375; + if (index == 56) limit = 1.0; + if (index == 57) limit = 0.5; + if (index == 58) limit = 0.875; + if (index == 59) limit = 0.375; + if (index == 60) limit = 0.96875; + if (index == 61) limit = 0.46875; + if (index == 62) limit = 0.84375; + if (index == 63) limit = 0.34375; + } + + return alpha < limit ? 0.0 : 1.0; +} + void main() { float u = v_texcoord.x; float v = v_texcoord.y; @@ -19,7 +95,13 @@ void main() { float g = v*(u*b + (1.0-u)*d) + (1.0-v)*(u*a + (1.0-u)*c); vec2 tex = v_blockTexcoord + (v_texcoord / (u_atlasSize * 3.0)); - vec3 diffuse = texture2D(u_texture, tex).rgb; - - gl_FragColor = vec4(diffuse * (v_lighting * g), 1.0); + vec4 diffuse = texture2D(u_texture, tex).rgba; + + float alpha = dither8x8(gl_FragCoord.xy, diffuse.a); + if (alpha < 0.5) + { + discard; + } + + gl_FragColor = vec4(diffuse.rgb * v_lighting * g, 1.0); } diff --git a/res/shaders/solid_tri_fragment.fs b/res/shaders/solid_tri_fragment.fs index 92adacd..950ac45 100644 --- a/res/shaders/solid_tri_fragment.fs +++ b/res/shaders/solid_tri_fragment.fs @@ -1,9 +1,9 @@ precision mediump float; -uniform vec3 u_fillColour; +uniform vec4 u_fillColour; varying float v_lighting; void main() { - gl_FragColor = vec4(u_fillColour * v_lighting, 1.0); + gl_FragColor = vec4(u_fillColour.rgb * v_lighting, u_fillColour.a); } diff --git a/res/shaders/solid_tri_vertex.vs b/res/shaders/solid_tri_vertex.vs index bd83be6..e83a9df 100644 --- a/res/shaders/solid_tri_vertex.vs +++ b/res/shaders/solid_tri_vertex.vs @@ -3,7 +3,7 @@ precision mediump float; uniform vec3 u_lightWorldPos; uniform mat4 u_worldViewProjection; uniform mat4 u_worldInverseTranspose; -uniform vec3 u_fillColour; +uniform vec4 u_fillColour; attribute vec3 position; attribute vec2 texcoord; diff --git a/res/shaders/texture_tri_fragment.fs b/res/shaders/texture_tri_fragment.fs index 1c05ad9..505f051 100644 --- a/res/shaders/texture_tri_fragment.fs +++ b/res/shaders/texture_tri_fragment.fs @@ -5,9 +5,104 @@ uniform sampler2D u_texture; varying float v_lighting; varying vec2 v_texcoord; +float dither8x8(vec2 position, float alpha) { + int x = int(mod(position.x, 8.0)); + int y = int(mod(position.y, 8.0)); + int index = x + y * 8; + float limit = 0.0; + + if (x < 8) { + if (index == 0) limit = 0.015625; + if (index == 1) limit = 0.515625; + if (index == 2) limit = 0.140625; + if (index == 3) limit = 0.640625; + if (index == 4) limit = 0.046875; + if (index == 5) limit = 0.546875; + if (index == 6) limit = 0.171875; + if (index == 7) limit = 0.671875; + if (index == 8) limit = 0.765625; + if (index == 9) limit = 0.265625; + if (index == 10) limit = 0.890625; + if (index == 11) limit = 0.390625; + if (index == 12) limit = 0.796875; + if (index == 13) limit = 0.296875; + if (index == 14) limit = 0.921875; + if (index == 15) limit = 0.421875; + if (index == 16) limit = 0.203125; + if (index == 17) limit = 0.703125; + if (index == 18) limit = 0.078125; + if (index == 19) limit = 0.578125; + if (index == 20) limit = 0.234375; + if (index == 21) limit = 0.734375; + if (index == 22) limit = 0.109375; + if (index == 23) limit = 0.609375; + if (index == 24) limit = 0.953125; + if (index == 25) limit = 0.453125; + if (index == 26) limit = 0.828125; + if (index == 27) limit = 0.328125; + if (index == 28) limit = 0.984375; + if (index == 29) limit = 0.484375; + if (index == 30) limit = 0.859375; + if (index == 31) limit = 0.359375; + if (index == 32) limit = 0.0625; + if (index == 33) limit = 0.5625; + if (index == 34) limit = 0.1875; + if (index == 35) limit = 0.6875; + if (index == 36) limit = 0.03125; + if (index == 37) limit = 0.53125; + if (index == 38) limit = 0.15625; + if (index == 39) limit = 0.65625; + if (index == 40) limit = 0.8125; + if (index == 41) limit = 0.3125; + if (index == 42) limit = 0.9375; + if (index == 43) limit = 0.4375; + if (index == 44) limit = 0.78125; + if (index == 45) limit = 0.28125; + if (index == 46) limit = 0.90625; + if (index == 47) limit = 0.40625; + if (index == 48) limit = 0.25; + if (index == 49) limit = 0.75; + if (index == 50) limit = 0.125; + if (index == 51) limit = 0.625; + if (index == 52) limit = 0.21875; + if (index == 53) limit = 0.71875; + if (index == 54) limit = 0.09375; + if (index == 55) limit = 0.59375; + if (index == 56) limit = 1.0; + if (index == 57) limit = 0.5; + if (index == 58) limit = 0.875; + if (index == 59) limit = 0.375; + if (index == 60) limit = 0.96875; + if (index == 61) limit = 0.46875; + if (index == 62) limit = 0.84375; + if (index == 63) limit = 0.34375; + } + + return alpha < limit ? 0.0 : 1.0; +} + +/* +const float ditherThreshold[64] = float[64]( + 0.015625, 0.51562, 0.14062, 0.64062, 0.04687, 0.54687, 0.17187, 0.67187, + 0.76562, 0.26562, 0.89062, 0.39062, 0.79687, 0.29687, 0.92187, 0.42187, + 0.20312, 0.70312, 0.07812, 0.57812, 0.23437, 0.73437, 0.10937, 0.60937, + 0.95312, 0.45312, 0.82812, 0.32812, 0.98437, 0.48437, 0.85937, 0.35937, + 0.0625, 0.5625, 0.1875, 0.6875, 0.03125, 0.53125, 0.15625, 0.65625, + 0.8125, 0.3125, 0.9375, 0.4375, 0.78125, 0.28125, 0.90625, 0.40625, + 0.25, 0.75, 0.125, 0.625, 0.21875, 0.71875, 0.09375, 0.59375, + 1.0, 0.5, 0.875, 0.375, 0.96875, 0.46875, 0.84375, 0.34375 +); +*/ + void main() { vec2 tex = vec2(v_texcoord.x, 1.0 - v_texcoord.y); - vec3 diffuse = texture2D(u_texture, tex).rgb; + vec4 diffuse = texture2D(u_texture, tex).rgba; - gl_FragColor = vec4(diffuse * v_lighting, 1.0); + float alpha = dither8x8(gl_FragCoord.xy, diffuse.a); + if (alpha < 0.5) + { + discard; + } + + gl_FragColor = vec4(diffuse.rgb * v_lighting, 1.0); } diff --git a/res/shaders/voxel_fragment.fs b/res/shaders/voxel_fragment.fs index ff067ca..3194bf1 100644 --- a/res/shaders/voxel_fragment.fs +++ b/res/shaders/voxel_fragment.fs @@ -3,7 +3,83 @@ precision mediump float; varying float v_lighting; varying vec4 v_occlusion; varying vec2 v_texcoord; -varying vec3 v_colour; +varying vec4 v_colour; + +float dither8x8(vec2 position, float alpha) { + int x = int(mod(position.x, 8.0)); + int y = int(mod(position.y, 8.0)); + int index = x + y * 8; + float limit = 0.0; + + if (x < 8) { + if (index == 0) limit = 0.015625; + if (index == 1) limit = 0.515625; + if (index == 2) limit = 0.140625; + if (index == 3) limit = 0.640625; + if (index == 4) limit = 0.046875; + if (index == 5) limit = 0.546875; + if (index == 6) limit = 0.171875; + if (index == 7) limit = 0.671875; + if (index == 8) limit = 0.765625; + if (index == 9) limit = 0.265625; + if (index == 10) limit = 0.890625; + if (index == 11) limit = 0.390625; + if (index == 12) limit = 0.796875; + if (index == 13) limit = 0.296875; + if (index == 14) limit = 0.921875; + if (index == 15) limit = 0.421875; + if (index == 16) limit = 0.203125; + if (index == 17) limit = 0.703125; + if (index == 18) limit = 0.078125; + if (index == 19) limit = 0.578125; + if (index == 20) limit = 0.234375; + if (index == 21) limit = 0.734375; + if (index == 22) limit = 0.109375; + if (index == 23) limit = 0.609375; + if (index == 24) limit = 0.953125; + if (index == 25) limit = 0.453125; + if (index == 26) limit = 0.828125; + if (index == 27) limit = 0.328125; + if (index == 28) limit = 0.984375; + if (index == 29) limit = 0.484375; + if (index == 30) limit = 0.859375; + if (index == 31) limit = 0.359375; + if (index == 32) limit = 0.0625; + if (index == 33) limit = 0.5625; + if (index == 34) limit = 0.1875; + if (index == 35) limit = 0.6875; + if (index == 36) limit = 0.03125; + if (index == 37) limit = 0.53125; + if (index == 38) limit = 0.15625; + if (index == 39) limit = 0.65625; + if (index == 40) limit = 0.8125; + if (index == 41) limit = 0.3125; + if (index == 42) limit = 0.9375; + if (index == 43) limit = 0.4375; + if (index == 44) limit = 0.78125; + if (index == 45) limit = 0.28125; + if (index == 46) limit = 0.90625; + if (index == 47) limit = 0.40625; + if (index == 48) limit = 0.25; + if (index == 49) limit = 0.75; + if (index == 50) limit = 0.125; + if (index == 51) limit = 0.625; + if (index == 52) limit = 0.21875; + if (index == 53) limit = 0.71875; + if (index == 54) limit = 0.09375; + if (index == 55) limit = 0.59375; + if (index == 56) limit = 1.0; + if (index == 57) limit = 0.5; + if (index == 58) limit = 0.875; + if (index == 59) limit = 0.375; + if (index == 60) limit = 0.96875; + if (index == 61) limit = 0.46875; + if (index == 62) limit = 0.84375; + if (index == 63) limit = 0.34375; + } + + return alpha < limit ? 0.0 : 1.0; +} void main() { float u = v_texcoord.x; @@ -15,5 +91,11 @@ void main() { float d = v_occlusion.w; float g = v*(u*b + (1.0-u)*d) + (1.0-v)*(u*a + (1.0-u)*c); - gl_FragColor = vec4(v_colour * (v_lighting * g), 1.0); + float alpha = dither8x8(gl_FragCoord.xy, v_colour.a); + if (alpha < 0.5) + { + discard; + } + + gl_FragColor = vec4(v_colour.rgb * (v_lighting * g), 1.0); } diff --git a/res/shaders/voxel_vertex.vs b/res/shaders/voxel_vertex.vs index 0018454..8096f25 100644 --- a/res/shaders/voxel_vertex.vs +++ b/res/shaders/voxel_vertex.vs @@ -6,14 +6,14 @@ uniform vec3 u_gridOffset; attribute vec3 position; attribute vec3 normal; -attribute vec3 colour; +attribute vec4 colour; attribute vec4 occlusion; attribute vec2 texcoord; varying float v_lighting; varying vec4 v_occlusion; varying vec2 v_texcoord; -varying vec3 v_colour; +varying vec4 v_colour; vec3 light = vec3(0.78, 0.98, 0.59); diff --git a/src/block_assigner.ts b/src/block_assigner.ts index f03df7f..bdc568d 100644 --- a/src/block_assigner.ts +++ b/src/block_assigner.ts @@ -1,13 +1,14 @@ import { BlockAtlas, BlockInfo } from './block_atlas'; -import { ASSERT, ColourSpace, RGB } from './util'; +import { RGBA } from './colour'; +import { ASSERT, ColourSpace } from './util'; import { Vector3 } from './vector'; interface IBlockAssigner { - assignBlock(voxelColour: RGB, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo; + assignBlock(voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo; } export class BasicBlockAssigner implements IBlockAssigner { - assignBlock(voxelColour: RGB, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { + assignBlock(voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { return BlockAtlas.Get.getBlock(voxelColour, colourSpace, exclude); } } @@ -36,7 +37,7 @@ export class OrderedDitheringBlockAssigner implements IBlockAssigner { return (OrderedDitheringBlockAssigner._mapMatrix[index] / (size * size * size)) - 0.5; } - assignBlock(voxelColour: RGB, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { + assignBlock(voxelColour: RGBA, voxelPosition: Vector3, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { const size = OrderedDitheringBlockAssigner._size; const map = this._getThresholdValue( Math.abs(voxelPosition.x % size), @@ -44,11 +45,12 @@ export class OrderedDitheringBlockAssigner implements IBlockAssigner { Math.abs(voxelPosition.z % size), ); - const newVoxelColour = new RGB( - ((255 * voxelColour.r) + map * OrderedDitheringBlockAssigner._threshold) / 255, - ((255 * voxelColour.g) + map * OrderedDitheringBlockAssigner._threshold) / 255, - ((255 * voxelColour.b) + map * OrderedDitheringBlockAssigner._threshold) / 255, - ); + const newVoxelColour: RGBA = { + r: ((255 * voxelColour.r) + map * OrderedDitheringBlockAssigner._threshold) / 255, + g: ((255 * voxelColour.g) + map * OrderedDitheringBlockAssigner._threshold) / 255, + b: ((255 * voxelColour.b) + map * OrderedDitheringBlockAssigner._threshold) / 255, + a: ((255 * voxelColour.a) + map * OrderedDitheringBlockAssigner._threshold) / 255, + }; return BlockAtlas.Get.getBlock(newVoxelColour, colourSpace, exclude); } diff --git a/src/block_atlas.ts b/src/block_atlas.ts index 1bcf0d2..224f731 100644 --- a/src/block_atlas.ts +++ b/src/block_atlas.ts @@ -1,10 +1,9 @@ -import { HashMap } from './hash_map'; -import { UV, RGB, ASSERT, fileExists, ColourSpace, ATLASES_DIR, PALETTES_DIR, AppError, LOG_WARN } from './util'; -import { Vector3 } from './vector'; +import { UV, ASSERT, fileExists, ColourSpace, ATLASES_DIR, PALETTES_DIR, AppError, LOG_WARN } from './util'; import fs from 'fs'; import path from 'path'; import { StatusHandler } from './status'; +import { RGBA, RGBAUtil } from './colour'; export interface TextureInfo { name: string @@ -23,7 +22,7 @@ export interface FaceInfo { export interface BlockInfo { name: string; - colour: RGB; + colour: RGBA; faces: FaceInfo } @@ -33,7 +32,6 @@ interface BlockPalette { /* eslint-enable */ export class BlockAtlas { - private _cachedBlocks: HashMap; private _atlasBlocks: Array; private _palette: string[]; private _atlasSize: number; @@ -48,7 +46,6 @@ export class BlockAtlas { } private constructor() { - this._cachedBlocks = new HashMap(0); this._atlasBlocks = []; this._atlasSize = 0; this._atlasLoaded = false; @@ -58,8 +55,6 @@ export class BlockAtlas { } public loadAtlas(atlasID: string) { - this._cachedBlocks = new HashMap(1024); - const atlasDir = path.join(ATLASES_DIR, atlasID + '.atlas'); ASSERT(fileExists(atlasDir), `Atlas to load does not exist ${atlasDir}`); @@ -73,11 +68,12 @@ export class BlockAtlas { this._atlasTextureID = atlasID; this._atlasBlocks = json.blocks; for (const block of this._atlasBlocks) { - block.colour = new RGB( - block.colour.r, - block.colour.g, - block.colour.b, - ); + block.colour = { + r: block.colour.r, + g: block.colour.g, + b: block.colour.b, + a: block.colour.a, + }; } if (this._atlasBlocks.length === 0) { @@ -92,8 +88,6 @@ export class BlockAtlas { public loadPalette(paletteID: string) { ASSERT(this._atlasLoaded, 'An atlas must be loaded before a palette'); - this._cachedBlocks = new HashMap(1024); - const paletteDir = path.join(PALETTES_DIR, paletteID + '.palette'); ASSERT(fileExists(paletteDir), `Palette to load does not exist ${paletteDir}`); @@ -124,15 +118,10 @@ export class BlockAtlas { this._paletteLoaded = true; } - public getBlock(voxelColour: RGB, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { + public getBlock(voxelColour: RGBA, colourSpace: ColourSpace, exclude?: string[]): BlockInfo { ASSERT(this._atlasLoaded, 'No atlas has been loaded'); ASSERT(this._paletteLoaded, 'No palette has been loaded'); - const cachedBlockIndex = this._cachedBlocks.get(voxelColour.toVector3()); - if (cachedBlockIndex && exclude === undefined) { - return this._atlasBlocks[cachedBlockIndex]; - } - let minDistance = Infinity; let blockChoiceIndex!: number; @@ -146,8 +135,8 @@ export class BlockAtlas { ASSERT(blockIndex !== undefined); const block: BlockInfo = this._atlasBlocks[blockIndex]; - const blockAvgColour = block.colour as RGB; - const distance = RGB.distance(blockAvgColour, voxelColour, colourSpace); + const blockAvgColour = block.colour as RGBA; + const distance = RGBAUtil.squaredDistance(blockAvgColour, voxelColour); if (distance < minDistance) { minDistance = distance; @@ -159,9 +148,6 @@ export class BlockAtlas { throw new AppError('The chosen palette does not have suitable blocks'); } - if (exclude === undefined) { - this._cachedBlocks.add(voxelColour.toVector3(), blockChoiceIndex); - } return this._atlasBlocks[blockChoiceIndex]; } diff --git a/src/colour.ts b/src/colour.ts new file mode 100644 index 0000000..69cfa40 --- /dev/null +++ b/src/colour.ts @@ -0,0 +1,63 @@ +export type RGBA = { + r: number, + g: number, + b: number, + a: number +} + +export namespace RGBAUtil { + export function lerp(a: RGBA, b: RGBA, alpha: number) { + return { + r: a.r * (1 - alpha) + b.r * alpha, + g: a.g * (1 - alpha) + b.g * alpha, + b: a.b * (1 - alpha) + b.b * alpha, + a: a.a * (1 - alpha) + b.a * alpha, + }; + } + + export function average(...colours: RGBA[]) { + const avg = { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }; + for (let i = 0; i < colours.length; ++i) { + avg.r += colours[i].r; + avg.g += colours[i].g; + avg.b += colours[i].b; + avg.a += colours[i].a; + } + return avg; + } + + export function squaredDistance(a: RGBA, b: RGBA) { + let squaredDistance = 0.0; + squaredDistance += Math.pow(a.r - b.r, 2); + squaredDistance += Math.pow(a.g - b.g, 2); + squaredDistance += Math.pow(a.b - b.b, 2); + squaredDistance += Math.pow(a.a - b.a, 2); + return squaredDistance; + } + + export function copy(a: RGBA): RGBA { + return { + r: a.r, + g: a.g, + b: a.b, + a: a.a, + }; + } + + export function toArray(a: RGBA): number[] { + return [a.r, a.g, a.b, a.a]; + } +} + +export namespace RGBAColours { + export const RED: RGBA = { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }; + export const GREEN: RGBA = { r: 0.0, g: 1.0, b: 0.0, a: 1.0 }; + export const BLUE: RGBA = { r: 0.0, g: 0.0, b: 1.0, a: 1.0 }; + + export const YELLOW: RGBA = { r: 1.0, g: 1.0, b: 0.0, a: 1.0 }; + export const CYAN: RGBA = { r: 0.0, g: 1.0, b: 1.0, a: 1.0 }; + export const MAGENTA: RGBA = { r: 1.0, g: 0.0, b: 1.0, a: 1.0 }; + + export const WHITE: RGBA = { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; + export const BLACK: RGBA = { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }; +} diff --git a/src/constants.ts b/src/constants.ts index c484066..973b507 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -7,7 +7,7 @@ export namespace AppConstants { export namespace ComponentSize { export const TEXCOORD = 2; export const POSITION = 3; - export const COLOUR = 3; + export const COLOUR = 4; export const NORMAL = 3; export const INDICES = 3; export const OCCLUSION = 4; diff --git a/src/geometry.ts b/src/geometry.ts index 9b5d8ea..af7bd31 100644 --- a/src/geometry.ts +++ b/src/geometry.ts @@ -2,9 +2,10 @@ import * as twgl from 'twgl.js'; import { Triangle, UVTriangle } from './triangle'; import { Vector3 } from './vector'; import { AttributeData, MergeAttributeData, RenderBuffer } from './buffer'; -import { ASSERT, Bounds, RGB, RGBA } from './util'; +import { ASSERT, Bounds } from './util'; import { Mesh } from './mesh'; import { VoxelMesh } from './voxel_mesh'; +import { RGBA } from './colour'; export class GeometryTemplates { private static readonly _default_cube = twgl.primitives.createCubeVertices(1.0); @@ -223,8 +224,8 @@ export class DebugGeometryTemplates { { name: 'position', numComponents: 3 }, { name: 'colour', numComponents: 4 }, ]); - const COLOUR_MINOR: RGBA = new RGB(0.5, 0.5, 0.5).toRGBA(0.3); - const COLOUR_MAJOR: RGBA = RGB.white.toRGBA(0.3); + const COLOUR_MINOR: RGBA = { r: 0.5, g: 0.5, b: 0.5, a: 0.3 }; + const COLOUR_MAJOR: RGBA = { r: 1.0, g: 1.0, b: 1.0, a: 0.3 }; buffer.add(DebugGeometryTemplates.line( new Vector3(-dimensions.x / 2, 0, -dimensions.z / 2), diff --git a/src/importers/obj_importer.ts b/src/importers/obj_importer.ts index 931e93d..3cf1946 100644 --- a/src/importers/obj_importer.ts +++ b/src/importers/obj_importer.ts @@ -1,12 +1,13 @@ import { MaterialType, Mesh, SolidMaterial, TexturedMaterial, Tri } from '../mesh'; import { Vector3 } from '../vector'; -import { UV, ASSERT, RGB, AppError, REGEX_NUMBER, RegExpBuilder, REGEX_NZ_ANY, LOG_ERROR } from '../util'; +import { UV, ASSERT, AppError, REGEX_NUMBER, RegExpBuilder, REGEX_NZ_ANY, LOG_ERROR } from '../util'; import { checkFractional, checkNaN } from '../math'; import fs from 'fs'; import path from 'path'; import { StatusHandler } from '../status'; import { IImporter } from './base_importer'; +import { RGBA, RGBAColours } from '../colour'; export class ObjImporter extends IImporter { private _vertices: Vector3[] = []; @@ -15,7 +16,7 @@ export class ObjImporter extends IImporter { private _tris: Tri[] = []; private _materials: {[key: string]: (SolidMaterial | TexturedMaterial)} = { - 'DEFAULT_UNASSIGNED': { type: MaterialType.solid, colour: RGB.white }, + 'DEFAULT_UNASSIGNED': { type: MaterialType.solid, colour: RGBAColours.WHITE }, }; private _mtlLibs: string[] = []; private _currentMaterialName: string = 'DEFAULT_UNASSIGNED'; @@ -187,7 +188,7 @@ export class ObjImporter extends IImporter { }, ]; - private _currentColour: RGB = RGB.black; + private _currentColour: RGBA = RGBAColours.BLACK; private _currentTexture: string = ''; private _materialReady: boolean = false; private _mtlParsers = [ @@ -218,7 +219,7 @@ export class ObjImporter extends IImporter { const b = parseFloat(match.b); checkNaN(r, g, b); checkFractional(r, g, b); - this._currentColour = new RGB(r, g, b); + this._currentColour = { r: r, g: g, b: b, a: 1.0 }; this._materialReady = true; }, }, diff --git a/src/mesh.ts b/src/mesh.ts index 2c07925..a56f296 100644 --- a/src/mesh.ts +++ b/src/mesh.ts @@ -1,12 +1,12 @@ import { Vector3 } from './vector'; import { UV, Bounds, ASSERT, AppError, LOG_WARN, getRandomID } from './util'; import { Triangle, UVTriangle } from './triangle'; -import { RGB } from './util'; import path from 'path'; import fs from 'fs'; import { Texture, TextureFiltering } from './texture'; import { StatusHandler } from './status'; +import { RGBA, RGBAColours, RGBAUtil } from './colour'; interface VertexIndices { x: number; @@ -24,7 +24,7 @@ export interface Tri { /* eslint-disable */ export enum MaterialType { solid, textured } /* eslint-enable */ -export interface SolidMaterial { colour: RGB; type: MaterialType.solid } +export interface SolidMaterial { colour: RGBA; type: MaterialType.solid } export interface TexturedMaterial { path: string; type: MaterialType.textured } export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)}; @@ -159,7 +159,7 @@ export class Mesh { ); this._materials[debugName] = { type: MaterialType.solid, - colour: RGB.white, + colour: RGBAColours.WHITE, }; } @@ -176,7 +176,7 @@ export class Mesh { LOG_WARN(`Could not find ${material.path} for material ${materialName}, changing to solid-white material`); this._materials[materialName] = { type: MaterialType.solid, - colour: RGB.white, + colour: RGBAColours.WHITE, }; } } @@ -301,14 +301,14 @@ export class Mesh { return this._materials; } - public sampleMaterial(materialName: string, uv: UV, textureFiltering: TextureFiltering) { + public sampleMaterial(materialName: string, uv: UV, textureFiltering: TextureFiltering): RGBA { ASSERT(materialName in this._materials, `Sampling material that does not exist: ${materialName}`); const material = this._materials[materialName]; if (material.type === MaterialType.solid) { return material.colour; } else { ASSERT(materialName in this._loadedTextures, 'Sampling texture that is not loaded'); - return this._loadedTextures[materialName].getRGB(uv, textureFiltering); + return this._loadedTextures[materialName].getRGBA(uv, textureFiltering); } } @@ -375,7 +375,7 @@ export class Mesh { if (material.type === MaterialType.solid) { materials[materialName] = { type: MaterialType.solid, - colour: material.colour.copy(), + colour: RGBAUtil.copy(material.colour), }; } else { materials[materialName] = { diff --git a/src/renderer.ts b/src/renderer.ts index 4d74f6b..a7fd4b9 100644 --- a/src/renderer.ts +++ b/src/renderer.ts @@ -5,12 +5,13 @@ import { RenderBuffer } from './buffer'; import { DebugGeometryTemplates, GeometryTemplates } from './geometry'; import { Mesh, SolidMaterial, TexturedMaterial, MaterialType } from './mesh'; import { BlockAtlas } from './block_atlas'; -import { ASSERT, LOG, RGB } from './util'; +import { ASSERT, LOG } from './util'; import { VoxelMesh } from './voxel_mesh'; import { BlockMesh } from './block_mesh'; import * as twgl from 'twgl.js'; import { EAppEvent, EventManager } from './event'; +import { RGBA, RGBAUtil } from './colour'; /* eslint-disable */ export enum MeshType { @@ -34,7 +35,7 @@ enum EDebugBufferComponents { export class Renderer { public _gl: WebGLRenderingContext; - private _backgroundColour = new RGB(0.125, 0.125, 0.125); + private _backgroundColour: RGBA = { r: 0.125, g: 0.125, b: 0.125, a: 1.0 }; private _atlasTexture?: WebGLTexture; private _meshToUse: MeshType = MeshType.None; @@ -84,9 +85,9 @@ export class Renderer { { name: 'position', numComponents: 3 }, { name: 'colour', numComponents: 4 }, ]); - this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(1, 0, 0), new RGB(0.96, 0.21, 0.32).toRGBA())); - this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 1, 0), new RGB(0.44, 0.64, 0.11).toRGBA())); - this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 0, 1), new RGB(0.18, 0.52, 0.89).toRGBA())); + this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(1, 0, 0), { r: 0.96, g: 0.21, b: 0.32, a: 1.0 })); + this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 1, 0), { r: 0.44, g: 0.64, b: 0.11, a: 1.0 })); + this._axisBuffer.add(DebugGeometryTemplates.arrow(new Vector3(0, 0, 0), new Vector3(0, 0, 1), { r: 0.18, g: 0.52, b: 0.89, a: 1.0 })); } public update() { @@ -189,8 +190,8 @@ export class Renderer { const dimensions = mesh.getBounds().getDimensions(); this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Grid] = DebugGeometryTemplates.grid(dimensions); - this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.meshWireframe(mesh, new RGB(0.18, 0.52, 0.89).toRGBA()); - this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Normals] = DebugGeometryTemplates.meshNormals(mesh, new RGB(0.89, 0.52, 0.18).toRGBA()); + this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.meshWireframe(mesh, { r: 0.18, g: 0.52, b: 0.89, a: 1.0 }); + this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Normals] = DebugGeometryTemplates.meshNormals(mesh, { r: 0.89, g: 0.52, b: 0.18, a: 1.0 }); delete this._debugBuffers[MeshType.TriangleMesh][EDebugBufferComponents.Dev]; this._modelsAvailable = 1; @@ -219,7 +220,7 @@ export class Renderer { dimensions.add(1); this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Grid] = DebugGeometryTemplates.grid(Vector3.mulScalar(dimensions, voxelSize), voxelSize); - this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.voxelMeshWireframe(voxelMesh, new RGB(0.18, 0.52, 0.89).toRGBA(), this._voxelSize); + this._debugBuffers[MeshType.VoxelMesh][EDebugBufferComponents.Wireframe] = DebugGeometryTemplates.voxelMeshWireframe(voxelMesh, { r: 0.18, g: 0.52, b: 0.89, a: 1.0 }, this._voxelSize); this._modelsAvailable = 2; this.setModelToUse(MeshType.VoxelMesh); @@ -290,7 +291,7 @@ export class Renderer { u_lightWorldPos: ArcballCamera.Get.getCameraPosition(0.0, 0.0), u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(), u_worldInverseTranspose: ArcballCamera.Get.getWorldInverseTranspose(), - u_fillColour: materialBuffer.material.colour.toArray(), + u_fillColour: RGBAUtil.toArray(materialBuffer.material.colour), }); } } @@ -312,6 +313,7 @@ export class Renderer { } private _drawBlockMesh() { + this._gl.enable(this._gl.CULL_FACE); const shader = ShaderManager.Get.blockProgram; const uniforms = { u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(), @@ -326,6 +328,7 @@ export class Renderer { twgl.setUniforms(shader, uniforms); this._gl.drawElements(this._gl.TRIANGLES, this._blockBuffer.numElements, this._gl.UNSIGNED_INT, 0); } + this._gl.disable(this._gl.CULL_FACE); } // ///////////////////////////////////////////////////////////////////////// diff --git a/src/texture.ts b/src/texture.ts index 9ec080d..6ecf0ef 100644 --- a/src/texture.ts +++ b/src/texture.ts @@ -1,12 +1,11 @@ import { UV, ASSERT, AppError } from './util'; -import { RGB } from './util'; import * as fs from 'fs'; import * as jpeg from 'jpeg-js'; import { PNG } from 'pngjs'; import path from 'path'; -import { Vector3 } from './vector'; import { clamp, wayThrough } from './math'; +import { RGBA, RGBAColours, RGBAUtil } from './colour'; /* eslint-disable */ export enum TextureFormat { @@ -49,15 +48,15 @@ export class Texture { } } - getRGB(uv: UV, filtering: TextureFiltering): RGB { + public getRGBA(uv: UV, filtering: TextureFiltering): RGBA { if (filtering === TextureFiltering.Nearest) { - return this._getNearestRGB(uv); + return this._getNearestRGBA(uv); } else { - return this._getLinearRGB(uv); + return this._getLinearRGBA(uv); } } - private _getLinearRGB(uv: UV): RGB { + private _getLinearRGBA(uv: UV): RGBA { uv.v = 1.0 - uv.v; uv.u = uv.u % 1.0; @@ -75,39 +74,39 @@ export class Texture { const v = wayThrough(y, yL, yU); if (!(u >= 0.0 && u <= 1.0 && v >= 0.0 && v <= 1.0)) { - return RGB.magenta; + return RGBAColours.MAGENTA; } - const A = this._getFromXY(xL, yU).toVector3(); - const B = this._getFromXY(xU, yU).toVector3(); - const midAB = Vector3.mulScalar(B, u).add(Vector3.mulScalar(A, 1.0-u)); + const A = this._getFromXY(xL, yU); + const B = this._getFromXY(xU, yU); + const AB = RGBAUtil.lerp(A, B, u); - const C = this._getFromXY(xL, yL).toVector3(); - const D = this._getFromXY(xU, yL).toVector3(); - const midCD = Vector3.mulScalar(D, u).add(Vector3.mulScalar(C, 1.0-u)); + const C = this._getFromXY(xL, yL); + const D = this._getFromXY(xU, yL); + const CD = RGBAUtil.lerp(C, D, u); - const mid = Vector3.mulScalar(midAB, v).add(Vector3.mulScalar(midCD, 1.0-v)); - return RGB.fromVector3(mid); + return RGBAUtil.lerp(AB, CD, v); } - private _getNearestRGB(uv: UV): RGB { + private _getNearestRGBA(uv: UV): RGBA { const x = Math.floor(uv.u * this._image.width); const y = Math.floor((1 - uv.v) * this._image.height); return this._getFromXY(x, y); } - private _getFromXY(x: number, y: number): RGB { + private _getFromXY(x: number, y: number): RGBA { x = clamp(x, 0, this._image.width - 1); y = clamp(y, 0, this._image.height - 1); const index = 4 * (this._image.width * y + x); const rgba = this._image.data.slice(index, index + 4); - return new RGB( - rgba[0] / 255, - rgba[1] / 255, - rgba[2] / 255, - ); + return { + r: rgba[0] / 255, + g: rgba[1] / 255, + b: rgba[2] / 255, + a: rgba[3] / 255, + }; } } diff --git a/src/util.ts b/src/util.ts index 7d0323a..e5d1e37 100644 --- a/src/util.ts +++ b/src/util.ts @@ -3,9 +3,6 @@ import { Vector3 } from './vector'; import { clamp } from './math'; import path from 'path'; - -const convert = require('color-convert'); - import fs from 'fs'; export class UV { @@ -29,111 +26,6 @@ export enum ColourSpace { } /* eslint-enable */ -export type RGBA = { - r: number, - g: number, - b: number, - a: number -} - -export class RGB { - public r: number; - public g: number; - public b: number; - - constructor(r: number, g: number, b: number) { - this.r = r; - this.g = g; - this.b = b; - } - - // Note: Uses naive sRGB Euclidian distance - public static averageFrom(colours: RGB[]): RGB { - let r = 0.0; - let g = 0.0; - let b = 0.0; - for (const colour of colours) { - r += colour.r; - g += colour.g; - b += colour.b; - } - const n = colours.length; - return new RGB(r / n, g / n, b / n); - } - - public static fromArray(array: number[]): RGB { - ASSERT(array.length === 3); - return new RGB(array[0], array[1], array[2]); - } - - public toArray(): number[] { - return [this.r, this.g, this.b]; - } - - public toRGBA(a: number = 1.0): RGBA { - return { r: this.r, g: this.g, b: this.b, a: a }; - } - - public static distance(a: RGB, b: RGB, colourSpace: ColourSpace): number { - if (colourSpace === ColourSpace.LAB) { - const aLAB = convert.rgb.lab(a.r * 255, a.g * 255, a.b * 255); - const bLAB = convert.rgb.lab(b.r * 255, b.g * 255, b.b * 255); - const _a = Vector3.fromArray(aLAB); - const _b = Vector3.fromArray(bLAB); - return _a.sub(_b).magnitude(); - } else { - ASSERT(colourSpace === ColourSpace.RGB); - const _a = a.toVector3(); - const _b = b.toVector3(); - return _a.sub(_b).magnitude(); - } - } - - public static get white(): RGB { - return new RGB(1.0, 1.0, 1.0); - } - - public static get red(): RGB { - return new RGB(1.0, 0.0, 0.0); - } - - public static get green(): RGB { - return new RGB(0.0, 1.0, 0.0); - } - - public static get blue(): RGB { - return new RGB(0.0, 0.0, 1.0); - } - - public static get yellow(): RGB { - return new RGB(1.0, 1.0, 0.0); - } - - public static get cyan(): RGB { - return new RGB(0.0, 1.0, 1.0); - } - - public static get magenta(): RGB { - return new RGB(1.0, 0.0, 1.0); - } - - public static get black(): RGB { - return new RGB(0.0, 0.0, 0.0); - } - - public static fromVector3(vec: Vector3): RGB { - return new RGB(vec.x, vec.y, vec.z); - } - - public toVector3(): Vector3 { - return new Vector3(this.r, this.g, this.b); - } - - public copy() { - return new RGB(this.r, this.g, this.b); - } -} - /** * A 3D cuboid volume defined by two opposing corners */ diff --git a/src/voxel_mesh.ts b/src/voxel_mesh.ts index 09a4111..c650de4 100644 --- a/src/voxel_mesh.ts +++ b/src/voxel_mesh.ts @@ -1,15 +1,16 @@ import { AttributeData } from './buffer'; +import { RGBA } from './colour'; import { AppConstants } from './constants'; import { GeometryTemplates } from './geometry'; import { HashMap } from './hash_map'; import { OcclusionManager } from './occlusion'; import { TextureFiltering } from './texture'; -import { Bounds, RGB } from './util'; +import { Bounds } from './util'; import { Vector3 } from './vector'; export interface Voxel { position: Vector3; - colour: RGB; + colour: RGBA; collisions: number; } @@ -50,19 +51,18 @@ export class VoxelMesh { } } - public addVoxel(pos: Vector3, colour: RGB) { + public addVoxel(pos: Vector3, colour: RGBA) { pos.round(); const voxelIndex = this._voxelsHash.get(pos); if (voxelIndex !== undefined) { // A voxel at this position already exists const voxel = this._voxels[voxelIndex]; - const voxelColour = voxel.colour.toVector3(); - voxelColour.mulScalar(voxel.collisions); + voxel.colour.r = ((voxel.colour.r * voxel.collisions) + colour.r) / (voxel.collisions + 1); + voxel.colour.g = ((voxel.colour.g * voxel.collisions) + colour.g) / (voxel.collisions + 1); + voxel.colour.b = ((voxel.colour.b * voxel.collisions) + colour.b) / (voxel.collisions + 1); + voxel.colour.a = ((voxel.colour.a * voxel.collisions) + colour.a) / (voxel.collisions + 1); ++voxel.collisions; - voxelColour.add(colour.toVector3()); - voxelColour.divScalar(voxel.collisions); - voxel.colour = RGB.fromVector3(voxelColour); } else { // This is a new voxel this._voxels.push({ @@ -179,7 +179,7 @@ export class VoxelMesh { const cube: AttributeData = GeometryTemplates.getBoxBufferData(new Vector3(0, 0, 0)); for (let i = 0; i < numVoxels; ++i) { const voxel = this._voxels[i]; - const voxelColourArray = voxel.colour.toArray(); + const voxelColourArray = [voxel.colour.r, voxel.colour.g, voxel.colour.b, voxel.colour.a]; const voxelPositionArray = voxel.position.toArray(); for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.POSITION; ++j) { @@ -187,7 +187,7 @@ export class VoxelMesh { } for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.COLOUR; ++j) { - newBuffer.colour.data[i * AppConstants.VoxelMeshBufferComponentOffsets.COLOUR + j] = voxelColourArray[j % 3]; + newBuffer.colour.data[i * AppConstants.VoxelMeshBufferComponentOffsets.COLOUR + j] = voxelColourArray[j % 4]; } for (let j = 0; j < AppConstants.VoxelMeshBufferComponentOffsets.NORMAL; ++j) { diff --git a/src/voxelisers/base-voxeliser.ts b/src/voxelisers/base-voxeliser.ts index bc1e153..8d059a5 100644 --- a/src/voxelisers/base-voxeliser.ts +++ b/src/voxelisers/base-voxeliser.ts @@ -1,10 +1,11 @@ import { UVTriangle, Triangle } from '../triangle'; -import { RGB, UV } from '../util'; +import { UV } from '../util'; import { Vector3 } from '../vector'; import { Mesh } from '../mesh'; import { VoxelMesh, VoxelMeshParams } from '../voxel_mesh'; import { TextureFiltering } from '../texture'; import { StatusHandler } from '../status'; +import { RGBA } from '../colour'; export abstract class IVoxeliser { public voxelise(mesh: Mesh, voxelMeshParams: VoxelMeshParams): VoxelMesh { @@ -20,7 +21,7 @@ export abstract class IVoxeliser { protected abstract _voxelise(mesh: Mesh, voxelMeshParams: VoxelMeshParams): VoxelMesh; - protected _getVoxelColour(mesh: Mesh, triangle: UVTriangle, materialName: string, location: Vector3, filtering: TextureFiltering): (RGB | undefined) { + protected _getVoxelColour(mesh: Mesh, triangle: UVTriangle, materialName: string, location: Vector3, filtering: TextureFiltering): (RGBA | undefined) { const area01 = new Triangle(triangle.v0, triangle.v1, location).getArea(); const area12 = new Triangle(triangle.v1, triangle.v2, location).getArea(); const area20 = new Triangle(triangle.v2, triangle.v0, location).getArea(); diff --git a/src/voxelisers/normal-corrected-ray-voxeliser.ts b/src/voxelisers/normal-corrected-ray-voxeliser.ts index 91c2399..1a829e4 100644 --- a/src/voxelisers/normal-corrected-ray-voxeliser.ts +++ b/src/voxelisers/normal-corrected-ray-voxeliser.ts @@ -3,9 +3,10 @@ import { AppConfig } from '../config'; import { Mesh } from '../mesh'; import { Axes, Ray, rayIntersectTriangle } from '../ray'; import { Triangle, UVTriangle } from '../triangle'; -import { Bounds, RGB, UV } from '../util'; +import { Bounds, UV } from '../util'; import { Vector3 } from '../vector'; import { IVoxeliser } from './base-voxeliser'; +import { RGBA, RGBAUtil } from '../colour'; /** * This voxeliser works by projecting rays onto each triangle @@ -74,14 +75,14 @@ export class NormalCorrectedRayVoxeliser extends IVoxeliser { } voxelPosition.round(); - let voxelColour: RGB; + let voxelColour: RGBA; if (this._voxelMeshParams!.useMultisampleColouring) { - const samples: RGB[] = []; + const samples: RGBA[] = []; for (let i = 0; i < AppConfig.MULTISAMPLE_COUNT; ++i) { const samplePosition = Vector3.add(voxelPosition, Vector3.random().add(-0.5)); samples.push(this.__getVoxelColour(triangle, materialName, samplePosition)); } - voxelColour = RGB.averageFrom(samples); + voxelColour = RGBAUtil.average(...samples); } else { voxelColour = this.__getVoxelColour(triangle, materialName, voxelPosition); } @@ -92,7 +93,7 @@ export class NormalCorrectedRayVoxeliser extends IVoxeliser { } // TODO: Remove - private __getVoxelColour(triangle: UVTriangle, materialName: string, location: Vector3): RGB { + private __getVoxelColour(triangle: UVTriangle, materialName: string, location: Vector3): RGBA { const area01 = new Triangle(triangle.v0, triangle.v1, location).getArea(); const area12 = new Triangle(triangle.v1, triangle.v2, location).getArea(); const area20 = new Triangle(triangle.v2, triangle.v0, location).getArea(); diff --git a/src/voxelisers/ray-voxeliser.ts b/src/voxelisers/ray-voxeliser.ts index e0f3e45..b60c9c2 100644 --- a/src/voxelisers/ray-voxeliser.ts +++ b/src/voxelisers/ray-voxeliser.ts @@ -3,9 +3,11 @@ import { AppConfig } from '../config'; import { Mesh } from '../mesh'; import { Axes, Ray, rayIntersectTriangle } from '../ray'; import { Triangle, UVTriangle } from '../triangle'; -import { Bounds, RGB, UV } from '../util'; +import { Bounds, UV } from '../util'; import { Vector3 } from '../vector'; import { IVoxeliser } from './base-voxeliser'; +import { RGBA } from '../colour'; +import { RGBAUtil } from '../colour'; /** * This voxeliser works by projecting rays onto each triangle @@ -58,14 +60,14 @@ export class RayVoxeliser extends IVoxeliser { break; } - let voxelColour: RGB; + let voxelColour: RGBA; if (this._voxelMeshParams!.useMultisampleColouring) { - const samples: RGB[] = []; + const samples: RGBA[] = []; for (let i = 0; i < AppConfig.MULTISAMPLE_COUNT; ++i) { const samplePosition = Vector3.add(voxelPosition, Vector3.random().add(-0.5)); samples.push(this.__getVoxelColour(triangle, materialName, samplePosition)); } - voxelColour = RGB.averageFrom(samples); + voxelColour = RGBAUtil.average(...samples); } else { voxelColour = this.__getVoxelColour(triangle, materialName, voxelPosition); } @@ -76,7 +78,7 @@ export class RayVoxeliser extends IVoxeliser { } // TODO: Remove - private __getVoxelColour(triangle: UVTriangle, materialName: string, location: Vector3): RGB { + private __getVoxelColour(triangle: UVTriangle, materialName: string, location: Vector3): RGBA { const area01 = new Triangle(triangle.v0, triangle.v1, location).getArea(); const area12 = new Triangle(triangle.v1, triangle.v2, location).getArea(); const area20 = new Triangle(triangle.v2, triangle.v0, location).getArea(); diff --git a/tools/build-atlas.ts b/tools/build-atlas.ts index e70ea9c..3954dd4 100644 --- a/tools/build-atlas.ts +++ b/tools/build-atlas.ts @@ -1,4 +1,4 @@ -import { ATLASES_DIR, RGB, TOOLS_DIR, UV } from '../src/util'; +import { ATLASES_DIR, TOOLS_DIR, UV } from '../src/util'; import { log, LogStyle } from './logging'; import { isDirSetup, ASSERT, getAverageColour, getPermission, getMinecraftDir } from './misc'; @@ -8,6 +8,7 @@ import images from 'images'; import { PNG } from 'pngjs'; import chalk from 'chalk'; import prompt from 'prompt'; +import { RGBA } from '../src/colour'; const AdmZip = require('adm-zip'); const copydir = require('copy-dir'); @@ -175,12 +176,13 @@ async function buildAtlas() { CubeBottomTop = 'minecraft:block/cube_bottom_top', TemplateSingleFace = 'minecraft:block/template_single_face', TemplateGlazedTerracotta = 'minecraft:block/template_glazed_terracotta', + Leaves = 'minecraft:block/leaves', } /* eslint-enable */ interface Model { name: string, - colour?: RGB, + colour?: RGBA, faces: { [face: string]: Texture } @@ -189,7 +191,7 @@ async function buildAtlas() { interface Texture { name: string, texcoord?: UV, - colour?: RGB + colour?: RGBA } log(LogStyle.Info, 'Loading block models...'); @@ -274,6 +276,16 @@ async function buildAtlas() { west: { name: modelData.textures.pattern }, }; break; + case parentModel.Leaves: + faceData = { + up: { name: modelData.textures.all }, + down: { name: modelData.textures.all }, + north: { name: modelData.textures.all }, + south: { name: modelData.textures.all }, + east: { name: modelData.textures.all }, + west: { name: modelData.textures.all }, + }; + break; default: return; } @@ -301,7 +313,7 @@ async function buildAtlas() { let offsetY = 0; const outputImage = images(atlasWidth * 3, atlasWidth * 3); - const textureDetails: { [textureName: string]: { texcoord: UV, colour: RGB } } = {}; + const textureDetails: { [textureName: string]: { texcoord: UV, colour: RGBA } } = {}; const { atlasName } = await prompt.get({ properties: { @@ -347,18 +359,22 @@ async function buildAtlas() { // Build up the output JSON log(LogStyle.Info, `Building ${atlasName}.atlas...\n`); for (const model of allModels) { - const blockColour = new RGB(0, 0, 0); + const blockColour: RGBA = { + r: 0.0, g: 0.0, b: 0.0, a: 0.0, + }; for (const face of faces) { const faceTexture = textureDetails[model.faces[face].name]; const faceColour = faceTexture.colour; blockColour.r += faceColour.r; blockColour.g += faceColour.g; blockColour.b += faceColour.b; + blockColour.a += faceColour.a; model.faces[face].texcoord = faceTexture.texcoord; } blockColour.r /= 6; blockColour.g /= 6; blockColour.b /= 6; + blockColour.a /= 6; model.colour = blockColour; } diff --git a/tools/misc.ts b/tools/misc.ts index e7a16c9..b13e91f 100644 --- a/tools/misc.ts +++ b/tools/misc.ts @@ -1,10 +1,11 @@ import { log, LogStyle } from './logging'; -import { RGB, TOOLS_DIR } from '../src/util'; +import { TOOLS_DIR } from '../src/util'; import fs from 'fs'; import path from 'path'; import { PNG } from 'pngjs'; import prompt from 'prompt'; +import { RGBA } from '../src/colour'; export const ASSERT = (condition: boolean, onFailMessage: string) => { if (!condition) { @@ -26,10 +27,12 @@ export function isDirSetup(relativePath: string, jarAssetDir: string) { return false; } -export function getAverageColour(image: PNG) { +export function getAverageColour(image: PNG): RGBA { let r = 0; let g = 0; let b = 0; + let a = 0; + let weight = 0; for (let x = 0; x < image.width; ++x) { for (let y = 0; y < image.height; ++y) { const index = 4 * (image.width * y + x); @@ -37,10 +40,17 @@ export function getAverageColour(image: PNG) { r += rgba[0]; g += rgba[1]; b += rgba[2]; + a += rgba[3]; + weight += rgba[3]; } } const numPixels = image.width * image.height; - return new RGB(r / (255 * numPixels), g / (255 * numPixels), b / (255 * numPixels)); + return { + r: r / (255 * numPixels), + g: g / (255 * numPixels), + b: b / (255 * numPixels), + a: a / (255 * numPixels), + }; } export async function getPermission() {