Object.assign(pc, function () {
/**
* @constructor
* @name pc.StandardMaterial
* @classdesc A Standard material is the main, general purpose material that is most often used for rendering.
* It can approximate a wide variety of surface types and can simulate dynamic reflected light.
* Most maps can use 3 types of input values in any combination: constant (color or number), mesh vertex colors and a texture. All enabled inputs are multiplied together.
*
* @property {pc.Color} ambient The ambient color of the material. This color value is 3-component (RGB),
* where each component is between 0 and 1.
*
* @property {pc.Color} diffuse The diffuse color of the material. This color value is 3-component (RGB),
* where each component is between 0 and 1.
* Defines basic surface color (aka albedo).
* @property {Boolean} diffuseTint Multiply diffuse map and/or diffuse vertex color by the constant diffuse value.
* @property {pc.Texture} diffuseMap The diffuse map of the material.
* @property {Number} diffuseMapUv Diffuse map UV channel
* @property {pc.Vec2} diffuseMapTiling Controls the 2D tiling of the diffuse map.
* @property {pc.Vec2} diffuseMapOffset Controls the 2D offset of the diffuse map. Each component is between 0 and 1.
* @property {String} diffuseMapChannel Color channels of the diffuse map to use. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
* @property {Boolean} diffuseVertexColor Use mesh vertex colors for diffuse. If diffuseMap or are diffuseTint are set, they'll be multiplied by vertex colors.
* @property {String} diffuseVertexColorChannel Vertex color channels to use for diffuse. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
*
* @property {pc.Color} specular The specular color of the material. This color value is 3-component (RGB),
* where each component is between 0 and 1.
* Defines surface reflection/specular color. Affects specular intensity and tint.
* @property {Boolean} specularTint Multiply specular map and/or specular vertex color by the constant specular value.
* @property {pc.Texture} specularMap The specular map of the material.
* @property {Number} specularMapUv Specular map UV channel
* @property {pc.Vec2} specularMapTiling Controls the 2D tiling of the specular map.
* @property {pc.Vec2} specularMapOffset Controls the 2D offset of the specular map. Each component is between 0 and 1.
* @property {String} specularMapChannel Color channels of the specular map to use. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
* @property {Boolean} specularVertexColor Use mesh vertex colors for specular. If specularMap or are specularTint are set, they'll be multiplied by vertex colors.
* @property {String} specularVertexColorChannel Vertex color channels to use for specular. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
*
* @property {Boolean} useMetalness Use metalness properties instead of specular.
* When enabled, diffuse colors also affect specular instead of the dedicated specular map.
* This can be used as alternative to specular color to save space.
* With metaless == 0, the pixel is assumed to be dielectric, and diffuse color is used as normal.
* With metaless == 1, the pixel is fully metallic, and diffuse color is used as specular color instead.
* @property {Number} metalness Defines how much the surface is metallic. From 0 (dielectric) to 1 (metal).
* @property {pc.Texture} metalnessMap Monochrome metalness map.
* @property {Number} metalnessMapUv Metalness map UV channel
* @property {pc.Vec2} metalnessMapTiling Controls the 2D tiling of the metalness map.
* @property {pc.Vec2} metalnessMapOffset Controls the 2D offset of the metalness map. Each component is between 0 and 1.
* @property {String} metalnessMapChannel Color channel of the metalness map to use. Can be "r", "g", "b" or "a".
* @property {Boolean} metalnessVertexColor Use mesh vertex colors for metalness. If metalnessMap is set, it'll be multiplied by vertex colors.
* @property {String} metalnessVertexColorChannel Vertex color channel to use for metalness. Can be "r", "g", "b" or "a".
*
* @property {Number} shininess Defines glossiness of the material from 0 (rough) to 100 (shiny mirror).
* A higher shininess value results in a more focused specular highlight.
* Glossiness map/vertex colors are always multiplied by this value (normalized to 0 - 1 range), or it is used directly as constant output.
* @property {pc.Texture} glossMap Glossiness map. If set, will be multiplied by normalized 'shininess' value and/or vertex colors.
* @property {Number} glossMapUv Gloss map UV channel
* @property {String} glossMapChannel Color channel of the gloss map to use. Can be "r", "g", "b" or "a".
* @property {pc.Vec2} glossMapTiling Controls the 2D tiling of the gloss map.
* @property {pc.Vec2} glossMapOffset Controls the 2D offset of the gloss map. Each component is between 0 and 1.
* @property {Boolean} glossVertexColor Use mesh vertex colors for glossiness. If glossMap is set, it'll be multiplied by vertex colors.
* @property {String} glossVertexColorChannel Vertex color channel to use for glossiness. Can be "r", "g", "b" or "a".
*
* @property {Number} refraction Defines the visibility of refraction. Material can refract the same cube map as used for reflections.
* @property {Number} refractionIndex Defines the index of refraction, i.e. the amount of distortion.
* The value is calculated as (outerIor / surfaceIor), where inputs are measured indices of refraction, the one around the object and the one of it's own surface.
* In most situations outer medium is air, so outerIor will be approximately 1. Then you only need to do (1.0 / surfaceIor).
*
* @property {pc.Color} emissive The emissive color of the material. This color value is 3-component (RGB),
* where each component is between 0 and 1.
* @property {Boolean} emissiveTint Multiply emissive map and/or emissive vertex color by the constant emissive value.
* @property {pc.Texture} emissiveMap The emissive map of the material. Can be HDR.
* @property {Number} emissiveIntensity Emissive color multiplier.
* @property {Number} emissiveMapUv Emissive map UV channel.
* @property {pc.Vec2} emissiveMapTiling Controls the 2D tiling of the emissive map.
* @property {pc.Vec2} emissiveMapOffset Controls the 2D offset of the emissive map. Each component is between 0 and 1.
* @property {String} emissiveMapChannel Color channels of the emissive map to use. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
* @property {Boolean} emissiveVertexColor Use mesh vertex colors for emission. If emissiveMap or emissiveTint are set, they'll be multiplied by vertex colors.
* @property {String} emissiveVertexColorChannel Vertex color channels to use for emission. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
*
* @property {Number} opacity The opacity of the material. This value can be between 0 and 1, where 0 is fully
* transparent and 1 is fully opaque. If you want the material to be semi-transparent you also need to
* set the {@link pc.Material#blendType} to pc.BLEND_NORMAL, pc.BLEND_ADDITIVE or any other mode.
* Also note that for most semi-transparent objects you want {@link pc.Material#depthWrite} to be false, otherwise they can fully occlude objects behind them.
* @property {pc.Texture} opacityMap The opacity map of the material.
* @property {Number} opacityMapUv Opacity map UV channel
* @property {String} opacityMapChannel Color channel of the opacity map to use. Can be "r", "g", "b" or "a".
* @property {pc.Vec2} opacityMapTiling Controls the 2D tiling of the opacity map.
* @property {pc.Vec2} opacityMapOffset Controls the 2D offset of the opacity map. Each component is between 0 and 1.
* @property {Boolean} opacityVertexColor Use mesh vertex colors for opacity. If opacityMap is set, it'll be multiplied by vertex colors.
* @property {String} opacityVertexColorChannel Vertex color channels to use for opacity. Can be "r", "g", "b" or "a".
*
* @property {pc.Texture} normalMap The normal map of the material.
* The texture must contains normalized, tangent space normals.
* @property {Number} normalMapUv Normal map UV channel
* @property {pc.Vec2} normalMapTiling Controls the 2D tiling of the normal map.
* @property {pc.Vec2} normalMapOffset Controls the 2D offset of the normal map. Each component is between 0 and 1.
* @property {Number} bumpiness The bumpiness of the material. This value scales the assigned normal map.
* It should be normally between 0 (no bump mapping) and 1 (full bump mapping), but can be set to e.g. 2 to give even more pronounced bump effect.
*
* @property {pc.Texture} heightMap The height map of the material. Used for a view-dependent parallax effect.
* The texture must represent the height of the surface where darker pixels are lower and lighter pixels are higher.
* It is recommended to use it together with a normal map.
* @property {Number} heightMapUv Height map UV channel
* @property {String} heightMapChannel Color channel of the height map to use. Can be "r", "g", "b" or "a".
* @property {pc.Vec2} heightMapTiling Controls the 2D tiling of the height map.
* @property {pc.Vec2} heightMapOffset Controls the 2D offset of the height map. Each component is between 0 and 1.
* @property {Number} heightMapFactor Height map multiplier. Affects the strength of the parallax effect.
*
* @property {pc.Texture} sphereMap The spherical environment map of the material. Affects reflections.
* @property {pc.Texture} cubeMap The cubic environment map of the material. Overrides sphereMap. Affects reflections. If cubemap is prefiltered, will also affect ambient color.
* @property {Number} cubeMapProjection The type of projection applied to the cubeMap property:
* <ul>
* <li>{@link pc.CUBEPROJ_NONE}: The cube map is treated as if it is infinitely far away.</li>
* <li>{@link pc.CUBEPROJ_BOX}: Box-projection based on a world space axis-aligned bounding box.</li>
* </ul>
* Defaults to pc.CUBEPROJ_NONE.
* @property {pc.BoundingBox} cubeMapProjectionBox The world space axis-aligned bounding box defining the
* box-projection used for the cubeMap property. Only used when cubeMapProjection is set to pc.CUBEPROJ_BOX.
* @property {Number} reflectivity Environment map intensity.
*
* @property {pc.Texture} lightMap A custom lightmap of the material. Lightmaps are textures that contain pre-rendered lighting. Can be HDR.
* @property {Number} lightMapUv Lightmap UV channel
* @property {String} lightMapChannel Color channels of the lightmap to use. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
* @property {pc.Vec2} lightMapTiling Controls the 2D tiling of the lightmap.
* @property {pc.Vec2} lightMapOffset Controls the 2D offset of the lightmap. Each component is between 0 and 1.
* @property {Boolean} lightVertexColor Use baked vertex lighting. If lightMap is set, it'll be multiplied by vertex colors.
* @property {String} lightVertexColorChannel Vertex color channels to use for baked lighting. Can be "r", "g", "b", "a", "rgb" or any swizzled combination.
*
* @property {Boolean} ambientTint Enables scene ambient multiplication by material ambient color.
* @property {pc.Texture} aoMap Baked ambient occlusion (AO) map. Modulates ambient color.
* @property {Number} aoMapUv AO map UV channel
* @property {String} aoMapChannel Color channel of the AO map to use. Can be "r", "g", "b" or "a".
* @property {pc.Vec2} aoMapTiling Controls the 2D tiling of the AO map.
* @property {pc.Vec2} aoMapOffset Controls the 2D offset of the AO map. Each component is between 0 and 1.
* @property {Boolean} aoVertexColor Use mesh vertex colors for AO. If aoMap is set, it'll be multiplied by vertex colors.
* @property {String} aoVertexColorChannel Vertex color channels to use for AO. Can be "r", "g", "b" or "a".
* @property {Number} occludeSpecular Uses ambient occlusion to darken specular/reflection. It's a hack, because real specular occlusion is view-dependent. However, it can be better than nothing.
* <ul>
* <li>{@link pc.SPECOCC_NONE}: No specular occlusion</li>
* <li>{@link pc.SPECOCC_AO}: Use AO directly to occlude specular.</li>
* <li>{@link pc.SPECOCC_GLOSSDEPENDENT}: Modify AO based on material glossiness/view angle to occlude specular.</li>
* </ul>
* @property {Number} occludeSpecularIntensity Controls visibility of specular occlusion.
* @property {Number} occludeDirect Tells if AO should darken directional lighting.
*
* @property {Boolean} specularAntialias Enables Toksvig AA for mipmapped normal maps with specular.
* @property {Boolean} conserveEnergy Defines how diffuse and specular components are combined when Fresnel is on.
* It is recommended that you leave this option enabled, although you may want to disable it in case when all reflection comes only from a few light sources, and you don't use an environment map, therefore having mostly black reflection.
* @property {Number} shadingModel Defines the shading model.
* <ul>
* <li>{@link pc.SPECULAR_PHONG}: Phong without energy conservation. You should only use it as a backwards compatibility with older projects.</li>
* <li>{@link pc.SPECULAR_BLINN}: Energy-conserving Blinn-Phong.</li>
* </ul>
* @property {Number} fresnelModel Defines the formula used for Fresnel effect.
* As a side-effect, enabling any Fresnel model changes the way diffuse and reflection components are combined.
* When Fresnel is off, legacy non energy-conserving combining is used. When it is on, combining behaviour is defined by conserveEnergy parameter.
* <ul>
* <li>{@link pc.FRESNEL_NONE}: No Fresnel.</li>
* <li>{@link pc.FRESNEL_SCHLICK}: Schlick's approximation of Fresnel (recommended). Parameterized by specular color.</li>
* </ul>
* @property {Boolean} useFog Apply fogging (as configured in scene settings)
* @property {Boolean} useLighting Apply lighting
* @property {Boolean} useSkybox Apply scene skybox as prefiltered environment map
* @property {Boolean} useGammaTonemap Apply gamma correction and tonemapping (as configured in scene settings)
* @property {Boolean} pixelSnap Align vertices to pixel co-ordinates when rendering. Useful for pixel perfect 2D graphics
* @property {Boolean} twoSidedLighting Calculate proper normals (and therefore lighting) on backfaces
*
* @property {Function} onUpdateShader A custom function that will be called after all shader generator properties are collected and before shader code is generated.
* This function will receive an object with shader generator settings (based on current material and scene properties), that you can change and then return.
* Returned value will be used instead. This is mostly useful when rendering the same set of objects, but with different shader variations based on the same material.
* For example, you may wish to render a depth or normal pass using textures assigned to the material, a reflection pass with simpler shaders and so on.
* Properties of the object passed into this function are:
* <ul>
* <li>pass: value of {@link pc.Layer#shaderPass} of the Layer being rendered.</li>
* <li>chunks: Object containing custom shader chunks that will replace default ones.</li>
* <li>customFragmentShader: Completely replace fragment shader with this code.</li>
* <li>forceUv1: if UV1 (second set of texture coordinates) is required in the shader. Will be declared as "vUv1" and passed to the fragment shader.</li>
* <li>fog: the type of fog being applied in the shader. See {@link pc.Scene#fog} for the list of possible values.</li>
* <li>gamma: the type of gamma correction being applied in the shader. See {@link pc.Scene#gammaCorrection} for the list of possible values.</li>
* <li>toneMap: the type of tone mapping being applied in the shader. See {@link pc.Scene#toneMapping} for the list of possible values.</li>
* <li>ambientTint: the value of {@link pc.StandardMaterial#ambientTint}.</li>
* <li>specularAntialias: the value of {@link pc.StandardMaterial#specularAntialias}.</li>
* <li>conserveEnergy: the value of {@link pc.StandardMaterial#conserveEnergy}.</li>
* <li>occludeSpecular: the value of {@link pc.StandardMaterial#occludeSpecular}.</li>
* <li>occludeDirect: the value of {@link pc.StandardMaterial#occludeDirect}.</li>
* <li>shadingModel: the value of {@link pc.StandardMaterial#shadingModel}.</li>
* <li>fresnelModel: the value of {@link pc.StandardMaterial#fresnelModel}.</li>
* <li>cubeMapProjection: the value of {@link pc.StandardMaterial#cubeMapProjection}.</li>
* <li>useMetalness: the value of {@link pc.StandardMaterial#useMetalness}.</li>
* <li>blendType: the value of {@link pc.Material#blendType}.</li>
* <li>twoSidedLighting: the value of {@link pc.Material#twoSidedLighting}.</li>
* <li>diffuseTint: defines if {@link pc.StandardMaterial#diffuse} constant should affect diffuse color.</li>
* <li>specularTint: defines if {@link pc.StandardMaterial#specular} constant should affect specular color.</li>
* <li>metalnessTint: defines if {@link pc.StandardMaterial#metalness} constant should affect metalness value.</li>
* <li>glossTint: defines if {@link pc.StandardMaterial#shininess} constant should affect glossiness value.</li>
* <li>emissiveTint: defines if {@link pc.StandardMaterial#emissive} constant should affect emission value.</li>
* <li>opacityTint: defines if {@link pc.StandardMaterial#opacity} constant should affect opacity value.</li>
* <li>occludeSpecularFloat: defines if {@link pc.StandardMaterial#occludeSpecularIntensity} constant should affect specular occlusion.</li>
* <li>alphaTest: enable alpha testing. See {@link pc.Material#alphaTest}.</li>
* <li>alphaToCoverage: enable alpha to coverage. See {@link pc.Material#alphaToCoverage}.</li>
* <li>sphereMap: if {@link pc.StandardMaterial#sphereMap} is used.</li>
* <li>cubeMap: if {@link pc.StandardMaterial#cubeMap} is used.</li>
* <li>dpAtlas: if dual-paraboloid reflection is used. Dual paraboloid reflections replace prefiltered cubemaps on certain platform (mostly Android) for performance reasons.</li>
* <li>ambientSH: if ambient spherical harmonics are used. Ambient SH replace prefiltered cubemap ambient on certain platform (mostly Android) for performance reasons.</li>
* <li>useSpecular: if any specular or reflections are needed at all.</li>
* <li>rgbmAmbient: if ambient cubemap or spherical harmonics are RGBM-encoded.</li>
* <li>hdrAmbient: if ambient cubemap or spherical harmonics are plain float HDR data.</li>
* <li>rgbmReflection: if reflection cubemap or dual paraboloid are RGBM-encoded.</li>
* <li>hdrReflection: if reflection cubemap or dual paraboloid are plain float HDR data.</li>
* <li>fixSeams: if cubemaps require seam fixing (see {@link pc.Texture#options.fixCubemapSeams}).</li>
* <li>prefilteredCubemap: if prefiltered cubemaps are used.</li>
* <li>emissiveFormat: how emissiveMap must be sampled. This value is based on {@link pc.Texture#options.rgbm} and {@link pc.Texture#options.format}. Possible values are:</li>
* <ul>
* <li>0: sRGB texture</li>
* <li>1: RGBM-encoded HDR texture</li>
* <li>2: Simple read (no conversion from sRGB)</li>
* </ul>
* <li>lightMapFormat: how lightMap must be sampled. This value is based on {@link pc.Texture#options.rgbm} and {@link pc.Texture#options.format}. Possible values are:</li>
* <ul>
* <li>0: sRGB texture</li>
* <li>1: RGBM-encoded HDR texture</li>
* <li>2: Simple read (no conversion from sRGB)</li>
* </ul>
* <li>useRgbm: if decodeRGBM() function is needed in the shader at all.</li>
* <li>packedNormal: if normal map contains X in RGB, Y in Alpha, and Z must be reconstructed.</li>
* <li>forceFragmentPrecision: Override fragment shader numeric precision. Can be "lowp", "mediump", "highp" or null to use default.</li>
* <li>fastTbn: Use slightly cheaper normal mapping code (skip tangent space normalization). Can look buggy sometimes.</li>
* <li>refraction: if refraction is used.</li>
* <li>skyboxIntensity: if reflected skybox intensity should be modulated.</li>
* <li>useTexCubeLod: if textureCubeLodEXT function should be used to read prefiltered cubemaps. Usually true of iOS, false on other devices due to quality/performance balance.</li>
* </ul>
*
* @example
* // Create a new Standard material
* var material = new pc.StandardMaterial();
*
* // Update the material's diffuse and specular properties
* material.diffuse.set(1, 0, 0);
* material.specular.set(1, 1, 1);
*
* // Notify the material that it has been modified
* material.update();
*
* @extends pc.Material
*/
var StandardMaterial = function () {
pc.Material.call(this);
// storage for texture and cubemap asset references
this._assetReferences = {};
this._validator = null;
this.shaderOptBuilder = new pc.StandardMaterialOptionsBuilder();
this.reset();
};
StandardMaterial.prototype = Object.create(pc.Material.prototype);
StandardMaterial.prototype.constructor = StandardMaterial;
var _propsSerial = [];
var _propsSerialDefaultVal = [];
var _propsInternalNull = [];
var _propsInternalVec3 = [];
var _prop2Uniform = {};
var _defineTex2D = function (obj, name, uv, channels, defChannel) {
var privMap = "_" + name + "Map";
var privMapTiling = privMap + "Tiling";
var privMapOffset = privMap + "Offset";
var mapTransform = privMap.substring(1) + "Transform";
var mapTransformUniform = mapTransform + "Uniform";
var privMapUv = privMap + "Uv";
var privMapChannel = privMap + "Channel";
var privMapVertexColor = "_" + name + "VertexColor";
var privMapVertexColorChannel = "_" + name + "VertexColorChannel";
obj[privMap] = null;
obj[privMapTiling] = new pc.Vec2(1, 1);
obj[privMapOffset] = new pc.Vec2(0, 0);
obj[mapTransform] = null;
obj[mapTransformUniform] = null;
obj[privMapUv] = uv;
if (channels > 0) {
var channel = defChannel ? defChannel : (channels > 1 ? "rgb" : "g");
obj[privMapChannel] = channel;
obj[privMapVertexColorChannel] = channel;
}
obj[privMapVertexColor] = false;
if (!pc._matTex2D) pc._matTex2D = [];
pc._matTex2D[name] = channels;
Object.defineProperty(StandardMaterial.prototype, privMap.substring(1), {
get: function () {
return this[privMap];
},
set: function (value) {
var oldVal = this[privMap];
if (!!oldVal ^ !!value) this.dirtyShader = true;
if (oldVal && value) {
if (oldVal.rgbm !== value.rgbm || oldVal.fixCubemapSeams !== value.fixCubemapSeams || oldVal.format !== value.format) {
this.dirtyShader = true;
}
}
this[privMap] = value;
}
});
var mapTiling = privMapTiling.substring(1);
var mapOffset = privMapOffset.substring(1);
Object.defineProperty(StandardMaterial.prototype, mapTiling, {
get: function () {
return this[privMapTiling];
},
set: function (value) {
this.dirtyShader = true;
this[privMapTiling] = value;
}
});
_prop2Uniform[mapTiling] = function (mat, val, changeMat) {
var tform = mat._updateMapTransform(
changeMat ? mat[mapTransform] : null,
val,
mat[privMapOffset]
);
return { name: ("texture_" + mapTransform), value: tform.data };
};
Object.defineProperty(StandardMaterial.prototype, mapOffset, {
get: function () {
return this[privMapOffset];
},
set: function (value) {
this.dirtyShader = true;
this[privMapOffset] = value;
}
});
_prop2Uniform[mapOffset] = function (mat, val, changeMat) {
var tform = mat._updateMapTransform(
changeMat ? mat[mapTransform] : null,
mat[privMapTiling],
val
);
return { name: ("texture_" + mapTransform), value: tform.data };
};
Object.defineProperty(StandardMaterial.prototype, privMapUv.substring(1), {
get: function () {
return this[privMapUv];
},
set: function (value) {
if (this[privMapUv] !== value) this.dirtyShader = true;
this[privMapUv] = value;
}
});
Object.defineProperty(StandardMaterial.prototype, privMapChannel.substring(1), {
get: function () {
return this[privMapChannel];
},
set: function (value) {
if (this[privMapChannel] !== value) this.dirtyShader = true;
this[privMapChannel] = value;
}
});
Object.defineProperty(StandardMaterial.prototype, privMapVertexColor.substring(1), {
get: function () {
return this[privMapVertexColor];
},
set: function (value) {
this.dirtyShader = true;
this[privMapVertexColor] = value;
}
});
Object.defineProperty(StandardMaterial.prototype, privMapVertexColorChannel.substring(1), {
get: function () {
return this[privMapVertexColorChannel];
},
set: function (value) {
if (this[privMapVertexColorChannel] !== value) this.dirtyShader = true;
this[privMapVertexColorChannel] = value;
}
});
_propsSerial.push(privMap.substring(1));
_propsSerial.push(privMapTiling.substring(1));
_propsSerial.push(privMapOffset.substring(1));
_propsSerial.push(privMapUv.substring(1));
_propsSerial.push(privMapChannel.substring(1));
_propsSerial.push(privMapVertexColor.substring(1));
_propsSerial.push(privMapVertexColorChannel.substring(1));
_propsInternalNull.push(mapTransform);
};
var _propsColor = [];
var _defineColor = function (obj, name, defaultValue, hasMultiplier) {
var priv = "_" + name;
var uform = name + "Uniform";
var mult = name + "Intensity";
var pmult = "_" + mult;
obj[priv] = defaultValue;
obj[uform] = new Float32Array(3);
Object.defineProperty(StandardMaterial.prototype, name, {
get: function () {
this.dirtyColor = true;
this.dirtyShader = true;
return this[priv];
},
set: function (newValue) {
var oldValue = this[priv];
var wasRound = (oldValue.r === 0 && oldValue.g === 0 && oldValue.b === 0) || (oldValue.r === 1 && oldValue.g === 1 && oldValue.b === 1);
var isRound = (newValue.r === 0 && newValue.g === 0 && newValue.b === 0) || (newValue.r === 1 && newValue.g === 1 && newValue.b === 1);
if (wasRound ^ isRound) this.dirtyShader = true;
this.dirtyColor = true;
this[priv] = newValue;
}
});
_propsSerial.push(name);
_propsInternalVec3.push(uform);
_propsColor.push(name);
_prop2Uniform[name] = function (mat, val, changeMat) {
var arr = changeMat ? mat[uform] : new Float32Array(3);
var gammaCorrection = false;
if (mat.useGammaTonemap) {
var scene = mat._scene || pc.Application.getApplication().scene;
gammaCorrection = scene.gammaCorrection;
}
for (var c = 0; c < 3; c++) {
if (gammaCorrection) {
arr[c] = Math.pow(val.data[c], 2.2);
} else {
arr[c] = val.data[c];
}
if (hasMultiplier) arr[c] *= mat[pmult];
}
return { name: ("material_" + name), value: arr };
};
if (hasMultiplier) {
obj[pmult] = 1;
Object.defineProperty(StandardMaterial.prototype, mult, {
get: function () {
return this[pmult];
},
set: function (newValue) {
var oldValue = this[pmult];
var wasRound = oldValue === 0 || oldValue === 1;
var isRound = newValue === 0 || newValue === 1;
if (wasRound ^ isRound) this.dirtyShader = true;
this.dirtyColor = true;
this[pmult] = newValue;
}
});
_propsSerial.push(mult);
_prop2Uniform[mult] = function (mat, val, changeMat) {
var arr = changeMat ? mat[uform] : new Float32Array(3);
var gammaCorrection = false;
if (mat.useGammaTonemap) {
var scene = mat._scene || pc.Application.getApplication().scene;
gammaCorrection = scene.gammaCorrection;
}
for (var c = 0; c < 3; c++) {
if (gammaCorrection) {
arr[c] = Math.pow(mat[priv].data[c], 2.2);
} else {
arr[c] = mat[priv].data[c];
}
arr[c] *= mat[pmult];
}
return { name: ("material_" + name), value: arr };
};
}
};
var _defineFloat = function (obj, name, defaultValue, func) {
var priv = "_" + name;
obj[priv] = defaultValue;
Object.defineProperty(StandardMaterial.prototype, name, {
get: function () {
return this[priv];
},
set: function (newValue) {
var oldValue = this[priv];
if (oldValue === newValue) return;
this[priv] = newValue;
// This is not always optimal and will sometimes trigger redundant shader
// recompilation. However, no number property on a standard material
// triggers a shader recompile if the previous and current values both
// have a fractional part.
var wasRound = oldValue === 0 || oldValue === 1;
var isRound = newValue === 0 || newValue === 1;
if (wasRound || isRound) this.dirtyShader = true;
}
});
_propsSerial.push(name);
_prop2Uniform[name] = func !== undefined ? func : function (mat, val, changeMat) {
return {
name: "material_" + name,
value: val
};
};
};
var _defineObject = function (obj, name, func) {
var priv = "_" + name;
obj[priv] = null;
Object.defineProperty(StandardMaterial.prototype, name, {
get: function () {
return this[priv];
},
set: function (value) {
var oldVal = this[priv];
if (!!oldVal ^ !!value) this.dirtyShader = true;
this[priv] = value;
}
});
_propsSerial.push(name);
_prop2Uniform[name] = func;
};
var _defineAlias = function (obj, newName, oldName) {
Object.defineProperty(StandardMaterial.prototype, oldName, {
get: function () {
return this[newName];
},
set: function (value) {
this[newName] = value;
}
});
};
var _defineChunks = function (obj) {
Object.defineProperty(StandardMaterial.prototype, "chunks", {
get: function () {
this.dirtyShader = true;
return this._chunks;
},
set: function (value) {
this.dirtyShader = true;
this._chunks = value;
}
});
_propsSerial.push("chunks");
};
var _defineFlag = function (obj, name, defaultValue) {
var priv = "_" + name;
obj[priv] = defaultValue;
Object.defineProperty(StandardMaterial.prototype, name, {
get: function () {
return this[priv];
},
set: function (value) {
if (this[priv] !== value) this.dirtyShader = true;
this[priv] = value;
}
});
_propsSerial.push(name);
};
var Chunks = function () { };
Chunks.prototype.copy = function (from) {
for (var p in from) {
if (from.hasOwnProperty(p) && p !== 'copy')
this[p] = from[p];
}
};
Object.assign(StandardMaterial.prototype, {
reset: function () {
var i;
for (i = 0; i < _propsSerial.length; i++) {
var defVal = _propsSerialDefaultVal[i];
this[_propsSerial[i]] = defVal ? (defVal.clone ? defVal.clone() : defVal) : defVal;
}
for (i = 0; i < _propsInternalNull.length; i++) {
this[_propsInternalNull[i]] = null;
}
for (i = 0; i < _propsInternalVec3.length; i++) {
this[_propsInternalVec3[i]] = new Float32Array(3);
}
this._chunks = new Chunks();
this.cubeMapMinUniform = new Float32Array(3);
this.cubeMapMaxUniform = new Float32Array(3);
},
/**
* @function
* @name pc.StandardMaterial#clone
* @description Duplicates a Standard material. All properties are duplicated except textures
* where only the references are copied.
* @returns {pc.StandardMaterial} A cloned Standard material.
*/
clone: function () {
var clone = new pc.StandardMaterial();
pc.Material.prototype._cloneInternal.call(this, clone);
var pname;
for (var i = 0; i < _propsSerial.length; i++) {
pname = _propsSerial[i];
if (this[pname] !== undefined) {
if (this[pname] && this[pname].copy) {
if (clone[pname]) {
clone[pname].copy(this[pname]);
} else {
clone[pname] = this[pname].clone();
}
} else {
clone[pname] = this[pname];
}
}
}
return clone;
},
_updateMapTransform: function (transform, tiling, offset) {
transform = transform || new pc.Vec4();
transform.set(tiling.x, tiling.y, offset.x, offset.y);
if ((transform.x === 1) && (transform.y === 1) && (transform.z === 0) && (transform.w === 0)) return null;
return transform;
},
_setParameter: function (name, value) {
if (!this.parameters[name])
this._propsSet.push(name);
this.setParameter(name, value);
},
_clearParameters: function () {
var props = this._propsSet;
for (var i = 0; i < props.length; i++) {
delete this.parameters[props[i]];
}
this._propsSet = [];
},
_updateMap: function (p) {
var mname = p + "Map";
if (this[mname]) {
this._setParameter("texture_" + mname, this[mname]);
var tname = mname + "Transform";
var uname = mname + "TransformUniform";
if (!this[tname]) {
this[uname] = new Float32Array(4);
}
this[tname] = this._updateMapTransform(
this[tname],
this[mname + "Tiling"],
this[mname + "Offset"]
);
if (this[tname]) {
this[uname][0] = this[tname].x;
this[uname][1] = this[tname].y;
this[uname][2] = this[tname].z;
this[uname][3] = this[tname].w;
this._setParameter('texture_' + tname, this[uname]);
}
}
},
getUniform: function (varName, value, changeMat) {
var func = _prop2Uniform[varName];
if (func) {
return func(this, value, changeMat);
}
return null;
},
updateUniforms: function () {
var uniform;
this._clearParameters();
this._setParameter('material_ambient', this.ambientUniform);
if (!this.diffuseMap || this.diffuseTint) {
this._setParameter('material_diffuse', this.diffuseUniform);
}
if (!this.useMetalness) {
if (!this.specularMap || this.specularTint) {
this._setParameter('material_specular', this.specularUniform);
}
} else {
if (!this.metalnessMap || this.metalness < 1) {
this._setParameter('material_metalness', this.metalness);
}
}
uniform = this.getUniform("shininess", this.shininess, true);
this._setParameter(uniform.name, uniform.value);
if (!this.emissiveMap || this.emissiveTint) {
this._setParameter('material_emissive', this.emissiveUniform);
}
if (this.emissiveMap) {
this._setParameter('material_emissiveIntensity', this.emissiveIntensity);
}
if (this.refraction > 0) {
this._setParameter('material_refraction', this.refraction);
this._setParameter('material_refractionIndex', this.refractionIndex);
}
this._setParameter('material_opacity', this.opacity);
if (this.occludeSpecular) {
this._setParameter('material_occludeSpecularIntensity', this.occludeSpecularIntensity);
}
if (this.cubeMapProjection === pc.CUBEPROJ_BOX) {
this._setParameter(this.getUniform("cubeMapProjectionBox", this.cubeMapProjectionBox, true));
}
for (var p in pc._matTex2D) {
this._updateMap(p);
}
if (this.ambientSH) {
this._setParameter('ambientSH[0]', this.ambientSH);
}
if (this.normalMap) {
this._setParameter('material_bumpiness', this.bumpiness);
}
if (this.heightMap) {
uniform = this.getUniform('heightMapFactor', this.heightMapFactor, true);
this._setParameter(uniform.name, uniform.value);
}
if (this.cubeMap) {
this._setParameter('texture_cubeMap', this.cubeMap);
}
if (this.prefilteredCubeMap128) {
this._setParameter('texture_prefilteredCubeMap128', this.prefilteredCubeMap128);
} else if (this._scene && this._scene._skyboxPrefiltered[0]) {
this._setParameter('texture_prefilteredCubeMap128', this._scene._skyboxPrefiltered[0]);
}
if (this.prefilteredCubeMap64) {
this._setParameter('texture_prefilteredCubeMap64', this.prefilteredCubeMap64);
} else if (this._scene && this._scene._skyboxPrefiltered[1]) {
this._setParameter('texture_prefilteredCubeMap64', this._scene._skyboxPrefiltered[1]);
}
if (this.prefilteredCubeMap32) {
this._setParameter('texture_prefilteredCubeMap32', this.prefilteredCubeMap32);
} else if (this._scene && this._scene._skyboxPrefiltered[2]) {
this._setParameter('texture_prefilteredCubeMap32', this._scene._skyboxPrefiltered[2]);
}
if (this.prefilteredCubeMap16) {
this._setParameter('texture_prefilteredCubeMap16', this.prefilteredCubeMap16);
} else if (this._scene && this._scene._skyboxPrefiltered[3]) {
this._setParameter('texture_prefilteredCubeMap16', this._scene._skyboxPrefiltered[3]);
}
if (this.prefilteredCubeMap8) {
this._setParameter('texture_prefilteredCubeMap8', this.prefilteredCubeMap8);
} else if (this._scene && this._scene._skyboxPrefiltered[4]) {
this._setParameter('texture_prefilteredCubeMap8', this._scene._skyboxPrefiltered[4]);
}
if (this.prefilteredCubeMap4) {
this._setParameter('texture_prefilteredCubeMap4', this.prefilteredCubeMap4);
} else if (this._scene && this._scene._skyboxPrefiltered[5]) {
this._setParameter('texture_prefilteredCubeMap4', this._scene._skyboxPrefiltered[5]);
}
if (this.sphereMap) {
this._setParameter('texture_sphereMap', this.sphereMap);
}
if (this.dpAtlas) {
this._setParameter('texture_sphereMap', this.dpAtlas);
}
// if (this.sphereMap || this.cubeMap || this.prefilteredCubeMap128) {
this._setParameter('material_reflectivity', this.reflectivity);
// }
if (this.dirtyShader || !this._scene) {
this.shader = null;
this.clearVariants();
}
this._processColor();
},
_processColor: function () {
var c, i;
if (!this.dirtyColor) return;
if (!this._scene && this.useGammaTonemap) return;
var gammaCorrection = false;
if (this.useGammaTonemap) gammaCorrection = this._scene.gammaCorrection;
// Gamma correct colors
for (i = 0; i < _propsColor.length; i++) {
var clr = this["_" + _propsColor[i]];
var arr = this[_propsColor[i] + "Uniform"];
if (gammaCorrection) {
arr[0] = Math.pow(clr.r, 2.2);
arr[1] = Math.pow(clr.g, 2.2);
arr[2] = Math.pow(clr.b, 2.2);
} else {
arr[0] = clr.r;
arr[1] = clr.g;
arr[2] = clr.b;
}
}
for (c = 0; c < 3; c++) {
this.emissiveUniform[c] *= this.emissiveIntensity;
}
this.dirtyColor = false;
},
updateShader: function (device, scene, objDefs, staticLightList, pass, sortedLights) {
if (!this._colorProcessed && this._scene) {
this._colorProcessed = true;
this._processColor();
}
var useTexCubeLod = device.useTexCubeLod;
var useDp = !device.extTextureLod; // no basic extension? likely slow device, force dp
var globalSky128, globalSky64, globalSky32, globalSky16, globalSky8, globalSky4;
if (this.useSkybox) {
globalSky128 = scene._skyboxPrefiltered[0];
globalSky64 = scene._skyboxPrefiltered[1];
globalSky32 = scene._skyboxPrefiltered[2];
globalSky16 = scene._skyboxPrefiltered[3];
globalSky8 = scene._skyboxPrefiltered[4];
globalSky4 = scene._skyboxPrefiltered[5];
}
var prefilteredCubeMap128 = this.prefilteredCubeMap128 || globalSky128;
var prefilteredCubeMap64 = this.prefilteredCubeMap64 || globalSky64;
var prefilteredCubeMap32 = this.prefilteredCubeMap32 || globalSky32;
var prefilteredCubeMap16 = this.prefilteredCubeMap16 || globalSky16;
var prefilteredCubeMap8 = this.prefilteredCubeMap8 || globalSky8;
var prefilteredCubeMap4 = this.prefilteredCubeMap4 || globalSky4;
if (prefilteredCubeMap128) {
var allMips = prefilteredCubeMap128 &&
prefilteredCubeMap64 &&
prefilteredCubeMap32 &&
prefilteredCubeMap16 &&
prefilteredCubeMap8 &&
prefilteredCubeMap4;
if (useDp && allMips) {
if (!prefilteredCubeMap128.dpAtlas) {
var atlas = [prefilteredCubeMap128, prefilteredCubeMap64, prefilteredCubeMap32,
prefilteredCubeMap16, prefilteredCubeMap8, prefilteredCubeMap4];
prefilteredCubeMap128.dpAtlas = pc.generateDpAtlas(device, atlas);
prefilteredCubeMap128.sh = pc.shFromCubemap(prefilteredCubeMap16);
}
this.dpAtlas = prefilteredCubeMap128.dpAtlas;
this.ambientSH = prefilteredCubeMap128.sh;
this._setParameter('ambientSH[0]', this.ambientSH);
this._setParameter('texture_sphereMap', this.dpAtlas);
} else if (useTexCubeLod) {
if (prefilteredCubeMap128._levels.length < 6) {
if (allMips) {
// Multiple -> single (provided cubemap per mip, but can use texCubeLod)
this._setParameter('texture_prefilteredCubeMap128', prefilteredCubeMap128);
} else {
console.log("Can't use prefiltered cubemap: " + allMips + ", " + useTexCubeLod + ", " + prefilteredCubeMap128._levels);
}
} else {
// Single (able to use single cubemap with texCubeLod)
this._setParameter('texture_prefilteredCubeMap128', prefilteredCubeMap128);
}
} else if (allMips) {
// Multiple (no texCubeLod, but able to use cubemap per mip)
this._setParameter('texture_prefilteredCubeMap128', prefilteredCubeMap128);
this._setParameter('texture_prefilteredCubeMap64', prefilteredCubeMap64);
this._setParameter('texture_prefilteredCubeMap32', prefilteredCubeMap32);
this._setParameter('texture_prefilteredCubeMap16', prefilteredCubeMap16);
this._setParameter('texture_prefilteredCubeMap8', prefilteredCubeMap8);
this._setParameter('texture_prefilteredCubeMap4', prefilteredCubeMap4);
} else {
console.log("Can't use prefiltered cubemap: " + allMips + ", " + useTexCubeLod + ", " + prefilteredCubeMap128._levels);
}
}
var generator = pc.programlib.standard;
// Minimal options for Depth and Shadow passes
var minimalOptions = pass > pc.SHADER_FORWARDHDR && pass <= pc.SHADER_PICK;
var options = minimalOptions ? generator.optionsContextMin : generator.optionsContext;
if (minimalOptions)
this.shaderOptBuilder.updateMinRef(options, device, scene, this, objDefs, staticLightList, pass, sortedLights, prefilteredCubeMap128);
else
this.shaderOptBuilder.updateRef(options, device, scene, this, objDefs, staticLightList, pass, sortedLights, prefilteredCubeMap128);
if (this.onUpdateShader) {
options = this.onUpdateShader(options);
}
var library = device.getProgramLibrary();
this.shader = library.getProgram('standard', options);
if (!objDefs) {
this.clearVariants();
this.variants[0] = this.shader;
}
this.dirtyShader = false;
}
});
var _defineMaterialProps = function (obj) {
obj.dirtyShader = true;
obj.dirtyColor = true;
obj._scene = null;
obj._colorProcessed = false;
_defineColor(obj, "ambient", new pc.Color(0.7, 0.7, 0.7));
_defineColor(obj, "diffuse", new pc.Color(1, 1, 1));
_defineColor(obj, "specular", new pc.Color(0, 0, 0));
_defineColor(obj, "emissive", new pc.Color(0, 0, 0), true);
_defineFloat(obj, "shininess", 25, function (mat, shininess) {
// Shininess is 0-100 value
// which is actually a 0-1 glosiness value.
// Can be converted to specular power using exp2(shininess * 0.01 * 11)
var value;
if (mat.shadingModel === pc.SPECULAR_PHONG) {
value = Math.pow(2, shininess * 0.01 * 11); // legacy: expand back to specular power
} else {
value = shininess * 0.01; // correct
}
return { name: "material_shininess", value: value };
});
_defineFloat(obj, "heightMapFactor", 1, function (mat, height) {
return { name: 'material_heightMapFactor', value: height * 0.025 };
});
_defineFloat(obj, "opacity", 1);
_defineFloat(obj, "alphaTest", 0);
_defineFloat(obj, "bumpiness", 1);
_defineFloat(obj, "reflectivity", 1);
_defineFloat(obj, "occludeSpecularIntensity", 1);
_defineFloat(obj, "refraction", 0);
_defineFloat(obj, "refractionIndex", 1.0 / 1.5); // approx. (air ior / glass ior)
_defineFloat(obj, "metalness", 1);
_defineFloat(obj, "aoUvSet", 0, null); // legacy
_defineObject(obj, "ambientSH", function (mat, val, changeMat) {
return { name: "ambientSH[0]", value: val };
});
_defineObject(obj, "cubeMapProjectionBox", function (mat, val, changeMat) {
var bmin = changeMat ? mat.cubeMapMinUniform : new Float32Array(3);
var bmax = changeMat ? mat.cubeMapMaxUniform : new Float32Array(3);
bmin[0] = val.center.x - val.halfExtents.x;
bmin[1] = val.center.y - val.halfExtents.y;
bmin[2] = val.center.z - val.halfExtents.z;
bmax[0] = val.center.x + val.halfExtents.x;
bmax[1] = val.center.y + val.halfExtents.y;
bmax[2] = val.center.z + val.halfExtents.z;
return [{ name: "envBoxMin", value: bmin }, { name: "envBoxMax", value: bmax }];
});
_defineChunks(obj);
_defineFlag(obj, "ambientTint", false);
_defineFlag(obj, "diffuseTint", false);
_defineFlag(obj, "specularTint", false);
_defineFlag(obj, "emissiveTint", false);
_defineFlag(obj, "fastTbn", false);
_defineFlag(obj, "specularAntialias", false);
_defineFlag(obj, "useMetalness", false);
_defineFlag(obj, "occludeDirect", false);
_defineFlag(obj, "normalizeNormalMap", true);
_defineFlag(obj, "conserveEnergy", true);
_defineFlag(obj, "occludeSpecular", pc.SPECOCC_AO);
_defineFlag(obj, "shadingModel", pc.SPECULAR_BLINN);
_defineFlag(obj, "fresnelModel", pc.FRESNEL_NONE);
_defineFlag(obj, "cubeMapProjection", pc.CUBEPROJ_NONE);
_defineFlag(obj, "customFragmentShader", null);
_defineFlag(obj, "forceFragmentPrecision", null);
_defineFlag(obj, "useFog", true);
_defineFlag(obj, "useLighting", true);
_defineFlag(obj, "useGammaTonemap", true);
_defineFlag(obj, "useSkybox", true);
_defineFlag(obj, "forceUv1", false);
_defineFlag(obj, "pixelSnap", false);
_defineFlag(obj, "twoSidedLighting", false);
_defineFlag(obj, "nineSlicedMode", pc.SPRITE_RENDERMODE_SLICED);
_defineTex2D(obj, "diffuse", 0, 3);
_defineTex2D(obj, "specular", 0, 3);
_defineTex2D(obj, "emissive", 0, 3);
_defineTex2D(obj, "normal", 0, -1);
_defineTex2D(obj, "metalness", 0, 1);
_defineTex2D(obj, "gloss", 0, 1);
_defineTex2D(obj, "opacity", 0, 1, 'a');
_defineTex2D(obj, "height", 0, 1);
_defineTex2D(obj, "ao", 0, 1);
_defineTex2D(obj, "light", 1, 3);
_defineTex2D(obj, "msdf", 0, 3);
_defineObject(obj, "cubeMap");
_defineObject(obj, "sphereMap");
_defineObject(obj, "dpAtlas");
_defineObject(obj, "prefilteredCubeMap128");
_defineObject(obj, "prefilteredCubeMap64");
_defineObject(obj, "prefilteredCubeMap32");
_defineObject(obj, "prefilteredCubeMap16");
_defineObject(obj, "prefilteredCubeMap8");
_defineObject(obj, "prefilteredCubeMap4");
_defineAlias(obj, "diffuseTint", "diffuseMapTint");
_defineAlias(obj, "specularTint", "specularMapTint");
_defineAlias(obj, "emissiveTint", "emissiveMapTint");
_defineAlias(obj, "aoVertexColor", "aoMapVertexColor");
_defineAlias(obj, "diffuseVertexColor", "diffuseMapVertexColor");
_defineAlias(obj, "specularVertexColor", "specularMapVertexColor");
_defineAlias(obj, "emissiveVertexColor", "emissiveMapVertexColor");
_defineAlias(obj, "metalnessVertexColor", "metalnessMapVertexColor");
_defineAlias(obj, "glossVertexColor", "glossMapVertexColor");
_defineAlias(obj, "opacityVertexColor", "opacityMapVertexColor");
_defineAlias(obj, "lightVertexColor", "lightMapVertexColor");
for (var i = 0; i < _propsSerial.length; i++) {
_propsSerialDefaultVal[i] = obj[_propsSerial[i]];
}
obj._propsSet = [];
};
_defineMaterialProps(StandardMaterial.prototype);
return {
StandardMaterial: StandardMaterial
};
}());