Source: material/Material.js

import { EventDispatcher } from '../EventDispatcher.js';
import { BLEND_TYPE, BLEND_EQUATION, BLEND_FACTOR, ENVMAP_COMBINE_TYPE, DRAW_SIDE, SHADING_TYPE, DRAW_MODE, WEBGL_COMPARE_FUNC, WEBGL_OP, VERTEX_COLOR } from '../const.js';
import { Color3 } from '../math/Color3.js';
import { generateUUID } from '../base.js';

var materialId = 0;

/**
 * Abstract base class for materials.
 * Materials describe the appearance of {@link zen3d.Object3D}.
 * They are defined in a (mostly) renderer-independent way, so you don't have to rewrite materials if you decide to use a different renderer.
 * The following properties and methods are inherited by all other material types (although they may have different defaults).
 * @constructor
 * @abstract
 * @memberof zen3d
 */
function Material() {
	EventDispatcher.call(this);

	Object.defineProperty(this, 'id', { value: materialId++ });

	// material type
	this.type = "";

	/**
     * UUID of this material instance.
     * This gets automatically assigned, so this shouldn't be edited.
     * @type {string}
     */
	this.uuid = generateUUID();

	/**
      * Override the renderer's default precision for this material.
      * Can be "highp", "mediump" or "lowp".
      * @type {string}
      * @default null
      */
	this.precision = null;

	/**
     * Float in the range of 0.0 - 1.0 indicating how transparent the material is.
     * A value of 0.0 indicates fully transparent, 1.0 is fully opaque.
     * @type {number}
     * @default 1
     */
	this.opacity = 1;

	/**
     * Defines whether this material is transparent.
     * This has an effect on rendering as transparent objects need special treatment and are rendered after non-transparent objects.
     * When set to true, the extent to which the material is transparent is controlled by setting it's blending property.
     * @type {boolean}
     * @default false
     */
	this.transparent = false;

	/**
     * Which blending to use when displaying objects with this material.
     * This must be set to zen3d.BLEND_TYPE.CUSTOM to use custom blendSrc, blendDst or blendEquation.
     * @type {zen3d.BLEND_TYPE}
     * @default zen3d.BLEND_TYPE.NORMAL
     */
	this.blending = BLEND_TYPE.NORMAL;

	/**
     * Blending source.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_FACTOR}
     * @default zen3d.BLEND_FACTOR.SRC_ALPHA
     */
	this.blendSrc = BLEND_FACTOR.SRC_ALPHA;

	/**
     * Blending destination.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_FACTOR}
     * @default zen3d.BLEND_FACTOR.ONE_MINUS_SRC_ALPHA
     */
	this.blendDst = BLEND_FACTOR.ONE_MINUS_SRC_ALPHA;

	/**
     * Blending equation to use when applying blending.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_EQUATION}
     * @default zen3d.BLEND_EQUATION.ADD
     */
	this.blendEquation = BLEND_EQUATION.ADD;

	/**
     * The transparency of the {@link zen3d.Material#blendSrc}.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_FACTOR}
     * @default null
     */
	this.blendSrcAlpha = null;

	/**
     * The transparency of the {@link zen3d.Material#blendDst}.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_FACTOR}
     * @default null
     */
	this.blendDstAlpha = null;

	/**
     * The tranparency of the {@link zen3d.Material#blendEquation}.
     * The {@link zen3d.Material#blending} must be set to zen3d.BLEND_TYPE.CUSTOM for this to have any effect.
     * @type {zen3d.BLEND_EQUATION}
     * @default null
     */
	this.blendEquationAlpha = null;

	/**
     * Whether to premultiply the alpha (transparency) value.
     * @type {boolean}
     * @default false
     */
	this.premultipliedAlpha = false;

	/**
     * Defines whether vertex coloring is used.
     * @type {zen3d.VERTEX_COLOR}
     * @default zen3d.VERTEX_COLOR.NONE
     */
	this.vertexColors = VERTEX_COLOR.NONE;

	/**
     * Defines whether precomputed vertex tangents, which must be provided in a vec4 "tangent" attribute, are used.
     * When disabled, tangents are derived automatically.
     * Using precomputed tangents will give more accurate normal map details in some cases, such as with mirrored UVs.
     * @type {boolean}
     * @default false
     */
	this.vertexTangents = false;

	/**
     * The diffuse color.
     * @type {zen3d.Color3}
     * @default zen3d.Color3(0xffffff)
     */
	this.diffuse = new Color3(0xffffff);

	/**
     * The diffuse map.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.diffuseMap = null;

	/**
     * Define the UV chanel for the diffuse map to use starting from 0 and defaulting to 0.
     * @type {number}
     * @default 0
     */
	this.diffuseMapCoord = 0;

	/**
     * The normal map.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.normalMap = null;

	/**
     * The alpha map.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.alphaMap = null;

	/**
     * Define the UV chanel for the alpha map to use starting from 0 and defaulting to 0.
     * @type {number}
     * @default 0
     */
	this.alphaMapCoord = 0;

	/**
     * The red channel of this texture is used as the ambient occlusion map.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.aoMap = null;

	/**
     * Intensity of the ambient occlusion effect.
     * @type {number}
     * @default 1
     */
	this.aoMapIntensity = 1.0;

	/**
     * Define the UV chanel for the ao map to use starting from 0 and defaulting to 0.
     * @type {number}
     * @default 0
     */
	this.aoMapCoord = 0;

	/**
     * The texture to create a bump map.
     * The black and white values map to the perceived depth in relation to the lights. Bump doesn't actually affect the geometry of the object, only the lighting.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.bumpMap = null;

	/**
     * How much the bump map affects the material.
     * Typical ranges are 0-1.
     * @type {number}
     * @default 1
     */
	this.bumpScale = 1;

	/**
     * The environment map.
     * @type {zen3d.TextureCube}
     * @default null
     */
	this.envMap = null;

	/**
     * Scales the effect of the environment map by multiplying its color.
     * @type {number}
     * @default 1
     */
	this.envMapIntensity = 1;

	/**
     * How to combine the result of the surface's color with the environment map, if any.
     * This has no effect in a {@link zen3d.PBRMaterial}.
     * @type {zen3d.ENVMAP_COMBINE_TYPE}
     * @default zen3d.ENVMAP_COMBINE_TYPE.MULTIPLY
     */
	this.envMapCombine = ENVMAP_COMBINE_TYPE.MULTIPLY;

	/**
     * Emissive (light) color of the material, essentially a solid color unaffected by other lighting.
     * @type {zen3d.Color3}
     * @default zen3d.Color3(0x000000)
     */
	this.emissive = new Color3(0x000000);

	/**
     * Set emissive (glow) map.
     * The emissive map color is modulated by the emissive color and the emissive intensity.
     * If you have an emissive map, be sure to set the emissive color to something other than black.
     * @type {zen3d.Texture2D}
     * @default null
     */
	this.emissiveMap = null;

	/**
     * Define the UV chanel for the emissive map to use starting from 0 and defaulting to 0.
     * @type {number}
     * @default 0
     */
	this.emissiveMapCoord = 0;

	/**
     * Intensity of the emissive light.
     * Modulates the emissive color.
     * @type {number}
     * @default 1
     */
	this.emissiveIntensity = 1;

	/**
      * Which depth function to use. See the {@link zen3d.WEBGL_COMPARE_FUNC} constants for all possible values.
      * @type {zen3d.WEBGL_COMPARE_FUNC}
      * @default zen3d.WEBGL_COMPARE_FUNC.LEQUAL
      */
	this.depthFunc = WEBGL_COMPARE_FUNC.LEQUAL;

	/**
     * Whether to have depth test enabled when rendering this material.
     * @type {boolean}
     * @default true
     */
	this.depthTest = true;

	/**
     * Whether rendering this material has any effect on the depth buffer.
     * When drawing 2D overlays it can be useful to disable the depth writing in order to layer several things together without creating z-index artifacts.
     * @type {boolean}
     * @default true
     */
	this.depthWrite = true;

	/**
     * Whether to render the material's color.
     * This can be used in conjunction with a mesh's renderOrder property to create invisible objects that occlude other objects.
     * @type {boolean}
     * @default true
     */
	this.colorWrite = true;

	/**
     * Whether stencil operations are performed against the stencil buffer.
     * In order to perform writes or comparisons against the stencil buffer this value must be true.
     * @type {boolean}
     * @default false
     */
	this.stencilTest = false;

	/**
     * The bit mask to use when writing to the stencil buffer.
     * @type {number}
     * @default 0xFF
     */
	this.stencilWriteMask = 0xff;

	/**
     * The stencil comparison function to use.
     * See the {@link zen3d.WEBGL_COMPARE_FUNC} constants for all possible values.
     * @type {zen3d.WEBGL_COMPARE_FUNC}
     * @default zen3d.WEBGL_COMPARE_FUNC.ALWAYS
     */
	this.stencilFunc = WEBGL_COMPARE_FUNC.ALWAYS;

	/**
     * The value to use when performing stencil comparisons or stencil operations.
     * @type {number}
     * @default 0
     */
	this.stencilRef = 0;

	/**
     * The bit mask to use when comparing against the stencil buffer.
     * @type {number}
     * @default 0xFF
     */
	this.stencilFuncMask = 0xff;

	/**
     * Which stencil operation to perform when the comparison function returns false.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * @type {zen3d.WEBGL_OP}
     * @default zen3d.WEBGL_OP.KEEP
     */
	this.stencilFail = WEBGL_OP.KEEP;

	/**
     * Which stencil operation to perform when the comparison function returns true but the depth test fails.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * @type {zen3d.WEBGL_OP}
     * @default zen3d.WEBGL_OP.KEEP
     */
	this.stencilZFail = WEBGL_OP.KEEP;

	/**
     * Which stencil operation to perform when the comparison function returns true and the depth test passes.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * @type {zen3d.WEBGL_OP}
     * @default zen3d.WEBGL_OP.KEEP
     */
	this.stencilZPass = WEBGL_OP.KEEP;

	/**
     * The stencil comparison function to use.
     * See the {@link zen3d.WEBGL_COMPARE_FUNC} constants for all possible values.
     * You can explicitly specify the two-sided stencil function state by defining stencilFuncBack, stencilRefBack and stencilFuncMaskBack.
     * @type {zen3d.WEBGL_COMPARE_FUNC|null}
     * @default null
     */
	this.stencilFuncBack = null;

	/**
     * The value to use when performing stencil comparisons or stencil operations.
     * You can explicitly specify the two-sided stencil function state by defining stencilFuncBack, stencilRefBack and stencilFuncMaskBack.
     * @type {number|null}
     * @default null
     */
	this.stencilRefBack = null;

	/**
     * The bit mask to use when comparing against the stencil buffer.
     * You can explicitly specify the two-sided stencil function state by defining stencilFuncBack, stencilRefBack and stencilFuncMaskBack.
     * @type {number|null}
     * @default null
     */
	this.stencilFuncMaskBack = null;

	/**
     * Which stencil operation to perform when the comparison function returns false.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * You can explicitly specify the two-sided stencil op state by defining stencilFailBack, stencilZFailBack and stencilZPassBack.
     * @type {zen3d.WEBGL_OP|null}
     * @default null
     */
	this.stencilFailBack = null;

	/**
     * Which stencil operation to perform when the comparison function returns true but the depth test fails.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * You can explicitly specify the two-sided stencil op state by defining stencilFailBack, stencilZFailBack and stencilZPassBack.
     * @type {zen3d.WEBGL_OP|null}
     * @default null
     */
	this.stencilZFailBack = null;

	/**
     * Which stencil operation to perform when the comparison function returns true and the depth test passes.
     * See the {@link zen3d.WEBGL_OP} constants for all possible values.
     * You can explicitly specify the two-sided stencil op state by defining stencilFailBack, stencilZFailBack and stencilZPassBack.
     * @type {zen3d.WEBGL_OP|null}
     * @default null
     */
	this.stencilZPassBack = null;

	/**
     * Sets the alpha value to be used when running an alpha test.
     * The material will not be renderered if the opacity is lower than this value.
     * @type {number}
     * @default 0
     */
	this.alphaTest = 0;

	/**
     * Defines which side of faces will be rendered - front, back or double.
     * @type {zen3d.DRAW_SIDE}
     * @default zen3d.DRAW_SIDE.FRONT
     */
	this.side = DRAW_SIDE.FRONT;

	/**
     * Whether to use polygon offset.
     * This corresponds to the GL_POLYGON_OFFSET_FILL WebGL feature.
     * @type {boolean}
     * @default false
     */
	this.polygonOffset = false;

	/**
     * Sets the polygon offset factor.
     * @type {number}
     * @default 0
     */
	this.polygonOffsetFactor = 0;

	/**
     * Sets the polygon offset units.
     * @type {number}
     * @default 0
     */
	this.polygonOffsetUnits = 0;

	/**
     * Define whether the material is rendered with flat shading or smooth shading.
     * @type {zen3d.SHADING_TYPE}
     * @default zen3d.SHADING_TYPE.SMOOTH_SHADING
     */
	this.shading = SHADING_TYPE.SMOOTH_SHADING;

	/**
      * Whether to apply dithering to the color to remove the appearance of banding.
      * @type {boolean}
      * @default false
      */
	this.dithering = false;

	/**
     * Whether the material is affected by lights.
     * If set true, renderer will try to upload light uniforms.
     * @type {boolean}
     * @default false
     */
	this.acceptLight = false;

	/**
     * Determines how the mesh triangles are constructed from the vertices.
     * @type {zen3d.DRAW_MODE}
     * @default zen3d.DRAW_MODE.TRIANGLES
     */
	this.drawMode = DRAW_MODE.TRIANGLES;

	/**
     * Specifies that the material needs to be recompiled.
     * This property is automatically set to true when instancing a new material.
     * @type {boolean}
     * @default true
     */
	this.needsUpdate = true;
}

