import { ShaderProgram } from '../shader/shader-program.js';
import { Texture2D } from '../texture/texture2d.js';
import {
DirectionalLightMaterial,
POSITION_ATTRIBUTE_LOCATION,
NORMAL_ATTRIBUTE_LOCATION,
FINAL_MATRIX_UNIFORM_NAME,
WORLD_MATRIX_UNIFORM_NAME,
WORLD_INVERSE_TRANSPOSE_MATRIX_UNIFORM_NAME,
COLOR_UNIFORM_NAME,
LIGHT_DIRECTION_UNIFORM_NAME,
CAMERA_POSITION_UNIFORM_NAME,
AMBIENT_STRENGTH_UNIFORM_NAME,
DIRECTIONAL_STRENGTH_UNIFORM_NAME,
LIGHTING_ENABLED_UNIFORM_NAME,
OPACITY_UNIFORM_NAME,
VECTOR3_ELEMENT_COUNT
} from './directional-light-material.js';
/**
* Attribute location used by `vec2` UV-coordinates.
*
* @type {number}
*/
const UV_ATTRIBUTE_LOCATION = 2;
/**
* String literal for `typeof object` checks.
*
* @type {string}
*/
const TYPEOF_OBJECT = 'object';
/**
* String literal for `typeof boolean` checks.
*
* @type {string}
*/
const TYPEOF_BOOLEAN = 'boolean';
/**
* String literal for `typeof number` checks.
*
* @type {string}
*/
const TYPEOF_NUMBER = 'number';
/**
* Diffuse map sampler uniform name.
*
* @type {string}
*/
const DIFFUSE_MAP_UNIFORM_NAME = 'u_diffuseMap';
/**
* Ambient map sampler uniform name.
*
* @type {string}
*/
const AMBIENT_MAP_UNIFORM_NAME = 'u_ambientMap';
/**
* Specular map sampler uniform name.
*
* @type {string}
*/
const SPECULAR_MAP_UNIFORM_NAME = 'u_specularMap';
/**
* Alpha map sampler uniform name.
*
* @type {string}
*/
const ALPHA_MAP_UNIFORM_NAME = 'u_alphaMap';
/**
* Bump map sampler uniform name.
*
* @type {string}
*/
const BUMP_MAP_UNIFORM_NAME = 'u_bumpMap';
/**
* Displacement map sampler uniform name.
*
* @type {string}
*/
const DISPLACEMENT_MAP_UNIFORM_NAME = 'u_displacementMap';
/**
* Reflection map sampler uniform name.
*
* @type {string}
*/
const REFLECTION_MAP_UNIFORM_NAME = 'u_reflectionMap';
/**
* Ambient color uniform name.
*
* @type {string}
*/
const AMBIENT_COLOR_UNIFORM_NAME = 'u_ambientColor';
/**
* Specular color uniform name.
*
* @type {string}
*/
const SPECULAR_COLOR_UNIFORM_NAME = 'u_specularColor';
/**
* Emissive color uniform name.
*
* @type {string}
*/
const EMISSIVE_COLOR_UNIFORM_NAME = 'u_emissiveColor';
/**
* Specular strength uniform name.
*
* @type {string}
*/
const SPECULAR_STRENGTH_UNIFORM_NAME = 'u_specularStrength';
/**
* Shininess uniform name.
*
* @type {string}
*/
const SHININESS_UNIFORM_NAME = 'u_shininess';
/**
* Specular enable uniform name.
*
* @type {string}
*/
const SPECULAR_ENABLED_UNIFORM_NAME = 'u_useSpecular';
/**
* Optical density uniform name.
*
* @type {string}
*/
const OPTICAL_DENSITY_UNIFORM_NAME = 'u_opticalDensity';
/**
* Bump multiplier uniform name.
*
* @type {string}
*/
const BUMP_MULTIPLIER_UNIFORM_NAME = 'u_bumpMultiplier';
/**
* Displacement scale uniform name.
*
* @type {string}
*/
const DISPLACEMENT_SCALE_UNIFORM_NAME = 'u_displacementScale';
/**
* Diffuse UV-offset uniform name.
*
* @type {string}
*/
const DIFFUSE_UV_OFFSET_UNIFORM_NAME = 'u_diffuseUvOffset';
/**
* Diffuse UV-scale uniform name.
*
* @type {string}
*/
const DIFFUSE_UV_SCALE_UNIFORM_NAME = 'u_diffuseUvScale';
/**
* Ambient UV-offset uniform name.
*
* @type {string}
*/
const AMBIENT_UV_OFFSET_UNIFORM_NAME = 'u_ambientUvOffset';
/**
* Ambient UV-scale uniform name.
*
* @type {string}
*/
const AMBIENT_UV_SCALE_UNIFORM_NAME = 'u_ambientUvScale';
/**
* Specular UV-offset uniform name.
*
* @type {string}
*/
const SPECULAR_UV_OFFSET_UNIFORM_NAME = 'u_specularUvOffset';
/**
* Specular UV-scale uniform name.
*
* @type {string}
*/
const SPECULAR_UV_SCALE_UNIFORM_NAME = 'u_specularUvScale';
/**
* Alpha UV-offset uniform name.
*
* @type {string}
*/
const ALPHA_UV_OFFSET_UNIFORM_NAME = 'u_alphaUvOffset';
/**
* Alpha UV-scale uniform name.
*
* @type {string}
*/
const ALPHA_UV_SCALE_UNIFORM_NAME = 'u_alphaUvScale';
/**
* Bump UV-offset uniform name.
*
* @type {string}
*/
const BUMP_UV_OFFSET_UNIFORM_NAME = 'u_bumpUvOffset';
/**
* Bump UV-scale uniform name.
*
* @type {string}
*/
const BUMP_UV_SCALE_UNIFORM_NAME = 'u_bumpUvScale';
/**
* Displacement UV-offset uniform name.
*
* @type {string}
*/
const DISPLACEMENT_UV_OFFSET_UNIFORM_NAME = 'u_displacementUvOffset';
/**
* Displacement UV-scale uniform name.
*
* @type {string}
*/
const DISPLACEMENT_UV_SCALE_UNIFORM_NAME = 'u_displacementUvScale';
/**
* Reflection UV-offset uniform name.
*
* @type {string}
*/
const REFLECTION_UV_OFFSET_UNIFORM_NAME = 'u_reflectionUvOffset';
/**
* Reflection UV-scale uniform name.
*
* @type {string}
*/
const REFLECTION_UV_SCALE_UNIFORM_NAME = 'u_reflectionUvScale';
/**
* Diffuse map usage uniform name.
*
* @type {string}
*/
const USE_DIFFUSE_MAP_UNIFORM_NAME = 'u_useDiffuseMap';
/**
* Ambient map usage uniform name.
*
* @type {string}
*/
const USE_AMBIENT_MAP_UNIFORM_NAME = 'u_useAmbientMap';
/**
* Specular map usage uniform name.
*
* @type {string}
*/
const USE_SPECULAR_MAP_UNIFORM_NAME = 'u_useSpecularMap';
/**
* Alpha map usage uniform name.
*
* @type {string}
*/
const USE_ALPHA_MAP_UNIFORM_NAME = 'u_useAlphaMap';
/**
* Bump map usage uniform name.
*
* @type {string}
*/
const USE_BUMP_MAP_UNIFORM_NAME = 'u_useBumpMap';
/**
* Displacement map usage uniform name.
*
* @type {string}
*/
const USE_DISPLACEMENT_MAP_UNIFORM_NAME = 'u_useDisplacementMap';
/**
* Reflection map usage uniform name.
*
* @type {string}
*/
const USE_REFLECTION_MAP_UNIFORM_NAME = 'u_useReflectionMap';
/**
* Threshold used to interpret lighting enabled values in shaders.
*
* @type {number}
*/
const LIGHTING_ENABLED_THRESHOLD = 0.5;
/**
* Default diffuse color.
*
* @type {Float32Array}
*/
const DEFAULT_DIFFUSE_COLOR = new Float32Array([1.0, 1.0, 1.0]);
/**
* Default ambient color.
*
* @type {Float32Array}
*/
const DEFAULT_AMBIENT_COLOR = new Float32Array([1.0, 1.0, 1.0]);
/**
* Default specular color.
*
* @type {Float32Array}
*/
const DEFAULT_SPECULAR_COLOR = new Float32Array([1.0, 1.0, 1.0]);
/**
* Default emissive color.
*
* @type {Float32Array}
*/
const DEFAULT_EMISSIVE_COLOR = new Float32Array([0.0, 0.0, 0.0]);
/**
* Default shininess value.
*
* @type {number}
*/
const DEFAULT_SHININESS = 16.0;
/**
* Default specular strength multiplier.
*
* @type {number}
*/
const DEFAULT_SPECULAR_STRENGTH = 1.0;
/**
* Default optical density value.
*
* @type {number}
*/
const DEFAULT_OPTICAL_DENSITY = 1.0;
/**
* Default bump multiplier.
*
* @type {number}
*/
const DEFAULT_BUMP_MULTIPLIER = 1.0;
/**
* Default displacement scale factor.
*
* @type {number}
*/
const DEFAULT_DISPLACEMENT_SCALE = 0.1;
/**
* Default UV offset.
*
* @type {Float32Array}
*/
const DEFAULT_UV_OFFSET = new Float32Array([0.0, 0.0]);
/**
* Default UV scale.
*
* @type {Float32Array}
*/
const DEFAULT_UV_SCALE = new Float32Array([1.0, 1.0]);
/**
* Boolean-as-float value for false.
*
* @type {number}
*/
const FLOAT_FALSE = 0.0;
/**
* Boolean-as-float value for true.
*
* @type {number}
*/
const FLOAT_TRUE = 1.0;
/**
* Default texture unit for diffuse map.
*
* @type {number}
*/
const DEFAULT_DIFFUSE_TEXTURE_UNIT = 0;
/**
* Default texture unit for ambient map.
*
* @type {number}
*/
const DEFAULT_AMBIENT_TEXTURE_UNIT = 1;
/**
* Default texture unit for specular map.
*
* @type {number}
*/
const DEFAULT_SPECULAR_TEXTURE_UNIT = 2;
/**
* Default texture unit for alpha map.
*
* @type {number}
*/
const DEFAULT_ALPHA_TEXTURE_UNIT = 3;
/**
* Default texture unit for bump map.
*
* @type {number}
*/
const DEFAULT_BUMP_TEXTURE_UNIT = 4;
/**
* Default texture unit for displacement map.
*
* @type {number}
*/
const DEFAULT_DISPLACEMENT_TEXTURE_UNIT = 5;
/**
* Default texture unit for reflection map.
*
* @type {number}
*/
const DEFAULT_REFLECTION_TEXTURE_UNIT = 6;
/**
* Zero value used for comparisons.
*
* @type {number}
*/
const ZERO_VALUE = 0.0;
/**
* Error message for invalid material options.
*
* @type {string}
*/
const ERROR_OPTIONS_OBJECT = '`MtlStandardMaterial` expects an options object (plain object).';
/**
* Error message for invalid shininess value.
*
* @type {string}
*/
const ERROR_SHININESS_TYPE = '`MtlStandardMaterial.setShininess` expects a finite number.';
/**
* Error message for invalid specular strength value.
*
* @type {string}
*/
const ERROR_SPECULAR_STRENGTH_TYPE = '`MtlStandardMaterial.setSpecularStrength` expects a finite number.';
/**
* Error message for invalid specular enabled flag.
*
* @type {string}
*/
const ERROR_SPECULAR_ENABLED_TYPE = '`MtlStandardMaterial.setSpecularEnabled` expects a boolean.';
/**
* Error message for invalid optical density value.
*
* @type {string}
*/
const ERROR_OPTICAL_DENSITY_TYPE = '`MtlStandardMaterial.setOpticalDensity` expects a finite number.';
/**
* Error message for invalid bump multiplier value.
*
* @type {string}
*/
const ERROR_BUMP_MULTIPLIER_TYPE = '`MtlStandardMaterial.setBumpMultiplier` expects a finite number.';
/**
* Error message for invalid displacement scale value.
*
* @type {string}
*/
const ERROR_DISPLACEMENT_SCALE_TYPE = '`MtlStandardMaterial.setDisplacementScale` expects a finite number.';
/**
* Error suffix for texture type validation.
*
* @type {string}
*/
const ERROR_EXPECTS_TEXTURE_SUFFIX = ' expects texture as Texture2D.';
/**
* Error suffix for options object validation.
*
* @type {string}
*/
const ERROR_EXPECTS_OPTIONS_OBJECT_SUFFIX = ' expects options as a plain object.';
/**
* Error suffix for texture unit index validation.
*
* @type {string}
*/
const ERROR_EXPECTS_TEXTURE_UNIT_INDEX_SUFFIX = ' expects options.textureUnitIndex as a non-negative integer.';
/**
* Error suffix for vector2 type validation.
*
* @type {string}
*/
const ERROR_EXPECTS_VECTOR2_TYPE_SUFFIX = ' expects a number[] or Float32Array.';
/**
* Error suffix for vector2 component validation.
*
* @type {string}
*/
const ERROR_EXPECTS_VECTOR2_COMPONENTS_SUFFIX = ' expects exactly 2 components.';
/**
* Error suffix for vector3 component validation.
*
* @type {string}
*/
const ERROR_EXPECTS_VECTOR3_COMPONENTS_SUFFIX = ' expects exactly 3 components.';
/**
* GLSL vertex shader source code.
*
* @type {string}
*/
const VERTEX_SHADER_SOURCE = `#version 300 es
precision mediump float;
layout(location = ${POSITION_ATTRIBUTE_LOCATION}) in vec3 a_position;
layout(location = ${NORMAL_ATTRIBUTE_LOCATION}) in vec3 a_normal;
layout(location = ${UV_ATTRIBUTE_LOCATION}) in vec2 a_uv;
uniform mat4 ${FINAL_MATRIX_UNIFORM_NAME};
uniform mat4 ${WORLD_MATRIX_UNIFORM_NAME};
uniform mat4 ${WORLD_INVERSE_TRANSPOSE_MATRIX_UNIFORM_NAME};
uniform sampler2D ${DISPLACEMENT_MAP_UNIFORM_NAME};
uniform float ${USE_DISPLACEMENT_MAP_UNIFORM_NAME};
uniform float ${DISPLACEMENT_SCALE_UNIFORM_NAME};
uniform vec2 ${DISPLACEMENT_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${DISPLACEMENT_UV_SCALE_UNIFORM_NAME};
out vec3 v_worldPosition;
out vec3 v_normal;
out vec2 v_uv;
void main() {
vec2 disp_uv = (a_uv * ${DISPLACEMENT_UV_SCALE_UNIFORM_NAME}) + ${DISPLACEMENT_UV_OFFSET_UNIFORM_NAME};
float displacement = 0.0;
if (${USE_DISPLACEMENT_MAP_UNIFORM_NAME} > 0.5) {
displacement = texture(${DISPLACEMENT_MAP_UNIFORM_NAME}, disp_uv).r * ${DISPLACEMENT_SCALE_UNIFORM_NAME};
}
vec3 displaced_position = a_position + (a_normal * displacement);
gl_Position = ${FINAL_MATRIX_UNIFORM_NAME} * vec4(displaced_position, 1.0);
v_worldPosition = (${WORLD_MATRIX_UNIFORM_NAME} * vec4(displaced_position, 1.0)).xyz;
v_normal = (${WORLD_INVERSE_TRANSPOSE_MATRIX_UNIFORM_NAME} * vec4(a_normal, 0.0)).xyz;
v_uv = a_uv;
}
`;
/**
* GLSL fragment shader source code.
*
* @type {string}
*/
const FRAGMENT_SHADER_SOURCE = `#version 300 es
precision mediump float;
in vec3 v_worldPosition;
in vec3 v_normal;
in vec2 v_uv;
uniform vec3 ${COLOR_UNIFORM_NAME};
uniform vec3 ${AMBIENT_COLOR_UNIFORM_NAME};
uniform vec3 ${SPECULAR_COLOR_UNIFORM_NAME};
uniform vec3 ${EMISSIVE_COLOR_UNIFORM_NAME};
uniform vec3 ${LIGHT_DIRECTION_UNIFORM_NAME};
uniform vec3 ${CAMERA_POSITION_UNIFORM_NAME};
uniform float ${AMBIENT_STRENGTH_UNIFORM_NAME};
uniform float ${DIRECTIONAL_STRENGTH_UNIFORM_NAME};
uniform float ${LIGHTING_ENABLED_UNIFORM_NAME};
uniform float ${SPECULAR_STRENGTH_UNIFORM_NAME};
uniform float ${SHININESS_UNIFORM_NAME};
uniform float ${SPECULAR_ENABLED_UNIFORM_NAME};
uniform float ${OPACITY_UNIFORM_NAME};
uniform float ${OPTICAL_DENSITY_UNIFORM_NAME};
uniform float ${BUMP_MULTIPLIER_UNIFORM_NAME};
uniform sampler2D ${DIFFUSE_MAP_UNIFORM_NAME};
uniform sampler2D ${AMBIENT_MAP_UNIFORM_NAME};
uniform sampler2D ${SPECULAR_MAP_UNIFORM_NAME};
uniform sampler2D ${ALPHA_MAP_UNIFORM_NAME};
uniform sampler2D ${BUMP_MAP_UNIFORM_NAME};
uniform sampler2D ${REFLECTION_MAP_UNIFORM_NAME};
uniform vec2 ${DIFFUSE_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${DIFFUSE_UV_SCALE_UNIFORM_NAME};
uniform vec2 ${AMBIENT_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${AMBIENT_UV_SCALE_UNIFORM_NAME};
uniform vec2 ${SPECULAR_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${SPECULAR_UV_SCALE_UNIFORM_NAME};
uniform vec2 ${ALPHA_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${ALPHA_UV_SCALE_UNIFORM_NAME};
uniform vec2 ${BUMP_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${BUMP_UV_SCALE_UNIFORM_NAME};
uniform vec2 ${REFLECTION_UV_OFFSET_UNIFORM_NAME};
uniform vec2 ${REFLECTION_UV_SCALE_UNIFORM_NAME};
uniform float ${USE_DIFFUSE_MAP_UNIFORM_NAME};
uniform float ${USE_AMBIENT_MAP_UNIFORM_NAME};
uniform float ${USE_SPECULAR_MAP_UNIFORM_NAME};
uniform float ${USE_ALPHA_MAP_UNIFORM_NAME};
uniform float ${USE_BUMP_MAP_UNIFORM_NAME};
uniform float ${USE_REFLECTION_MAP_UNIFORM_NAME};
out vec4 outColor;
vec2 apply_uv(vec2 base_uv, vec2 offset, vec2 scale) {
return (base_uv * scale) + offset;
}
vec3 compute_bump_normal(vec3 normal, vec2 uv) {
vec3 tangent_normal = texture(${BUMP_MAP_UNIFORM_NAME}, uv).xyz * 2.0 - 1.0;
tangent_normal.xy *= ${BUMP_MULTIPLIER_UNIFORM_NAME};
tangent_normal = normalize(tangent_normal);
vec3 dp1 = dFdx(v_worldPosition);
vec3 dp2 = dFdy(v_worldPosition);
vec2 duv1 = dFdx(uv);
vec2 duv2 = dFdy(uv);
vec3 tangent = normalize(dp1 * duv2.y - dp2 * duv1.y);
vec3 bitangent = normalize(-dp1 * duv2.x + dp2 * duv1.x);
mat3 tbn = mat3(tangent, bitangent, normal);
return normalize(tbn * tangent_normal);
}
vec2 compute_reflection_uv(vec3 normal, vec3 view_dir) {
vec3 reflect_dir = reflect(-view_dir, normal);
float m = 2.0 * sqrt(reflect_dir.x * reflect_dir.x
+ reflect_dir.y * reflect_dir.y
+ (reflect_dir.z + 1.0) * (reflect_dir.z + 1.0));
return (reflect_dir.xy / m) + vec2(0.5, 0.5);
}
void main() {
vec3 diffuse_color = ${COLOR_UNIFORM_NAME};
vec3 diffuse_map_color = vec3(1.0);
if (${USE_DIFFUSE_MAP_UNIFORM_NAME} > 0.5) {
vec2 diff_uv = apply_uv(v_uv, ${DIFFUSE_UV_OFFSET_UNIFORM_NAME}, ${DIFFUSE_UV_SCALE_UNIFORM_NAME});
diffuse_map_color = texture(${DIFFUSE_MAP_UNIFORM_NAME}, diff_uv).rgb;
diffuse_color *= diffuse_map_color;
}
float alpha = ${OPACITY_UNIFORM_NAME};
if (${USE_ALPHA_MAP_UNIFORM_NAME} > 0.5) {
vec2 alpha_uv = apply_uv(v_uv, ${ALPHA_UV_OFFSET_UNIFORM_NAME}, ${ALPHA_UV_SCALE_UNIFORM_NAME});
alpha *= texture(${ALPHA_MAP_UNIFORM_NAME}, alpha_uv).r;
}
if (${LIGHTING_ENABLED_UNIFORM_NAME} <= ${LIGHTING_ENABLED_THRESHOLD}) {
vec3 unlit_color = diffuse_color;
if (${USE_DIFFUSE_MAP_UNIFORM_NAME} > 0.5) {
unlit_color = diffuse_map_color;
}
vec3 rgb = unlit_color + ${EMISSIVE_COLOR_UNIFORM_NAME};
outColor = vec4(rgb, alpha);
return;
}
vec3 normal = normalize(v_normal);
vec3 view_dir = normalize(${CAMERA_POSITION_UNIFORM_NAME} - v_worldPosition);
if (${USE_BUMP_MAP_UNIFORM_NAME} > 0.5) {
vec2 bump_uv = apply_uv(v_uv, ${BUMP_UV_OFFSET_UNIFORM_NAME}, ${BUMP_UV_SCALE_UNIFORM_NAME});
normal = compute_bump_normal(normal, bump_uv);
}
if (!gl_FrontFacing) {
normal = -normal;
}
vec3 ambient_tint = ${AMBIENT_COLOR_UNIFORM_NAME};
if (${USE_AMBIENT_MAP_UNIFORM_NAME} > 0.5) {
vec2 amb_uv = apply_uv(v_uv, ${AMBIENT_UV_OFFSET_UNIFORM_NAME}, ${AMBIENT_UV_SCALE_UNIFORM_NAME});
ambient_tint *= texture(${AMBIENT_MAP_UNIFORM_NAME}, amb_uv).rgb;
}
vec3 specular_color = ${SPECULAR_COLOR_UNIFORM_NAME};
if (${USE_SPECULAR_MAP_UNIFORM_NAME} > 0.5) {
vec2 spec_uv = apply_uv(v_uv, ${SPECULAR_UV_OFFSET_UNIFORM_NAME}, ${SPECULAR_UV_SCALE_UNIFORM_NAME});
specular_color *= texture(${SPECULAR_MAP_UNIFORM_NAME}, spec_uv).rgb;
}
vec3 light_direction = normalize(${LIGHT_DIRECTION_UNIFORM_NAME});
float diffuse_intensity = max(dot(normal, light_direction), 0.0);
vec3 ambient = diffuse_color * ambient_tint * ${AMBIENT_STRENGTH_UNIFORM_NAME};
vec3 diffuse = diffuse_color * (diffuse_intensity * ${DIRECTIONAL_STRENGTH_UNIFORM_NAME});
float specular_intensity = 0.0;
if (${SPECULAR_ENABLED_UNIFORM_NAME} > 0.5 && diffuse_intensity > 0.0) {
vec3 reflection_direction = reflect(-light_direction, normal);
float specular_base = max(dot(view_dir, reflection_direction), 0.0);
specular_intensity = pow(specular_base, ${SHININESS_UNIFORM_NAME});
}
vec3 specular = specular_color * (specular_intensity * ${SPECULAR_STRENGTH_UNIFORM_NAME}
* ${DIRECTIONAL_STRENGTH_UNIFORM_NAME});
vec3 emissive = ${EMISSIVE_COLOR_UNIFORM_NAME};
vec3 rgb = ambient + diffuse + specular + emissive;
if (${USE_REFLECTION_MAP_UNIFORM_NAME} > 0.5) {
vec2 refl_uv = compute_reflection_uv(normal, view_dir);
vec2 refl_uv_scaled = apply_uv(refl_uv, ${REFLECTION_UV_OFFSET_UNIFORM_NAME}, ${REFLECTION_UV_SCALE_UNIFORM_NAME});
vec3 refl_color = texture(${REFLECTION_MAP_UNIFORM_NAME}, refl_uv_scaled).rgb;
float refl_strength = clamp(${OPTICAL_DENSITY_UNIFORM_NAME} - 1.0, 0.0, 1.0);
rgb = mix(rgb, refl_color, refl_strength);
}
outColor = vec4(rgb, alpha);
}
`;
/**
* Options used by `MtlStandardMaterial`.
*
* @typedef {Object} MtlStandardMaterialOptions
* @property {Float32Array | number[]} [diffuseColor] - Diffuse RGB color in 0..1 range.
* @property {Float32Array | number[]} [ambientColor] - Ambient RGB color in 0..1 range.
* @property {Float32Array | number[]} [specularColor] - Specular RGB color in 0..1 range.
* @property {Float32Array | number[]} [emissiveColor] - Emissive RGB color in 0..1 range.
* @property {Float32Array | number[]} [lightDirection]- Directional light direction (world space).
* @property {number} [ambientStrength] - Ambient strength multiplier.
* @property {number} [shininess] - Specular exponent.
* @property {number} [specularStrength] - Specular strength multiplier.
* @property {number} [opticalDensity] - Optical density used for reflection blending.
*/
/**
* Texture map setter options.
*
* @typedef {Object} MtlStandardMaterialMapOptions
* @property {number} [textureUnitIndex] - Texture unit index.
* @property {Float32Array | number[]} [uvOffset] - UV-offset vector.
* @property {Float32Array | number[]} [uvScale] - UV-scale vector.
*/
/**
* Material for MTL assets with multiple texture maps and Phong lighting.
*/
export class MtlStandardMaterial extends DirectionalLightMaterial {
/**
* Fallback texture used, when a map is not assigned.
*
* @type {Texture2D}
* @private
*/
#fallbackTexture;
/**
* Diffuse texture.
*
* @type {Texture2D}
* @private
*/
#diffuseTexture;
/**
* Ambient texture.
*
* @type {Texture2D}
* @private
*/
#ambientTexture;
/**
* Specular texture.
*
* @type {Texture2D}
* @private
*/
#specularTexture;
/**
* Alpha texture.
*
* @type {Texture2D}
* @private
*/
#alphaTexture;
/**
* Bump texture.
*
* @type {Texture2D}
* @private
*/
#bumpTexture;
/**
* Displacement texture.
*
* @type {Texture2D}
* @private
*/
#displacementTexture;
/**
* Reflection texture.
*
* @type {Texture2D}
* @private
*/
#reflectionTexture;
/**
* Diffuse texture unit index.
*
* @type {number}
* @private
*/
#diffuseTextureUnit = DEFAULT_DIFFUSE_TEXTURE_UNIT;
/**
* Ambient texture unit index.
*
* @type {number}
* @private
*/
#ambientTextureUnit = DEFAULT_AMBIENT_TEXTURE_UNIT;
/**
* Specular texture unit index.
*
* @type {number}
* @private
*/
#specularTextureUnit = DEFAULT_SPECULAR_TEXTURE_UNIT;
/**
* Alpha texture unit index.
*
* @type {number}
* @private
*/
#alphaTextureUnit = DEFAULT_ALPHA_TEXTURE_UNIT;
/**
* Bump texture unit index.
*
* @type {number}
* @private
*/
#bumpTextureUnit = DEFAULT_BUMP_TEXTURE_UNIT;
/**
* Displacement texture unit index.
*
* @type {number}
* @private
*/
#displacementTextureUnit = DEFAULT_DISPLACEMENT_TEXTURE_UNIT;
/**
* Reflection texture unit index.
*
* @type {number}
* @private
*/
#reflectionTextureUnit = DEFAULT_REFLECTION_TEXTURE_UNIT;
/**
* Ambient color.
*
* @type {Float32Array}
* @private
*/
#ambientColor = new Float32Array(DEFAULT_AMBIENT_COLOR);
/**
* Specular color.
*
* @type {Float32Array}
* @private
*/
#specularColor = new Float32Array(DEFAULT_SPECULAR_COLOR);
/**
* Emissive color.
*
* @type {Float32Array}
* @private
*/
#emissiveColor = new Float32Array(DEFAULT_EMISSIVE_COLOR);
/**
* Shininess exponent.
*
* @type {number}
* @private
*/
#shininess = DEFAULT_SHININESS;
/**
* Specular strength multiplier.
*
* @type {number}
* @private
*/
#specularStrength = DEFAULT_SPECULAR_STRENGTH;
/**
* Optical density value.
*
* @type {number}
* @private
*/
#opticalDensity = DEFAULT_OPTICAL_DENSITY;
/**
* Bump multiplier.
*
* @type {number}
* @private
*/
#bumpMultiplier = DEFAULT_BUMP_MULTIPLIER;
/**
* Displacement scale factor.
*
* @type {number}
* @private
*/
#displacementScale = DEFAULT_DISPLACEMENT_SCALE;
/**
* Flag, controlling the specular lighting.
*
* @type {boolean}
* @private
*/
#specularEnabled = true;
/**
* Diffuse UV-offset.
*
* @type {Float32Array}
* @private
*/
#diffuseUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Diffuse UV-scale.
*
* @type {Float32Array}
* @private
*/
#diffuseUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Ambient UV-offset.
*
* @type {Float32Array}
* @private
*/
#ambientUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Ambient UV-scale.
*
* @type {Float32Array}
* @private
*/
#ambientUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Specular UV-offset.
*
* @type {Float32Array}
* @private
*/
#specularUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Specular UV-scale.
*
* @type {Float32Array}
* @private
*/
#specularUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Alpha UV-offset.
*
* @type {Float32Array}
* @private
*/
#alphaUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Alpha UV-scale.
*
* @type {Float32Array}
* @private
*/
#alphaUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Bump UV-offset.
*
* @type {Float32Array}
* @private
*/
#bumpUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Bump UV-scale.
*
* @type {Float32Array}
* @private
*/
#bumpUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Displacement UV-offset.
*
* @type {Float32Array}
* @private
*/
#displacementUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Displacement UV-scale.
*
* @type {Float32Array}
* @private
*/
#displacementUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Reflection UV-offset.
*
* @type {Float32Array}
* @private
*/
#reflectionUvOffset = new Float32Array(DEFAULT_UV_OFFSET);
/**
* Reflection UV-scale.
*
* @type {Float32Array}
* @private
*/
#reflectionUvScale = new Float32Array(DEFAULT_UV_SCALE);
/**
* Flag, indicating the diffuse map usage.
*
* @type {boolean}
* @private
*/
#useDiffuseMap = false;
/**
* Flag, indicating the ambient map usage.
*
* @type {boolean}
* @private
*/
#useAmbientMap = false;
/**
* Flag, indicating the specular map usage.
*
* @type {boolean}
* @private
*/
#useSpecularMap = false;
/**
* Flag, indicating the alpha map usage.
*
* @type {boolean}
* @private
*/
#useAlphaMap = false;
/**
* Flag, indicating the bump map usage.
*
* @type {boolean}
* @private
*/
#useBumpMap = false;
/**
* Flag, indicating the displacement map usage.
*
* @type {boolean}
* @private
*/
#useDisplacementMap = false;
/**
* Flag, indicating the reflection map usage.
*
* @type {boolean}
* @private
*/
#useReflectionMap = false;
/**
* Creates a new MTL standard material.
*
* @param {WebGL2RenderingContext} webglContext - WebGL2 rendering context, used to compile the shaders.
* @param {MtlStandardMaterialOptions} [options] - Material options.
*/
constructor(webglContext, options = {}) {
if (options === null || typeof options !== TYPEOF_OBJECT || Array.isArray(options)) {
throw new TypeError(ERROR_OPTIONS_OBJECT);
}
const {
diffuseColor,
ambientColor,
specularColor,
emissiveColor,
lightDirection,
ambientStrength,
shininess,
specularStrength,
opticalDensity
} = options;
super(
webglContext,
new ShaderProgram(webglContext, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE),
{
color : diffuseColor || DEFAULT_DIFFUSE_COLOR,
lightDirection : lightDirection,
ambientStrength : ambientStrength
},
{ ownsShaderProgram: true }
);
this.#fallbackTexture = new Texture2D(webglContext);
this.#diffuseTexture = this.#fallbackTexture;
this.#ambientTexture = this.#fallbackTexture;
this.#specularTexture = this.#fallbackTexture;
this.#alphaTexture = this.#fallbackTexture;
this.#bumpTexture = this.#fallbackTexture;
this.#displacementTexture = this.#fallbackTexture;
this.#reflectionTexture = this.#fallbackTexture;
this.setAmbientColor(ambientColor || DEFAULT_AMBIENT_COLOR);
this.setSpecularColor(specularColor || DEFAULT_SPECULAR_COLOR);
this.setEmissiveColor(emissiveColor || DEFAULT_EMISSIVE_COLOR);
if (shininess !== undefined) {
this.setShininess(shininess);
}
if (specularStrength !== undefined) {
this.setSpecularStrength(specularStrength);
}
if (opticalDensity !== undefined) {
this.setOpticalDensity(opticalDensity);
}
}
/**
* Uploads per-object uniforms specific to the standard material.
*
* @param {Float32Array} worldMatrix - World matrix.
* @param {Float32Array} cameraPosition - Camera position.
* @protected
*/
applyAdditionalUniforms(worldMatrix, cameraPosition) {
// Transform & camera uniforms:
this.shaderProgram.setMatrix4(WORLD_MATRIX_UNIFORM_NAME, worldMatrix);
this.shaderProgram.setVector3(CAMERA_POSITION_UNIFORM_NAME, cameraPosition);
// Material colors & shading parameters:
this.shaderProgram.setVector3(AMBIENT_COLOR_UNIFORM_NAME, this.#ambientColor);
this.shaderProgram.setVector3(SPECULAR_COLOR_UNIFORM_NAME, this.#specularColor);
this.shaderProgram.setVector3(EMISSIVE_COLOR_UNIFORM_NAME, this.#emissiveColor);
this.shaderProgram.setFloat(SPECULAR_STRENGTH_UNIFORM_NAME, this.#specularStrength);
this.shaderProgram.setFloat(SHININESS_UNIFORM_NAME, this.#shininess);
this.shaderProgram.setFloat(SPECULAR_ENABLED_UNIFORM_NAME, this.#specularEnabled ? FLOAT_TRUE : FLOAT_FALSE);
// Optical & surface detail parameters:
this.shaderProgram.setFloat(OPTICAL_DENSITY_UNIFORM_NAME, this.#opticalDensity);
this.shaderProgram.setFloat(BUMP_MULTIPLIER_UNIFORM_NAME, this.#bumpMultiplier);
this.shaderProgram.setFloat(DISPLACEMENT_SCALE_UNIFORM_NAME, this.#displacementScale);
// Per-map UV transforms (offset/scale):
this.shaderProgram.setVector2(DIFFUSE_UV_OFFSET_UNIFORM_NAME, this.#diffuseUvOffset);
this.shaderProgram.setVector2(DIFFUSE_UV_SCALE_UNIFORM_NAME, this.#diffuseUvScale);
this.shaderProgram.setVector2(AMBIENT_UV_OFFSET_UNIFORM_NAME, this.#ambientUvOffset);
this.shaderProgram.setVector2(AMBIENT_UV_SCALE_UNIFORM_NAME, this.#ambientUvScale);
this.shaderProgram.setVector2(SPECULAR_UV_OFFSET_UNIFORM_NAME, this.#specularUvOffset);
this.shaderProgram.setVector2(SPECULAR_UV_SCALE_UNIFORM_NAME, this.#specularUvScale);
this.shaderProgram.setVector2(ALPHA_UV_OFFSET_UNIFORM_NAME, this.#alphaUvOffset);
this.shaderProgram.setVector2(ALPHA_UV_SCALE_UNIFORM_NAME, this.#alphaUvScale);
this.shaderProgram.setVector2(BUMP_UV_OFFSET_UNIFORM_NAME, this.#bumpUvOffset);
this.shaderProgram.setVector2(BUMP_UV_SCALE_UNIFORM_NAME, this.#bumpUvScale);
this.shaderProgram.setVector2(DISPLACEMENT_UV_OFFSET_UNIFORM_NAME, this.#displacementUvOffset);
this.shaderProgram.setVector2(DISPLACEMENT_UV_SCALE_UNIFORM_NAME, this.#displacementUvScale);
this.shaderProgram.setVector2(REFLECTION_UV_OFFSET_UNIFORM_NAME, this.#reflectionUvOffset);
this.shaderProgram.setVector2(REFLECTION_UV_SCALE_UNIFORM_NAME, this.#reflectionUvScale);
// Per-map usage flags (as float booleans for GLSL):
this.shaderProgram.setFloat(USE_DIFFUSE_MAP_UNIFORM_NAME, this.#useDiffuseMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_AMBIENT_MAP_UNIFORM_NAME, this.#useAmbientMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_SPECULAR_MAP_UNIFORM_NAME, this.#useSpecularMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_ALPHA_MAP_UNIFORM_NAME, this.#useAlphaMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_BUMP_MAP_UNIFORM_NAME, this.#useBumpMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_DISPLACEMENT_MAP_UNIFORM_NAME, this.#useDisplacementMap ? FLOAT_TRUE : FLOAT_FALSE);
this.shaderProgram.setFloat(USE_REFLECTION_MAP_UNIFORM_NAME, this.#useReflectionMap ? FLOAT_TRUE : FLOAT_FALSE);
// Texture bindings (samplers + texture units):
this.shaderProgram.setTexture2D(DIFFUSE_MAP_UNIFORM_NAME, this.#diffuseTexture, this.#diffuseTextureUnit);
this.shaderProgram.setTexture2D(AMBIENT_MAP_UNIFORM_NAME, this.#ambientTexture, this.#ambientTextureUnit);
this.shaderProgram.setTexture2D(SPECULAR_MAP_UNIFORM_NAME, this.#specularTexture, this.#specularTextureUnit);
this.shaderProgram.setTexture2D(ALPHA_MAP_UNIFORM_NAME, this.#alphaTexture, this.#alphaTextureUnit);
this.shaderProgram.setTexture2D(BUMP_MAP_UNIFORM_NAME, this.#bumpTexture, this.#bumpTextureUnit);
this.shaderProgram.setTexture2D(DISPLACEMENT_MAP_UNIFORM_NAME, this.#displacementTexture, this.#displacementTextureUnit);
this.shaderProgram.setTexture2D(REFLECTION_MAP_UNIFORM_NAME, this.#reflectionTexture, this.#reflectionTextureUnit);
}
/**
* Sets the ambient color.
*
* @param {Float32Array | number[]} color - RGB color in [0..1] range.
*/
setAmbientColor(color) {
MtlStandardMaterial.#assertVector3('`MtlStandardMaterial.setAmbientColor`', color);
this.#ambientColor.set(color);
}
/**
* Sets the specular color.
*
* @param {Float32Array | number[]} color - RGB color in [0..1] range.
*/
setSpecularColor(color) {
MtlStandardMaterial.#assertVector3('`MtlStandardMaterial.setSpecularColor`', color);
this.#specularColor.set(color);
}
/**
* Sets the emissive color.
*
* @param {Float32Array | number[]} color - RGB color in [0..1] range.
*/
setEmissiveColor(color) {
MtlStandardMaterial.#assertVector3('`MtlStandardMaterial.setEmissiveColor`', color);
this.#emissiveColor.set(color);
}
/**
* Sets the shininess exponent.
*
* @param {number} value - Shininess exponent.
*/
setShininess(value) {
if (typeof value !== TYPEOF_NUMBER || !Number.isFinite(value)) {
throw new TypeError(ERROR_SHININESS_TYPE);
}
this.#shininess = value;
}
/**
* Sets the specular strength multiplier.
*
* @param {number} value - Specular strength.
*/
setSpecularStrength(value) {
if (typeof value !== TYPEOF_NUMBER || !Number.isFinite(value)) {
throw new TypeError(ERROR_SPECULAR_STRENGTH_TYPE);
}
this.#specularStrength = value;
}
/**
* Enables or disables specular term.
*
* @param {boolean} enabled - Specular usage flag.
*/
setSpecularEnabled(enabled) {
if (typeof enabled !== TYPEOF_BOOLEAN) {
throw new TypeError(ERROR_SPECULAR_ENABLED_TYPE);
}
this.#specularEnabled = enabled;
}
/**
* Sets optical density value.
*
* @param {number} value - Optical density.
*/
setOpticalDensity(value) {
if (typeof value !== TYPEOF_NUMBER || !Number.isFinite(value)) {
throw new TypeError(ERROR_OPTICAL_DENSITY_TYPE);
}
this.#opticalDensity = value;
}
/**
* Sets bump multiplier value.
*
* @param {number} value - Bump multiplier.
*/
setBumpMultiplier(value) {
if (typeof value !== TYPEOF_NUMBER || !Number.isFinite(value)) {
throw new TypeError(ERROR_BUMP_MULTIPLIER_TYPE);
}
this.#bumpMultiplier = value;
}
/**
* Sets displacement scale.
*
* @param {number} value - Displacement scale factor.
*/
setDisplacementScale(value) {
if (typeof value !== TYPEOF_NUMBER || !Number.isFinite(value)) {
throw new TypeError(ERROR_DISPLACEMENT_SCALE_TYPE);
}
this.#displacementScale = value;
}
/**
* Sets a diffuse texture and its UV-transform.
*
* @param {Texture2D} texture - Diffuse texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setDiffuseMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setDiffuseMap`',
texture,
options,
(map) => {
this.#diffuseTexture = map.texture;
this.#diffuseTextureUnit = map.textureUnitIndex;
this.#useDiffuseMap = true;
this.#diffuseUvOffset.set(map.uvOffset);
this.#diffuseUvScale.set(map.uvScale);
}
);
}
/**
* Sets an ambient texture and its UV-transform.
*
* @param {Texture2D} texture - Ambient texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setAmbientMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setAmbientMap`',
texture,
options,
(map) => {
this.#ambientTexture = map.texture;
this.#ambientTextureUnit = map.textureUnitIndex;
this.#useAmbientMap = true;
this.#ambientUvOffset.set(map.uvOffset);
this.#ambientUvScale.set(map.uvScale);
}
);
}
/**
* Sets a specular texture and its UV-transform.
*
* @param {Texture2D} texture - Specular texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setSpecularMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setSpecularMap`',
texture,
options,
(map) => {
this.#specularTexture = map.texture;
this.#specularTextureUnit = map.textureUnitIndex;
this.#useSpecularMap = true;
this.#specularUvOffset.set(map.uvOffset);
this.#specularUvScale.set(map.uvScale);
}
);
}
/**
* Sets an alpha texture and its UV-transform.
*
* @param {Texture2D} texture - Alpha texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setAlphaMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setAlphaMap`',
texture,
options,
(map) => {
this.#alphaTexture = map.texture;
this.#alphaTextureUnit = map.textureUnitIndex;
this.#useAlphaMap = true;
this.#alphaUvOffset.set(map.uvOffset);
this.#alphaUvScale.set(map.uvScale);
}
);
}
/**
* Sets a bump texture and its UV-transform.
*
* @param {Texture2D} texture - Bump texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setBumpMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setBumpMap`',
texture,
options,
(map) => {
this.#bumpTexture = map.texture;
this.#bumpTextureUnit = map.textureUnitIndex;
this.#useBumpMap = true;
this.#bumpUvOffset.set(map.uvOffset);
this.#bumpUvScale.set(map.uvScale);
}
);
}
/**
* Sets a displacement texture and its UV-transform.
*
* @param {Texture2D} texture - Displacement texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setDisplacementMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setDisplacementMap`',
texture,
options,
(map) => {
this.#displacementTexture = map.texture;
this.#displacementTextureUnit = map.textureUnitIndex;
this.#useDisplacementMap = true;
this.#displacementUvOffset.set(map.uvOffset);
this.#displacementUvScale.set(map.uvScale);
}
);
}
/**
* Sets a reflection texture and its UV-transform.
*
* @param {Texture2D} texture - Reflection texture.
* @param {MtlStandardMaterialMapOptions} [options] - Map options.
*/
setReflectionMap(texture, options = {}) {
this.#setMap(
'`MtlStandardMaterial.setReflectionMap`',
texture,
options,
(map) => {
this.#reflectionTexture = map.texture;
this.#reflectionTextureUnit = map.textureUnitIndex;
this.#useReflectionMap = true;
this.#reflectionUvOffset.set(map.uvOffset);
this.#reflectionUvScale.set(map.uvScale);
}
);
}
/**
* Disposes resources, owned by this material.
*/
dispose() {
if (this.isDisposed) {
return;
}
if (this.#fallbackTexture) {
this.#fallbackTexture.dispose();
}
super.dispose();
}
/**
* Validates and applies the map options for a texture assignment.
*
* @param {string} context - Error context.
* @param {Texture2D} texture - Map texture.
* @param {MtlStandardMaterialMapOptions} options - Map options.
* @param {function(Object): void} apply - Apply callback.
* @returns {void}
* @private
*/
#setMap(context, texture, options, apply) {
if (!(texture instanceof Texture2D)) {
throw new TypeError(context + ERROR_EXPECTS_TEXTURE_SUFFIX);
}
if (options === null || typeof options !== TYPEOF_OBJECT || Array.isArray(options)) {
throw new TypeError(context + ERROR_EXPECTS_OPTIONS_OBJECT_SUFFIX);
}
const {
textureUnitIndex = DEFAULT_DIFFUSE_TEXTURE_UNIT,
uvOffset = DEFAULT_UV_OFFSET,
uvScale = DEFAULT_UV_SCALE
} = options;
if (!Number.isInteger(textureUnitIndex) || textureUnitIndex < ZERO_VALUE) {
throw new TypeError(context + ERROR_EXPECTS_TEXTURE_UNIT_INDEX_SUFFIX);
}
MtlStandardMaterial.#assertVector2(`${context} options.uvOffset` , uvOffset);
MtlStandardMaterial.#assertVector2(`${context} options.uvScale` , uvScale);
apply({
texture,
textureUnitIndex,
uvOffset,
uvScale
});
}
/**
* Validates the `vector2` arrays.
*
* @param {string} context - Error context.
* @param {Float32Array | number[]} value - Vector to validate.
* @returns {void}
* @private
*/
static #assertVector2(context, value) {
if (!Array.isArray(value) && !(value instanceof Float32Array)) {
throw new TypeError(context + ERROR_EXPECTS_VECTOR2_TYPE_SUFFIX);
}
if (value.length !== DEFAULT_UV_OFFSET.length) {
throw new TypeError(context + ERROR_EXPECTS_VECTOR2_COMPONENTS_SUFFIX);
}
}
/**
* Validates the `vector3` arrays.
*
* @param {string} context - Error context.
* @param {Float32Array | number[]} value - Vector to validate.
* @returns {void}
* @private
*/
static #assertVector3(context, value) {
if (!Array.isArray(value) && !(value instanceof Float32Array)) {
throw new TypeError(context + ERROR_EXPECTS_VECTOR2_TYPE_SUFFIX);
}
if (value.length !== VECTOR3_ELEMENT_COUNT) {
throw new TypeError(context + ERROR_EXPECTS_VECTOR3_COMPONENTS_SUFFIX);
}
}
}