Added support for transparent blocks

This commit is contained in:
Lucas Dower 2022-06-12 00:47:30 +01:00
parent 6eb9cd2adc
commit 80967ec945
22 changed files with 467 additions and 231 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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<Vector3, number>;
private _atlasBlocks: Array<BlockInfo>;
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];
}

63
src/colour.ts Normal file
View File

@ -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 };
}

View File

@ -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;

View File

@ -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),

View File

@ -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;
},
},

View File

@ -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] = {

View File

@ -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);
}
// /////////////////////////////////////////////////////////////////////////

View File

@ -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,
};
}
}

View File

@ -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
*/

View File

@ -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) {

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;
}

View File

@ -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() {