forked from mirror/ObjToSchematic
Added support for alpha maps
This commit is contained in:
parent
80967ec945
commit
687ed7159a
@ -1,6 +1,9 @@
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform sampler2D u_alpha;
|
||||
uniform bool u_useAlphaMap;
|
||||
uniform bool u_useAlphaChannel;
|
||||
|
||||
varying float v_lighting;
|
||||
varying vec2 v_texcoord;
|
||||
@ -98,7 +101,12 @@ void main() {
|
||||
vec2 tex = vec2(v_texcoord.x, 1.0 - v_texcoord.y);
|
||||
vec4 diffuse = texture2D(u_texture, tex).rgba;
|
||||
|
||||
float alpha = dither8x8(gl_FragCoord.xy, diffuse.a);
|
||||
float alpha = diffuse.a;
|
||||
if (u_useAlphaMap) {
|
||||
alpha = u_useAlphaChannel ? texture2D(u_alpha, tex).a : texture2D(u_alpha, tex).r;
|
||||
}
|
||||
|
||||
alpha = dither8x8(gl_FragCoord.xy, alpha);
|
||||
if (alpha < 0.5)
|
||||
{
|
||||
discard;
|
||||
|
@ -190,6 +190,7 @@ export class ObjImporter extends IImporter {
|
||||
|
||||
private _currentColour: RGBA = RGBAColours.BLACK;
|
||||
private _currentTexture: string = '';
|
||||
private _currentTransparencyTexture: string = '';
|
||||
private _materialReady: boolean = false;
|
||||
private _mtlParsers = [
|
||||
{
|
||||
@ -236,6 +237,20 @@ export class ObjImporter extends IImporter {
|
||||
this._materialReady = true;
|
||||
},
|
||||
},
|
||||
{
|
||||
// Transparency map
|
||||
// e.g. 'map_d my/path/to/file.png'
|
||||
regex: new RegExpBuilder().add(/^map_d/).add(REGEX_NZ_ANY, 'path').toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
let texturePath = match.path.trim();
|
||||
if (!path.isAbsolute(texturePath)) {
|
||||
ASSERT(this._objPath, 'no obj path');
|
||||
texturePath = path.join(this._objPath.dir, texturePath);
|
||||
}
|
||||
this._currentTransparencyTexture = texturePath;
|
||||
this._materialReady = true;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
override parseFile(filePath: string) {
|
||||
@ -356,7 +371,9 @@ export class ObjImporter extends IImporter {
|
||||
this._materials[this._currentMaterialName] = {
|
||||
type: MaterialType.textured,
|
||||
path: this._currentTexture,
|
||||
alphaPath: this._currentTransparencyTexture === '' ? undefined : this._currentTransparencyTexture,
|
||||
};
|
||||
this._currentTransparencyTexture = '';
|
||||
} else {
|
||||
this._materials[this._currentMaterialName] = {
|
||||
type: MaterialType.solid,
|
||||
|
@ -25,7 +25,11 @@ export interface Tri {
|
||||
export enum MaterialType { solid, textured }
|
||||
/* eslint-enable */
|
||||
export interface SolidMaterial { colour: RGBA; type: MaterialType.solid }
|
||||
export interface TexturedMaterial { path: string; type: MaterialType.textured }
|
||||
export interface TexturedMaterial {
|
||||
path: string;
|
||||
type: MaterialType.textured;
|
||||
alphaPath?: string;
|
||||
}
|
||||
export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)};
|
||||
|
||||
export class Mesh {
|
||||
@ -227,7 +231,7 @@ export class Mesh {
|
||||
const material = this._materials[tri.material];
|
||||
if (material.type == MaterialType.textured) {
|
||||
if (!(tri.material in this._loadedTextures)) {
|
||||
this._loadedTextures[tri.material] = new Texture(material.path);
|
||||
this._loadedTextures[tri.material] = new Texture(material.path, material.alphaPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { BlockMesh } from './block_mesh';
|
||||
import * as twgl from 'twgl.js';
|
||||
import { EAppEvent, EventManager } from './event';
|
||||
import { RGBA, RGBAUtil } from './colour';
|
||||
import { Texture } from './texture';
|
||||
|
||||
/* eslint-disable */
|
||||
export enum MeshType {
|
||||
@ -46,7 +47,7 @@ export class Renderer {
|
||||
|
||||
private _materialBuffers: Array<{
|
||||
buffer: RenderBuffer,
|
||||
material: (SolidMaterial | (TexturedMaterial & { texture: WebGLTexture }))
|
||||
material: (SolidMaterial | (TexturedMaterial & { texture: WebGLTexture, alpha?: WebGLTexture, useAlphaChannel?: boolean }))
|
||||
}>;
|
||||
public _voxelBuffer?: twgl.BufferInfo;
|
||||
public _voxelBufferRaw?: any;
|
||||
@ -183,6 +184,11 @@ export class Renderer {
|
||||
src: material.path,
|
||||
mag: this._gl.LINEAR,
|
||||
}),
|
||||
alpha: material.alphaPath ? twgl.createTexture(this._gl, {
|
||||
src: material.alphaPath,
|
||||
mag: this._gl.LINEAR,
|
||||
}) : undefined,
|
||||
useAlphaChannel: material.alphaPath ? new Texture(material.path, material.alphaPath)._useAlphaChannel() : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -285,6 +291,9 @@ export class Renderer {
|
||||
u_worldViewProjection: ArcballCamera.Get.getWorldViewProjection(),
|
||||
u_worldInverseTranspose: ArcballCamera.Get.getWorldInverseTranspose(),
|
||||
u_texture: materialBuffer.material.texture,
|
||||
u_alpha: materialBuffer.material.alpha,
|
||||
u_useAlphaMap: materialBuffer.material.alpha !== undefined,
|
||||
u_useAlphaChannel: materialBuffer.material.useAlphaChannel,
|
||||
});
|
||||
} else {
|
||||
this._drawRegister(materialBuffer.buffer, ShaderManager.Get.solidTriProgram, {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UV, ASSERT, AppError } from './util';
|
||||
import { UV, ASSERT, AppError, LOG } from './util';
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as jpeg from 'jpeg-js';
|
||||
@ -21,28 +21,40 @@ export enum TextureFiltering {
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
export class Texture {
|
||||
private _image: {
|
||||
data: Buffer,
|
||||
width: number,
|
||||
height: number
|
||||
};
|
||||
type ImageData = {
|
||||
data: Buffer,
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
|
||||
constructor(filename: string) {
|
||||
export class Texture {
|
||||
private _image: ImageData;
|
||||
private _alphaImage?: ImageData;
|
||||
|
||||
constructor(filename: string, transparencyFilename?: string) {
|
||||
ASSERT(path.isAbsolute(filename));
|
||||
const filePath = path.parse(filename);
|
||||
|
||||
this._image = this._loadImageFile(filename);
|
||||
|
||||
if (transparencyFilename) {
|
||||
this._alphaImage = this._loadImageFile(transparencyFilename);
|
||||
}
|
||||
}
|
||||
|
||||
private _loadImageFile(filename: string): ImageData {
|
||||
ASSERT(path.isAbsolute(filename));
|
||||
const filePath = path.parse(filename);
|
||||
try {
|
||||
const data = fs.readFileSync(filename);
|
||||
if (filePath.ext.toLowerCase() === '.png') {
|
||||
this._image = PNG.sync.read(data);
|
||||
return PNG.sync.read(data);
|
||||
} else if (['.jpg', '.jpeg'].includes(filePath.ext.toLowerCase())) {
|
||||
this._image = jpeg.decode(data);
|
||||
this._useAlphaChannelValue = false;
|
||||
return jpeg.decode(data);
|
||||
} else {
|
||||
throw new AppError(`Failed to load ${filename}`);
|
||||
}
|
||||
if (this._image.width * this._image.height * 4 !== this._image.data.length) {
|
||||
throw new AppError(`Unexpected image resolution mismatch: ${filename}`);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new AppError(`Could not read ${filename}`);
|
||||
}
|
||||
@ -96,11 +108,50 @@ export class Texture {
|
||||
}
|
||||
|
||||
private _getFromXY(x: number, y: number): RGBA {
|
||||
x = clamp(x, 0, this._image.width - 1);
|
||||
y = clamp(y, 0, this._image.height - 1);
|
||||
const diffuse = Texture._sampleImage(this._image, x, y);
|
||||
|
||||
if (this._alphaImage) {
|
||||
const alpha = Texture._sampleImage(this._alphaImage, x, y);
|
||||
return {
|
||||
r: diffuse.r,
|
||||
g: diffuse.g,
|
||||
b: diffuse.b,
|
||||
a: this._useAlphaChannel() ? alpha.a : alpha.r,
|
||||
};
|
||||
}
|
||||
|
||||
return diffuse;
|
||||
}
|
||||
|
||||
public _useAlphaChannelValue?: boolean;
|
||||
public _useAlphaChannel() {
|
||||
ASSERT(this._alphaImage !== undefined);
|
||||
if (this._useAlphaChannelValue !== undefined) {
|
||||
return this._useAlphaChannelValue;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._alphaImage.width; ++i) {
|
||||
for (let j = 0; j < this._alphaImage.height; ++j) {
|
||||
const value = Texture._sampleImage(this._alphaImage, i, j);
|
||||
if (value.a != 1.0) {
|
||||
LOG(`Using alpha channel`);
|
||||
this._useAlphaChannelValue = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const index = 4 * (this._image.width * y + x);
|
||||
const rgba = this._image.data.slice(index, index + 4);
|
||||
LOG(`Using red channel`);
|
||||
this._useAlphaChannelValue = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static _sampleImage(image: ImageData, x: number, y: number) {
|
||||
x = clamp(x, 0, image.width - 1);
|
||||
y = clamp(y, 0, image.height - 1);
|
||||
|
||||
const index = 4 * (image.width * y + x);
|
||||
const rgba = image.data.slice(index, index + 4);
|
||||
|
||||
return {
|
||||
r: rgba[0] / 255,
|
||||
|
@ -52,6 +52,10 @@ export class VoxelMesh {
|
||||
}
|
||||
|
||||
public addVoxel(pos: Vector3, colour: RGBA) {
|
||||
if (colour.a === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos.round();
|
||||
|
||||
const voxelIndex = this._voxelsHash.get(pos);
|
||||
|
Loading…
Reference in New Issue
Block a user