import { Vector4, Vector2 } from '@zephyr3d/base';
import { applyMaterialMixins, MeshMaterial } from './meshmaterial.js';
import { mixinPBRMetallicRoughness } from './mixins/lightmodel/pbrmetallicroughness.js';
import { RENDER_PASS_TYPE_LIGHT } from '../values.js';
import { ShaderHelper } from './shader/helper.js';
import { mixinFoliage } from './mixins/foliage.js';

/**
 * Terrain grass material
 * @public
 */ class GrassMaterial extends applyMaterialMixins(MeshMaterial, mixinPBRMetallicRoughness, mixinFoliage) {
    /** @internal */ _terrainSize;
    /** @internal */ _terrainNormalMap;
    /** @internal */ _textureSize;
    /**
   * Creates an instance of GrassMaterial class
   * @param terrainSize - terrain size
   * @param normalMap - normal map
   * @param grassTexture - grass texture
   */ constructor(terrainSize, normalMap, grassTexture){
        super();
        this.metallic = 0;
        this.roughness = 1;
        this.specularFactor = new Vector4(1, 1, 1, 0.2);
        this.doubleSidedLighting = false;
        this._terrainSize = terrainSize;
        this._terrainNormalMap = normalMap;
        this._textureSize = Vector2.one();
        if (grassTexture) {
            this.albedoTexture = grassTexture;
            this._textureSize.setXY(grassTexture.width, grassTexture.height);
        }
    }
    clone() {
        const other = new GrassMaterial(this._terrainSize, this._terrainNormalMap, this.albedoTexture);
        other.copyFrom(this);
        return other;
    }
    /**
   * {@inheritDoc MeshMaterial.isTransparentPass}
   * @override
   */ isTransparentPass(_pass) {
        return false;
    }
    /**
   * {@inheritDoc Material.supportLighting}
   * @override
   */ supportLighting() {
        return true;
    }
    /**
   * {@inheritDoc Material.supportInstancing}
   * @override
   */ supportInstancing() {
        return false;
    }
    applyUniformValues(bindGroup, ctx, pass) {
        super.applyUniformValues(bindGroup, ctx, pass);
        bindGroup.setTexture('terrainNormalMap', this._terrainNormalMap);
        bindGroup.setValue('terrainSize', this._terrainSize);
        if (this.needFragmentColor(ctx)) {
            bindGroup.setValue('albedoTextureSize', this._textureSize);
        }
    }
    vertexShader(scope) {
        super.vertexShader(scope);
        const pb = scope.$builder;
        scope.$inputs.pos = pb.vec3().attrib('position');
        scope.$inputs.placement = pb.vec4().attrib('texCoord1');
        scope.terrainNormalMap = pb.tex2D().uniform(2);
        scope.terrainSize = pb.vec2().uniform(2);
        const normalSample = pb.textureSampleLevel(scope.terrainNormalMap, pb.div(scope.$inputs.placement.xz, scope.terrainSize), 0).rgb;
        scope.$l.normal = pb.normalize(pb.sub(pb.mul(normalSample, 2), pb.vec3(1)));
        scope.$l.axisX = pb.vec3(1, 0, 0);
        scope.$l.axisZ = pb.cross(scope.axisX, scope.normal);
        scope.$l.axisX = pb.cross(scope.normal, scope.axisZ);
        scope.$l.rotPos = pb.mul(pb.mat3(scope.axisX, scope.normal, scope.axisZ), scope.$inputs.pos);
        scope.$outputs.worldPos = pb.mul(ShaderHelper.getWorldMatrix(scope), pb.vec4(pb.add(scope.rotPos, scope.$inputs.placement.xyz), 1)).xyz;
        ShaderHelper.setClipSpacePosition(scope, pb.mul(ShaderHelper.getViewProjectionMatrix(scope), pb.vec4(scope.$outputs.worldPos, 1)));
        scope.$outputs.worldNorm = pb.mul(ShaderHelper.getNormalMatrix(scope), pb.vec4(scope.normal, 0)).xyz;
    }
    fragmentShader(scope) {
        super.fragmentShader(scope);
        const pb = scope.$builder;
        const that = this;
        if (this.needFragmentColor()) {
            scope.albedoTextureSize = pb.vec2().uniform(2);
            scope.$l.albedo = this.calculateAlbedoColor(scope);
            scope.albedo = that.calculateFoliageAlbedo(scope, scope.albedo, pb.mul(that.getAlbedoTexCoord(scope), scope.albedoTextureSize));
            scope.$l.litColor = pb.vec3(0);
            if (this.drawContext.renderPass.type === RENDER_PASS_TYPE_LIGHT) {
                scope.$l.normalInfo = this.calculateNormalAndTBN(scope, scope.$inputs.worldPos, scope.$inputs.worldNorm);
                scope.$l.viewVec = this.calculateViewVector(scope, scope.$inputs.worldPos);
                scope.$l.litColor = this.PBRLight(scope, scope.$inputs.worldPos, scope.normalInfo.normal, scope.viewVec, scope.albedo, scope.normalInfo.TBN);
            }
            this.outputFragmentColor(scope, scope.$inputs.worldPos, pb.vec4(scope.litColor, scope.albedo.a));
        } else {
            this.outputFragmentColor(scope, scope.$inputs.worldPos, null);
        }
    }
    apply(ctx) {
        this.alphaToCoverage = ctx.device.getFrameBufferSampleCount() > 1;
        this.alphaCutoff = this.alphaToCoverage ? 1 : 0.8;
        return super.apply(ctx);
    }
    updateRenderStates(pass, stateSet, ctx) {
        super.updateRenderStates(pass, stateSet, ctx);
        stateSet.useRasterizerState().setCullMode('none');
    }
}

export { GrassMaterial };
//# sourceMappingURL=grassmaterial.js.map
