import * as THREE from 'three' ; import { getTexelDecodingFunction } from './getTexelDecodingFunction' ; import vertexShader from './shaders/mtoon.vert' ; import fragmentShader from './shaders/mtoon.frag' ; const TAU = 2.0 * Math . PI ; export interface MToonParameters extends THREE . ShaderMaterialParameters { mToonVersion ? : number ; cutoff ? : number ; color ? : THREE . Vector4 ; shadeColor ? : THREE . Vector4 ; map ? : THREE . Texture ; mainTex ? : THREE . Texture ; mainTex_ST ? : THREE . Vector4 ; shadeTexture ? : THREE . Texture ; bumpScale ? : number ; normalMap ? : THREE . Texture ; normalMapType ? : THREE . NormalMapTypes ; normalScale ? : THREE . Vector2 ; bumpMap ? : THREE . Texture ; receiveShadowRate ? : number ; receiveShadowTexture ? : THREE . Texture ; shadingGradeRate ? : number ; shadingGradeTexture ? : THREE . Texture ; shadeShift ? : number ; shadeToony ? : number ; lightColorAttenuation ? : number ; indirectLightIntensity ? : number ; rimTexture ? : THREE . Texture ; rimColor ? : THREE . Vector4 ; rimLightingMix ? : number ; rimFresnelPower ? : number ; rimLift ? : number ; sphereAdd ? : THREE . Texture ; emissionColor ? : THREE . Vector4 ; emissiveMap ? : THREE . Texture ; emissionMap ? : THREE . Texture ; outlineWidthTexture ? : THREE . Texture ; outlineWidth ? : number ; outlineScaledMaxDistance ? : number ; outlineColor ? : THREE . Vector4 ; outlineLightingMix ? : number ; uvAnimMaskTexture ? : THREE . Texture ; uvAnimScrollX ? : number ; uvAnimScrollY ? : number ; uvAnimRotation ? : number ; debugMode ? : MToonMaterialDebugMode | number ; blendMode ? : MToonMaterialRenderMode | number ; outlineWidthMode ? : MToonMaterialOutlineWidthMode | number ; outlineColorMode ? : MToonMaterialOutlineColorMode | number ; cullMode ? : MToonMaterialCullMode | number ; outlineCullMode ? : MToonMaterialCullMode | number ; srcBlend ? : number ; dstBlend ? : number ; zWrite ? : number ; isOutline ? : boolean ; /**
     * Specify the encoding of input uniform colors.
     *
     * When your `renderer.outputEncoding` is `THREE.LinearEncoding`, use `THREE.LinearEncoding`.
     * When your `renderer.outputEncoding` is `THREE.sRGBEncoding`, use `THREE.sRGBEncoding`.
     *
     * Encodings of textures should be set independently on textures.
     *
     * This will use `THREE.LinearEncoding` if this option isn't specified.
     *
     * See also: https://threejs.org/docs/#api/en/renderers/WebGLRenderer.outputEncoding
     */ encoding ? : THREE . TextureEncoding ; } export enum MToonMaterialCullMode { Off , Front , Back } export enum MToonMaterialDebugMode { None , Normal , LitShadeRate , UV } export enum MToonMaterialOutlineColorMode { FixedColor , MixedLighting } export enum MToonMaterialOutlineWidthMode { None , WorldCoordinates , ScreenCoordinates } export enum MToonMaterialRenderMode { Opaque , Cutout , Transparent , TransparentWithZWrite } /**
 * MToon is a material specification that has various features.
 * The spec and implementation are originally founded for Unity engine and this is a port of the material.
 *
 * See: https://github.com/Santarh/MToon
 */ export class MToonMaterial extends THREE . ShaderMaterial { /**
     * Readonly boolean that indicates this is a [[MToonMaterial]].
     */ public readonly isMToonMaterial : boolean = true ; public cutoff = 0.5 ; public color = new THREE . Vector4 ( 1.0 , 1.0 , 1.0 , 1.0 ) ; public shadeColor = new THREE . Vector4 ( 0.97 , 0.81 , 0.86 , 1.0 ) ; public map : THREE . Texture | null = null ; public mainTex_ST = new THREE . Vector4 ( 0.0 , 0.0 , 1.0 , 1.0 ) ; public shadeTexture : THREE . Texture | null = null ; public normalMap : THREE . Texture | null = null ; public normalMapType = THREE . TangentSpaceNormalMap ; public normalScale = new THREE . Vector2 ( 1.0 , 1.0 ) ; public receiveShadowRate = 1.0 ; public receiveShadowTexture : THREE . Texture | null = null ; public shadingGradeRate = 1.0 ; public shadingGradeTexture : THREE . Texture | null = null ; public shadeShift = 0.0 ; public shadeToony = 0.9 ; public lightColorAttenuation = 0.0 ; public indirectLightIntensity = 0.1 ; public rimTexture : THREE . Texture | null = null ; public rimColor = new THREE . Vector4 ( 0.0 , 0.0 , 0.0 , 1.0 ) ; public rimLightingMix = 0.0 ; public rimFresnelPower = 1.0 ; public rimLift = 0.0 ; public sphereAdd : THREE . Texture | null = null ; public emissionColor = new THREE . Vector4 ( 0.0 , 0.0 , 0.0 , 1.0 ) ; public emissiveMap : THREE . Texture | null = null ; public outlineWidthTexture : THREE . Texture | null = null ; public outlineWidth = 0.5 ; public outlineScaledMaxDistance = 1.0 ; public outlineColor = new THREE . Vector4 ( 0.0 , 0.0 , 0.0 , 1.0 ) ; public outlineLightingMix = 1.0 ; public uvAnimMaskTexture : THREE . Texture | null = null ; public uvAnimScrollX = 0.0 ; public uvAnimScrollY = 0.0 ; public uvAnimRotation = 0.0 ; public shouldApplyUniforms = true ; /**
     * The encoding of input uniform colors.
     *
     * When your `renderer.outputEncoding` is `THREE.LinearEncoding`, use `THREE.LinearEncoding`.
     * When your `renderer.outputEncoding` is `THREE.sRGBEncoding`, use `THREE.sRGBEncoding`.
     *
     * Encodings of textures are set independently on textures.
     *
     * This is `THREE.LinearEncoding` by default.
     *
     * See also: https://threejs.org/docs/#api/en/renderers/WebGLRenderer.outputEncoding
     */ public encoding : THREE . TextureEncoding ; private _debugMode = MToonMaterialDebugMode . None ; private _blendMode = MToonMaterialRenderMode . Opaque ; private _outlineWidthMode = MToonMaterialOutlineWidthMode . None ; private _outlineColorMode = MToonMaterialOutlineColorMode . FixedColor ; private _cullMode = MToonMaterialCullMode . Back ; private _outlineCullMode = MToonMaterialCullMode . Front ; private _isOutline = false ; private _uvAnimOffsetX = 0.0 ; private _uvAnimOffsetY = 0.0 ; private _uvAnimPhase = 0.0 ; constructor ( parameters = { } ) { super ( ) ; this . encoding = parameters . encoding || THREE . LinearEncoding ; if ( this . encoding !== THREE . LinearEncoding && this . encoding !== THREE . sRGBEncoding ) { console . warn ( 'The specified color encoding does not work properly with MToonMaterial. You might want to use THREE.sRGBEncoding instead.' ) ; } [ 'mToonVersion' , 'shadeTexture_ST' , 'bumpMap_ST' , 'receiveShadowTexture_ST' , 'shadingGradeTexture_ST' , 'rimTexture_ST' , 'sphereAdd_ST' , 'emissionMap_ST' , 'outlineWidthTexture_ST' , 'uvAnimMaskTexture_ST' , 'srcBlend' , 'dstBlend' , ] . forEach ( ( key ) => { if ( ( parameters as any ) [ key ] !== undefined ) { delete ( parameters as any ) [ key ] ; } } ) ; parameters . fog = true ; parameters . lights = true ; parameters . clipping = true ; if ( parseInt ( THREE . REVISION , 10 ) < 129 ) { ( parameters as any ) . skinning = ( parameters as any ) . skinning || false ; } if ( parseInt ( THREE . REVISION , 10 ) < 131 ) { ( parameters as any ) . morphTargets = ( parameters as any ) . morphTargets || false ; ( parameters as any ) . morphNormals = ( parameters as any ) . morphNormals || false ; } parameters . uniforms = THREE . UniformsUtils . merge ( [ THREE . UniformsLib . common , THREE . UniformsLib . normalmap , THREE . UniformsLib . emissivemap , THREE . UniformsLib . fog , THREE . UniformsLib . lights , { cutoff : { value : 0.5 } , color : { value : new THREE . Color ( 1.0 , 1.0 , 1.0 ) } , colorAlpha : { value : 1.0 } , shadeColor : { value : new THREE . Color ( 0.97 , 0.81 , 0.86 ) } , mainTex_ST : { value : new THREE . Vector4 ( 0.0 , 0.0 , 1.0 , 1.0 ) } , shadeTexture : { value : null } , receiveShadowRate : { value : 1.0 } , receiveShadowTexture : { value : null } , shadingGradeRate : { value : 1.0 } , shadingGradeTexture : { value : null } , shadeShift : { value : 0.0 } , shadeToony : { value : 0.9 } , lightColorAttenuation : { value : 0.0 } , indirectLightIntensity : { value : 0.1 } , rimTexture : { value : null } , rimColor : { value : new THREE . Color ( 0.0 , 0.0 , 0.0 ) } , rimLightingMix : { value : 0.0 } , rimFresnelPower : { value : 1.0 } , rimLift : { value : 0.0 } , sphereAdd : { value : null } , emissionColor : { value : new THREE . Color ( 0.0 , 0.0 , 0.0 ) } , outlineWidthTexture : { value : null } , outlineWidth : { value : 0.5 } , outlineScaledMaxDistance : { value : 1.0 } , outlineColor : { value : new THREE . Color ( 0.0 , 0.0 , 0.0 ) } , outlineLightingMix : { value : 1.0 } , uvAnimMaskTexture : { value : null } , uvAnimOffsetX : { value : 0.0 } , uvAnimOffsetY : { value : 0.0 } , uvAnimTheta : { value : 0.0 } , } , ] ) ; this . setValues ( parameters ) ; this . _updateShaderCode ( ) ; this . _applyUniforms ( ) ; } get mainTex ( ) : THREE . Texture | null { return this . map ; } set mainTex ( t ) { this . map = t ; } get bumpMap ( ) : THREE . Texture | null { return this . normalMap ; } set bumpMap ( t ) { this . normalMap = t ; } /**
     * Getting the `bumpScale` reutrns its x component of `normalScale` (assuming x and y component of `normalScale` are same).
     */ get bumpScale ( ) : number { return this . normalScale . x ; } /**
     * Setting the `bumpScale` will be convert the value into Vector2 `normalScale` .
     */ set bumpScale ( t ) { this . normalScale . set ( t , t ) ; } get emissionMap ( ) : THREE . Texture | null { return this . emissiveMap ; } set emissionMap ( t ) { this . emissiveMap = t ; } get blendMode ( ) : MToonMaterialRenderMode { return this . _blendMode ; } set blendMode ( m ) { this . _blendMode = m ; this . depthWrite = this . _blendMode !== MToonMaterialRenderMode . Transparent ; this . transparent = this . _blendMode === MToonMaterialRenderMode . Transparent || this . _blendMode === MToonMaterialRenderMode . TransparentWithZWrite ; this . _updateShaderCode ( ) ; } get debugMode ( ) : MToonMaterialDebugMode { return this . _debugMode ; } set debugMode ( m ) { this . _debugMode = m ; this . _updateShaderCode ( ) ; } get outlineWidthMode ( ) : MToonMaterialOutlineWidthMode { return this . _outlineWidthMode ; } set outlineWidthMode ( m ) { this . _outlineWidthMode = m ; this . _updateShaderCode ( ) ; } get outlineColorMode ( ) : MToonMaterialOutlineColorMode { return this . _outlineColorMode ; } set outlineColorMode ( m ) { this . _outlineColorMode = m ; this . _updateShaderCode ( ) ; } get cullMode ( ) : MToonMaterialCullMode { return this . _cullMode ; } set cullMode ( m ) { this . _cullMode = m ; this . _updateCullFace ( ) ; } get outlineCullMode ( ) : MToonMaterialCullMode { return this . _outlineCullMode ; } set outlineCullMode ( m ) { this . _outlineCullMode = m ; this . _updateCullFace ( ) ; } get zWrite ( ) : number { return this . depthWrite ? 1 : 0 ; } set zWrite ( i ) { this . depthWrite = 0.5 <= i ; } get isOutline ( ) : boolean { return this . _isOutline ; } set isOutline ( b ) { this . _isOutline = b ; this . _updateShaderCode ( ) ; this . _updateCullFace ( ) ; } param delta public updateVRMMaterials ( delta ) { this . _uvAnimOffsetX = this . _uvAnimOffsetX + delta * this . uvAnimScrollX ; this . _uvAnimOffsetY = this . _uvAnimOffsetY - delta * this . uvAnimScrollY ; this . _uvAnimPhase = this . _uvAnimPhase + delta * this . uvAnimRotation ; this . _applyUniforms ( ) ; } public copy ( source ) { super . copy ( source ) ; this . cutoff = source . cutoff ; this . color . copy ( source . color ) ; this . shadeColor . copy ( source . shadeColor ) ; this . map = source . map ; this . mainTex_ST . copy ( source . mainTex_ST ) ; this . shadeTexture = source . shadeTexture ; this . normalMap = source . normalMap ; this . normalMapType = source . normalMapType ; this . normalScale . copy ( this . normalScale ) ; this . receiveShadowRate = source . receiveShadowRate ; this . receiveShadowTexture = source . receiveShadowTexture ; this . shadingGradeRate = source . shadingGradeRate ; this . shadingGradeTexture = source . shadingGradeTexture ; this . shadeShift = source . shadeShift ; this . shadeToony = source . shadeToony ; this . lightColorAttenuation = source . lightColorAttenuation ; this . indirectLightIntensity = source . indirectLightIntensity ; this . rimTexture = source . rimTexture ; this . rimColor . copy ( source . rimColor ) ; this . rimLightingMix = source . rimLightingMix ; this . rimFresnelPower = source . rimFresnelPower ; this . rimLift = source . rimLift ; this . sphereAdd = source . sphereAdd ; this . emissionColor . copy ( source . emissionColor ) ; this . emissiveMap = source . emissiveMap ; this . outlineWidthTexture = source . outlineWidthTexture ; this . outlineWidth = source . outlineWidth ; this . outlineScaledMaxDistance = source . outlineScaledMaxDistance ; this . outlineColor . copy ( source . outlineColor ) ; this . outlineLightingMix = source . outlineLightingMix ; this . uvAnimMaskTexture = source . uvAnimMaskTexture ; this . uvAnimScrollX = source . uvAnimScrollX ; this . uvAnimScrollY = source . uvAnimScrollY ; this . uvAnimRotation = source . uvAnimRotation ; this . debugMode = source . debugMode ; this . blendMode = source . blendMode ; this . outlineWidthMode = source . outlineWidthMode ; this . outlineColorMode = source . outlineColorMode ; this . cullMode = source . cullMode ; this . outlineCullMode = source . outlineCullMode ; this . isOutline = source . isOutline ; return this ; } /**
     * Apply updated uniform variables.
     */ private _applyUniforms ( ) { this . uniforms . uvAnimOffsetX . value = this . _uvAnimOffsetX ; this . uniforms . uvAnimOffsetY . value = this . _uvAnimOffsetY ; this . uniforms . uvAnimTheta . value = TAU * this . _uvAnimPhase ; if ( ! this . shouldApplyUniforms ) { return ; } this . shouldApplyUniforms = false ; this . uniforms . cutoff . value = this . cutoff ; this . uniforms . color . value . setRGB ( this . color . x , this . color . y , this . color . z ) ; this . uniforms . colorAlpha . value = this . color . w ; this . uniforms . shadeColor . value . setRGB ( this . shadeColor . x , this . shadeColor . y , this . shadeColor . z ) ; this . uniforms . map . value = this . map ; this . uniforms . mainTex_ST . value . copy ( this . mainTex_ST ) ; this . uniforms . shadeTexture . value = this . shadeTexture ; this . uniforms . normalMap . value = this . normalMap ; this . uniforms . normalScale . value . copy ( this . normalScale ) ; this . uniforms . receiveShadowRate . value = this . receiveShadowRate ; this . uniforms . receiveShadowTexture . value = this . receiveShadowTexture ; this . uniforms . shadingGradeRate . value = this . shadingGradeRate ; this . uniforms . shadingGradeTexture . value = this . shadingGradeTexture ; this . uniforms . shadeShift . value = this . shadeShift ; this . uniforms . shadeToony . value = this . shadeToony ; this . uniforms . lightColorAttenuation . value = this . lightColorAttenuation ; this . uniforms . indirectLightIntensity . value = this . indirectLightIntensity ; this . uniforms . rimTexture . value = this . rimTexture ; this . uniforms . rimColor . value . setRGB ( this . rimColor . x , this . rimColor . y , this . rimColor . z ) ; this . uniforms . rimLightingMix . value = this . rimLightingMix ; this . uniforms . rimFresnelPower . value = this . rimFresnelPower ; this . uniforms . rimLift . value = this . rimLift ; this . uniforms . sphereAdd . value = this . sphereAdd ; this . uniforms . emissionColor . value . setRGB ( this . emissionColor . x , this . emissionColor . y , this . emissionColor . z ) ; this . uniforms . emissiveMap . value = this . emissiveMap ; this . uniforms . outlineWidthTexture . value = this . outlineWidthTexture ; this . uniforms . outlineWidth . value = this . outlineWidth ; this . uniforms . outlineScaledMaxDistance . value = this . outlineScaledMaxDistance ; this . uniforms . outlineColor . value . setRGB ( this . outlineColor . x , this . outlineColor . y , this . outlineColor . z ) ; this . uniforms . outlineLightingMix . value = this . outlineLightingMix ; this . uniforms . uvAnimMaskTexture . value = this . uvAnimMaskTexture ; if ( this . encoding === THREE . sRGBEncoding ) { this . uniforms . color . value . convertSRGBToLinear ( ) ; this . uniforms . shadeColor . value . convertSRGBToLinear ( ) ; this . uniforms . rimColor . value . convertSRGBToLinear ( ) ; this . uniforms . emissionColor . value . convertSRGBToLinear ( ) ; this . uniforms . outlineColor . value . convertSRGBToLinear ( ) ; } this . _updateCullFace ( ) ; } private _updateShaderCode ( ) { const useUvInVert = this . outlineWidthTexture !== null ; const useUvInFrag = this . map !== null || this . shadeTexture !== null || this . receiveShadowTexture !== null || this . shadingGradeTexture !== null || this . rimTexture !== null || this . uvAnimMaskTexture !== null ; this . defines = { THREE_VRM_THREE_REVISION : parseInt ( THREE . REVISION , 10 ) , OUTLINE : this . _isOutline , BLENDMODE_OPAQUE : this . _blendMode === MToonMaterialRenderMode . Opaque , BLENDMODE_CUTOUT : this . _blendMode === MToonMaterialRenderMode . Cutout , BLENDMODE_TRANSPARENT : this . _blendMode === MToonMaterialRenderMode . Transparent || this . _blendMode === MToonMaterialRenderMode . TransparentWithZWrite , MTOON_USE_UV : useUvInVert || useUvInFrag , MTOON_UVS_VERTEX_ONLY : useUvInVert && ! useUvInFrag , USE_SHADETEXTURE : this . shadeTexture !== null , USE_RECEIVESHADOWTEXTURE : this . receiveShadowTexture !== null , USE_SHADINGGRADETEXTURE : this . shadingGradeTexture !== null , USE_RIMTEXTURE : this . rimTexture !== null , USE_SPHEREADD : this . sphereAdd !== null , USE_OUTLINEWIDTHTEXTURE : this . outlineWidthTexture !== null , USE_UVANIMMASKTEXTURE : this . uvAnimMaskTexture !== null , DEBUG_NORMAL : this . _debugMode === MToonMaterialDebugMode . Normal , DEBUG_LITSHADERATE : this . _debugMode === MToonMaterialDebugMode . LitShadeRate , DEBUG_UV : this . _debugMode === MToonMaterialDebugMode . UV , OUTLINE_WIDTH_WORLD : this . _outlineWidthMode === MToonMaterialOutlineWidthMode . WorldCoordinates , OUTLINE_WIDTH_SCREEN : this . _outlineWidthMode === MToonMaterialOutlineWidthMode . ScreenCoordinates , OUTLINE_COLOR_FIXED : this . _outlineColorMode === MToonMaterialOutlineColorMode . FixedColor , OUTLINE_COLOR_MIXED : this . _outlineColorMode === MToonMaterialOutlineColorMode . MixedLighting , } ; const encodings = ( this . shadeTexture !== null ? getTexelDecodingFunction ( 'shadeTextureTexelToLinear' , this . shadeTexture . encoding ) + '\n' : '' ) + ( this . sphereAdd !== null ? getTexelDecodingFunction ( 'sphereAddTexelToLinear' , this . sphereAdd . encoding ) + '\n' : '' ) + ( this . rimTexture !== null ? getTexelDecodingFunction ( 'rimTextureTexelToLinear' , this . rimTexture . encoding ) + '\n' : '' ) ; this . vertexShader = vertexShader ; this . fragmentShader = encodings + fragmentShader ; this . needsUpdate = true ; } private _updateCullFace ( ) { if ( ! this . isOutline ) { if ( this . cullMode === MToonMaterialCullMode . Off ) { this . side = THREE . DoubleSide ; } else if ( this . cullMode === MToonMaterialCullMode . Front ) { this . side = THREE . BackSide ; } else if ( this . cullMode === MToonMaterialCullMode . Back ) { this . side = THREE . FrontSide ; } } else { if ( this . outlineCullMode === MToonMaterialCullMode . Off ) { this . side = THREE . DoubleSide ; } else if ( this . outlineCullMode === MToonMaterialCullMode . Front ) { this . side = THREE . BackSide ; } else if ( this . outlineCullMode === MToonMaterialCullMode . Back ) { this . side = THREE . FrontSide ; } } } }