forked from mirror/ObjToSchematic
Added support for transparent blocks
This commit is contained in:
parent
6eb9cd2adc
commit
80967ec945
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
63
src/colour.ts
Normal 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 };
|
||||
}
|
@ -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;
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
},
|
||||
},
|
||||
|
14
src/mesh.ts
14
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] = {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
108
src/util.ts
108
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
|
||||
*/
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user