import Material from './Material';
import Shader from './Shader';
import standardEssl from './shader/source/standard.glsl.js';
import util from './core/util';
// Import standard shader
Shader['import'](standardEssl);
var TEXTURE_PROPERTIES = ['diffuseMap', 'normalMap', 'roughnessMap', 'metalnessMap', 'emissiveMap', 'environmentMap', 'brdfLookup', 'ssaoMap', 'aoMap'];
var SIMPLE_PROPERTIES = ['color', 'emission', 'emissionIntensity', 'alpha', 'roughness', 'metalness', 'uvRepeat', 'uvOffset', 'aoIntensity', 'alphaCutoff', 'normalScale'];
var PROPERTIES_CHANGE_SHADER = ['linear', 'encodeRGBM', 'decodeRGBM', 'doubleSided', 'alphaTest', 'roughnessChannel', 'metalnessChannel', 'environmentMapPrefiltered'];
var NUM_DEFINE_MAP = {
'roughnessChannel': 'ROUGHNESS_CHANNEL',
'metalnessChannel': 'METALNESS_CHANNEL'
};
var BOOL_DEFINE_MAP = {
'linear': 'SRGB_DECODE',
'encodeRGBM': 'RGBM_ENCODE',
'decodeRGBM': 'RGBM_DECODE',
'doubleSided': 'DOUBLE_SIDED',
'alphaTest': 'ALPHA_TEST',
'environmentMapPrefiltered': 'ENVIRONMENTMAP_PREFILTER'
};
var standardShader;
/**
* Standard material without custom shader.
* @constructor clay.StandardMaterial
* @extends clay.Base
* @example
* var mat = new clay.StandardMaterial({
* color: [1, 1, 1],
* diffuseMap: diffuseTexture
* });
* mat.roughness = 1;
*/
var StandardMaterial = Material.extend(function () {
if (!standardShader) {
standardShader = new Shader(Shader.source('clay.standardMR.vertex'), Shader.source('clay.standardMR.fragment'));
}
return /** @lends clay.StandardMaterial# */ {
shader: standardShader
};
}, function (option) {
// PENDING
util.extend(this, option);
// Extend after shader is created.
util.defaults(this, /** @lends clay.StandardMaterial# */ {
/**
* @type {Array.<number>}
* @default [1, 1, 1]
*/
color: [1, 1, 1],
/**
* @type {Array.<number>}
* @default [0, 0, 0]
*/
emission: [0, 0, 0],
/**
* @type {number}
* @default 0
*/
emissionIntensity: 0,
/**
* @type {number}
* @default 0.5
*/
roughness: 0.5,
/**
* @type {number}
* @default 0
*/
metalness: 0,
/**
* @type {number}
* @default 1
*/
alpha: 1,
/**
* @type {boolean}
*/
alphaTest: false,
/**
* Cutoff threshold for alpha test
* @type {number}
*/
alphaCutoff: 0.9,
/**
* Scalar multiplier applied to each normal vector of normal texture.
*
* @type {number}
*
* XXX This value is considered only if a normal texture is specified.
*/
normalScale: 1.0,
/**
* @type {boolean}
*/
// TODO Must disable culling.
doubleSided: false,
/**
* @type {clay.Texture2D}
*/
diffuseMap: null,
/**
* @type {clay.Texture2D}
*/
normalMap: null,
/**
* @type {clay.Texture2D}
*/
roughnessMap: null,
/**
* @type {clay.Texture2D}
*/
metalnessMap: null,
/**
* @type {clay.Texture2D}
*/
emissiveMap: null,
/**
* @type {clay.TextureCube}
*/
environmentMap: null,
/**
* @type {clay.BoundingBox}
*/
environmentBox: null,
/**
* BRDF Lookup is generated by clay.util.cubemap.integrateBrdf
* @type {clay.Texture2D}
*/
brdfLookup: null,
/**
* @type {clay.Texture2D}
*/
ssaoMap: null,
/**
* @type {clay.Texture2D}
*/
aoMap: null,
/**
* @type {Array.<number>}
* @default [1, 1]
*/
uvRepeat: [1, 1],
/**
* @type {Array.<number>}
* @default [0, 0]
*/
uvOffset: [0, 0],
/**
* @type {number}
* @default 1
*/
aoIntensity: 1,
/**
* @type {boolean}
*/
environmentMapPrefiltered: false,
/**
* @type {boolean}
*/
linear: false,
/**
* @type {boolean}
*/
encodeRGBM: false,
/**
* @type {boolean}
*/
decodeRGBM: false,
/**
* @type {Number}
*/
roughnessChannel: 0,
/**
* @type {Number}
*/
metalnessChannel: 1
});
}, {
clone: function () {
var material = new StandardMaterial({
name: this.name
});
TEXTURE_PROPERTIES.forEach(function (propName) {
if (this[propName]) {
material[propName] = this[propName];
}
}, this);
SIMPLE_PROPERTIES.concat(PROPERTIES_CHANGE_SHADER).forEach(function (propName) {
material[propName] = this[propName];
}, this);
return material;
}
});
SIMPLE_PROPERTIES.forEach(function (propName) {
Object.defineProperty(StandardMaterial.prototype, propName, {
get: function () {
return this.get(propName);
},
set: function (value) {
this.setUniform(propName, value);
}
});
});
TEXTURE_PROPERTIES.forEach(function (propName) {
Object.defineProperty(StandardMaterial.prototype, propName, {
get: function () {
return this.get(propName);
},
set: function (value) {
this.setUniform(propName, value);
}
});
});
PROPERTIES_CHANGE_SHADER.forEach(function (propName) {
var privateKey = '_' + propName;
Object.defineProperty(StandardMaterial.prototype, propName, {
get: function () {
return this[privateKey];
},
set: function (value) {
this[privateKey] = value;
if (propName in NUM_DEFINE_MAP) {
var defineName = NUM_DEFINE_MAP[propName];
this.define('fragment', defineName, value);
}
else {
var defineName = BOOL_DEFINE_MAP[propName];
value ? this.define('fragment', defineName) : this.undefine('fragment', defineName);
}
}
});
});
Object.defineProperty(StandardMaterial.prototype, 'environmentBox', {
get: function () {
var envBox = this._environmentBox;
if (envBox) {
envBox.min.setArray(this.get('environmentBoxMin'));
envBox.max.setArray(this.get('environmentBoxMax'));
}
return envBox;
},
set: function (value) {
this._environmentBox = value;
var uniforms = this.uniforms = this.uniforms || {};
uniforms['environmentBoxMin'] = uniforms['environmentBoxMin'] || {
value: null
};
uniforms['environmentBoxMax'] = uniforms['environmentBoxMax'] || {
value: null
};
// TODO Can't detect operation like box.min = new Vector()
if (value) {
this.setUniform('environmentBoxMin', value.min.array);
this.setUniform('environmentBoxMax', value.max.array);
}
if (value) {
this.define('fragment', 'PARALLAX_CORRECTED');
}
else {
this.undefine('fragment', 'PARALLAX_CORRECTED');
}
}
});
export default StandardMaterial;