import Texture2D from '../Texture2D';
import TextureCube from '../TextureCube';
import vendor from '../core/vendor';
import EnvironmentMapPass from '../prePass/EnvironmentMap';
import Skydome from '../plugin/Skydome';
import Scene from '../Scene';
import dds from './dds';
import hdr from './hdr';
/**
* @alias clay.util.texture
*/
var textureUtil = {
/**
* @param {string|object} path
* @param {object} [option]
* @param {Function} [onsuccess]
* @param {Function} [onerror]
* @return {clay.Texture}
*/
loadTexture: function (path, option, onsuccess, onerror) {
var texture;
if (typeof(option) === 'function') {
onsuccess = option;
onerror = onsuccess;
option = {};
}
else {
option = option || {};
}
if (typeof(path) === 'string') {
if (path.match(/.hdr$/) || option.fileType === 'hdr') {
texture = new Texture2D({
width: 0,
height: 0,
sRGB: false
});
textureUtil._fetchTexture(
path,
function (data) {
hdr.parseRGBE(data, texture, option.exposure);
texture.dirty();
onsuccess && onsuccess(texture);
},
onerror
);
return texture;
}
else if (path.match(/.dds$/) || option.fileType === 'dds') {
texture = new Texture2D({
width: 0,
height: 0
});
textureUtil._fetchTexture(
path,
function (data) {
dds.parse(data, texture);
texture.dirty();
onsuccess && onsuccess(texture);
},
onerror
);
}
else {
texture = new Texture2D();
texture.load(path);
texture.success(onsuccess);
texture.error(onerror);
}
}
else if (typeof path === 'object' && typeof(path.px) !== 'undefined') {
texture = new TextureCube();
texture.load(path);
texture.success(onsuccess);
texture.error(onerror);
}
return texture;
},
/**
* Load a panorama texture and render it to a cube map
* @param {clay.Renderer} renderer
* @param {string} path
* @param {clay.TextureCube} cubeMap
* @param {object} [option]
* @param {boolean} [option.encodeRGBM]
* @param {number} [option.exposure]
* @param {Function} [onsuccess]
* @param {Function} [onerror]
*/
loadPanorama: function (renderer, path, cubeMap, option, onsuccess, onerror) {
var self = this;
if (typeof(option) === 'function') {
onsuccess = option;
onerror = onsuccess;
option = {};
}
else {
option = option || {};
}
textureUtil.loadTexture(path, option, function (texture) {
// PENDING
texture.flipY = option.flipY || false;
self.panoramaToCubeMap(renderer, texture, cubeMap, option);
texture.dispose(renderer);
onsuccess && onsuccess(cubeMap);
}, onerror);
},
/**
* Render a panorama texture to a cube map
* @param {clay.Renderer} renderer
* @param {clay.Texture2D} panoramaMap
* @param {clay.TextureCube} cubeMap
* @param {Object} option
* @param {boolean} [option.encodeRGBM]
*/
panoramaToCubeMap: function (renderer, panoramaMap, cubeMap, option) {
var environmentMapPass = new EnvironmentMapPass();
var skydome = new Skydome({
scene: new Scene()
});
skydome.setEnvironmentMap(panoramaMap);
option = option || {};
if (option.encodeRGBM) {
skydome.material.define('fragment', 'RGBM_ENCODE');
}
// Share sRGB
cubeMap.sRGB = panoramaMap.sRGB;
environmentMapPass.texture = cubeMap;
environmentMapPass.render(renderer, skydome.scene);
environmentMapPass.texture = null;
environmentMapPass.dispose(renderer);
return cubeMap;
},
/**
* Convert height map to normal map
* @param {HTMLImageElement|HTMLCanvasElement} image
* @param {boolean} [checkBump=false]
* @return {HTMLCanvasElement}
*/
heightToNormal: function (image, checkBump) {
var canvas = document.createElement('canvas');
var width = canvas.width = image.width;
var height = canvas.height = image.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, width, height);
checkBump = checkBump || false;
var srcData = ctx.getImageData(0, 0, width, height);
var dstData = ctx.createImageData(width, height);
for (var i = 0; i < srcData.data.length; i += 4) {
if (checkBump) {
var r = srcData.data[i];
var g = srcData.data[i + 1];
var b = srcData.data[i + 2];
var diff = Math.abs(r - g) + Math.abs(g - b);
if (diff > 20) {
console.warn('Given image is not a height map');
return image;
}
}
// Modified from http://mrdoob.com/lab/javascript/height2normal/
var x1, y1, x2, y2;
if (i % (width * 4) === 0) {
// left edge
x1 = srcData.data[i];
x2 = srcData.data[i + 4];
}
else if (i % (width * 4) === (width - 1) * 4) {
// right edge
x1 = srcData.data[i - 4];
x2 = srcData.data[i];
}
else {
x1 = srcData.data[i - 4];
x2 = srcData.data[i + 4];
}
if (i < width * 4) {
// top edge
y1 = srcData.data[i];
y2 = srcData.data[i + width * 4];
}
else if (i > width * (height - 1) * 4) {
// bottom edge
y1 = srcData.data[i - width * 4];
y2 = srcData.data[i];
}
else {
y1 = srcData.data[i - width * 4];
y2 = srcData.data[i + width * 4];
}
dstData.data[i] = (x1 - x2) + 127;
dstData.data[i + 1] = (y1 - y2) + 127;
dstData.data[i + 2] = 255;
dstData.data[i + 3] = 255;
}
ctx.putImageData(dstData, 0, 0);
return canvas;
},
/**
* Convert height map to normal map
* @param {HTMLImageElement|HTMLCanvasElement} image
* @param {boolean} [checkBump=false]
* @param {number} [threshold=20]
* @return {HTMLCanvasElement}
*/
isHeightImage: function (img, downScaleSize, threshold) {
if (!img || !img.width || !img.height) {
return false;
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var size = downScaleSize || 32;
threshold = threshold || 20;
canvas.width = canvas.height = size;
ctx.drawImage(img, 0, 0, size, size);
var srcData = ctx.getImageData(0, 0, size, size);
for (var i = 0; i < srcData.data.length; i += 4) {
var r = srcData.data[i];
var g = srcData.data[i + 1];
var b = srcData.data[i + 2];
var diff = Math.abs(r - g) + Math.abs(g - b);
if (diff > threshold) {
return false;
}
}
return true;
},
_fetchTexture: function (path, onsuccess, onerror) {
vendor.request.get({
url: path,
responseType: 'arraybuffer',
onload: onsuccess,
onerror: onerror
});
},
/**
* Create a chessboard texture
* @param {number} [size]
* @param {number} [unitSize]
* @param {string} [color1]
* @param {string} [color2]
* @return {clay.Texture2D}
*/
createChessboard: function (size, unitSize, color1, color2) {
size = size || 512;
unitSize = unitSize || 64;
color1 = color1 || 'black';
color2 = color2 || 'white';
var repeat = Math.ceil(size / unitSize);
var canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
ctx.fillStyle = color2;
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = color1;
for (var i = 0; i < repeat; i++) {
for (var j = 0; j < repeat; j++) {
var isFill = j % 2 ? (i % 2) : (i % 2 - 1);
if (isFill) {
ctx.fillRect(i * unitSize, j * unitSize, unitSize, unitSize);
}
}
}
var texture = new Texture2D({
image: canvas,
anisotropic: 8
});
return texture;
},
/**
* Create a blank pure color 1x1 texture
* @param {string} color
* @return {clay.Texture2D}
*/
createBlank: function (color) {
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
var ctx = canvas.getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0, 0, 1, 1);
var texture = new Texture2D({
image: canvas
});
return texture;
}
};
export default textureUtil;