Material.prototype = Object.assign(Object.create(EventDispatcher.prototype), /** @lends zen3d.Material.prototype */{

	constructor: Material,

	/**
     * Copy the parameters from the passed material into this material.
     * @param {zen3d.Material} source - The material to be copied.
     * @return {zen3d.Material}
     */
	copy: function(source) {
		this.type = source.type;
		this.precision = source.precision;
		if (source.defines) {
			this.defines = Object.assign({}, source.defines);
		}
		this.opacity = source.opacity;
		this.transparent = source.transparent;
		this.premultipliedAlpha = source.premultipliedAlpha;
		this.vertexColors = source.vertexColors;
		this.diffuse.copy(source.diffuse);
		this.diffuseMap = source.diffuseMap;
		this.diffuseMapCoord = source.diffuseMapCoord;
		this.alphaMap = source.alphaMap;
		this.alphaMapCoord = source.alphaMapCoord;
		this.normalMap = source.normalMap;
		this.aoMap = source.aoMap;
		this.aoMapIntensity = source.aoMapIntensity;
		this.aoMapCoord = source.aoMapCoord;
		this.bumpMap = source.bumpMap;
		this.bumpScale = source.bumpScale;
		this.envMap = source.envMap;
		this.envMapIntensity = source.envMapIntensity;
		this.envMapCombine = source.envMapCombine;
		this.emissive.copy(source.emissive);
		this.emissiveMap = source.emissiveMap;
		this.emissiveMapCoord = source.emissiveMapCoord;
		this.emissiveIntensity = source.emissiveIntensity;
		this.blending = source.blending;
		this.depthFunc = source.depthFunc;
		this.depthTest = source.depthTest;
		this.depthWrite = source.depthWrite;
		this.colorWrite = source.colorWrite;
		this.alphaTest = source.alphaTest;
		this.side = source.side;
		this.polygonOffset = source.polygonOffset;
		this.polygonOffsetFactor = source.polygonOffsetFactor;
		this.polygonOffsetUnits = source.polygonOffsetUnits;
		this.dithering = source.dithering;
		this.shading = source.shading;
		this.acceptLight = source.acceptLight;
		this.drawMode = source.drawMode;

		return this;
	},

	/**
     * Return a new material with the same parameters as this material.
     * @return {zen3d.Material}
     */
	clone: function() {
		return new this.constructor().copy(this);
	},

	/**
     * This disposes the material.
     * Textures of a material don't get disposed. These needs to be disposed by Texture.
     */
	dispose: function() {
		this.dispatchEvent({ type: 'dispose' });
	}

});

export { Material };