import { Disposable, Vector2 } from '@zephyr3d/base';
import '@zephyr3d/device';
import { valueNoise } from '../shaders/noise.js';
import '../shaders/atmosphere.js';
import { ShaderHelper } from '../material/shader/helper.js';
import '../material/lambert.js';
import '../material/blinn.js';
import '../material/unlit.js';
import '../material/particle.js';
import '../values.js';
import '../material/meshmaterial.js';
import '../material/grassmaterial.js';
import '../material/terrain-cm.js';
import '../material/pbrmr.js';
import '../material/pbrsg.js';
import '../material/pbrblueprint.js';
import '../material/sprite.js';
import '../material/sprite_std.js';
import '../utility/blueprint/material/texture.js';

const MAX_NUM_OCTAVES = 16;
/**
 * FBM wave generator.
 * @public
 */ class FBMWaveGenerator extends Disposable {
    _version;
    _windVelocity;
    _numOctaves;
    _amplitude;
    _frequency;
    _lacunarity;
    _gain;
    /**
   * Creates a new Gerstner wave generator.
   */ constructor(){
        super();
        this._version = 0;
        this._numOctaves = 4;
        this._windVelocity = new Vector2(0.1, 0);
        this._amplitude = 0.3;
        this._frequency = 3;
        this._lacunarity = 1.83;
        this._gain = 0.57;
    }
    clone() {
        const other = new FBMWaveGenerator();
        other.numOctaves = this.numOctaves;
        other.wind = this.wind;
        other.amplitude = this.amplitude;
        other.frequency = this.frequency;
        return other;
    }
    get version() {
        return this._version;
    }
    /** Number of octaves */ get numOctaves() {
        return this._numOctaves;
    }
    set numOctaves(val) {
        if (val !== this.numOctaves) {
            this._numOctaves = val;
            this._version++;
        }
    }
    /** Wave amplitude */ get amplitude() {
        return this._amplitude;
    }
    set amplitude(val) {
        if (val !== this._amplitude) {
            this._amplitude = val;
            this._version++;
        }
    }
    /** Wave frequency */ get frequency() {
        return this._frequency;
    }
    set frequency(val) {
        if (val !== this._frequency) {
            this._frequency = val;
            this._version++;
        }
    }
    /** Wind velocity */ get wind() {
        return this._windVelocity;
    }
    set wind(val) {
        if (!val.equalsTo(this._windVelocity)) {
            this._windVelocity.set(val);
            this._version++;
        }
    }
    /** @internal */ calcWaveNormal(scope, worldPos, time, windVelocity, amplitude, frequency, gain, lacunarity, numOctaves) {
        const pb = scope.$builder;
        const funcName = 'calcWaveNormal';
        const that = this;
        pb.func(funcName, [
            pb.vec2('worldPos'),
            pb.float('time'),
            pb.vec2('windVelocity'),
            pb.float('amplitude'),
            pb.float('frequency'),
            pb.float('gain'),
            pb.float('lacunarity'),
            pb.int('numOctaves')
        ], function() {
            this.$l.epsilon = pb.float(0.1);
            this.$l.hr = that.calcWaveHeight(this, pb.add(this.worldPos, pb.vec2(this.epsilon, 0)), this.windVelocity, this.amplitude, this.frequency, this.gain, this.lacunarity, this.numOctaves);
            this.$l.hl = that.calcWaveHeight(this, pb.sub(this.worldPos, pb.vec2(this.epsilon, 0)), this.windVelocity, this.amplitude, this.frequency, this.gain, this.lacunarity, this.numOctaves);
            this.$l.hu = that.calcWaveHeight(this, pb.add(this.worldPos, pb.vec2(0, this.epsilon)), this.windVelocity, this.amplitude, this.frequency, this.gain, this.lacunarity, this.numOctaves);
            this.$l.hd = that.calcWaveHeight(this, pb.sub(this.worldPos, pb.vec2(0, this.epsilon)), this.windVelocity, this.amplitude, this.frequency, this.gain, this.lacunarity, this.numOctaves);
            this.$l.dHdx = pb.div(pb.sub(this.hr, this.hl), pb.mul(this.epsilon, 2));
            this.$l.dHdz = pb.div(pb.sub(this.hu, this.hd), pb.mul(this.epsilon, 2));
            this.$l.normal = pb.normalize(pb.vec3(pb.neg(this.dHdx), 1, pb.neg(this.dHdz)));
            this.$return(this.normal);
        });
        return scope[funcName](worldPos, time, windVelocity, amplitude, frequency, gain, lacunarity, numOctaves);
    }
    /** @internal */ calcWaveHeight(scope, worldPos, windVelocity, amplitude, frequency, gain, lacunarity, numOctaves) {
        const pb = scope.$builder;
        const funcName = 'calcWaveHeight';
        const that = this;
        pb.func(funcName, [
            pb.vec2('worldPos'),
            pb.vec2('windVelocity'),
            pb.float('amplitude'),
            pb.float('frequency'),
            pb.float('gain'),
            pb.float('lacunarity'),
            pb.int('numOctaves')
        ], function() {
            this.$l.windOffset = pb.mul(this.windVelocity, ShaderHelper.getElapsedTime(this));
            this.$l.height = that.fbm(this, pb.add(pb.mul(this.worldPos, 0.01), this.windOffset), this.frequency, this.gain, this.lacunarity, this.numOctaves);
            this.height = pb.add(this.height, pb.mul(0.3, that.fbm(this, pb.add(pb.mul(this.worldPos, 0.05), pb.mul(this.windOffset, 1.5)), this.frequency, this.gain, this.lacunarity, this.numOctaves)));
            this.height = pb.add(this.height, pb.mul(0.1, that.fbm(this, pb.add(pb.mul(this.worldPos, 0.1), pb.mul(this.windOffset, 2)), this.frequency, this.gain, this.lacunarity, this.numOctaves)));
            this.$return(pb.mul(this.height, this.amplitude));
        });
        return scope[funcName](worldPos, windVelocity, amplitude, frequency, gain, lacunarity, numOctaves);
    }
    /** @internal */ fbm(scope, st, frequency, gain, lacunarity, numOctaves) {
        const pb = scope.$builder;
        const funcName = 'FBM';
        pb.func(funcName, [
            pb.vec2('st'),
            pb.float('frequency'),
            pb.float('gain'),
            pb.float('lacunarity'),
            pb.int('numOctaves')
        ], function() {
            this.$l.m2 = pb.mat2(0.8, 0.6, -0.6, 0.8);
            this.$l.p = this.st; //pb.mul(this.st, 0.05);
            this.$l.a = pb.float(0);
            this.$l.b = pb.float(1);
            this.$l.c = pb.float(0);
            this.$for(pb.int('i'), 0, pb.getDevice().type === 'webgl' ? MAX_NUM_OCTAVES : this.numOctaves, function() {
                if (pb.getDevice().type === 'webgl') {
                    this.$if(pb.greaterThanEqual(this.i, this.numOctaves), function() {
                        this.$break();
                    });
                }
                this.$l.n = pb.sub(valueNoise(this, this.p), 0.5);
                this.a = pb.add(this.a, pb.mul(this.n, this.b));
                this.c = pb.add(this.c, this.b);
                this.b = pb.mul(this.b, this.gain);
                this.p = pb.mul(this.m2, this.p, this.frequency);
            });
            this.$return(pb.div(this.a, this.c));
        });
        return scope[funcName](st, frequency, gain, lacunarity, numOctaves);
    }
    /** {@inheritDoc WaveGenerator.update} */ update() {}
    /** {@inheritDoc WaveGenerator.needUpdate} */ needUpdate() {
        return false;
    }
    /** {@inheritDoc WaveGenerator.calcClipmapTileAABB} */ calcClipmapTileAABB(minX, maxX, minZ, maxZ, y, outAABB) {
        const maxHeight = 1.5 * this._amplitude;
        outAABB.minPoint.setXYZ(minX, y - maxHeight, minZ);
        outAABB.maxPoint.setXYZ(maxX, y + maxHeight, maxZ);
    }
    /** {@inheritDoc WaveGenerator.calcFragmentNormal} */ calcFragmentNormal(scope, xz) {
        const pb = scope.$builder;
        const that = this;
        pb.func('calcFragmentNormal', [
            pb.vec2('xz')
        ], function() {
            this.$l.normal = that.calcWaveNormal(this, this.xz, ShaderHelper.getElapsedTime(this), this.windVelocity, this.waveAmplitude, this.waveFrequency, this.waveGain, this.waveLacunarity, this.numOctaves);
            this.$return(this.normal);
        });
        return scope.calcFragmentNormal(xz);
    }
    /** {@inheritDoc WaveGenerator.calcFragmentNormalAndFoam} */ calcFragmentNormalAndFoam(scope, xz) {
        return scope.$builder.vec4(this.calcFragmentNormal(scope, xz), 0);
    }
    /** {@inheritDoc WaveGenerator.setupUniforms} */ setupUniforms(scope, uniformGroup) {
        const pb = scope.$builder;
        scope.numOctaves = pb.int().uniform(uniformGroup);
        scope.windVelocity = pb.vec2().uniform(uniformGroup);
        scope.waveAmplitude = pb.float().uniform(uniformGroup);
        scope.waveFrequency = pb.float().uniform(uniformGroup);
        scope.waveGain = pb.float().uniform(uniformGroup);
        scope.waveLacunarity = pb.float().uniform(uniformGroup);
    }
    /** @internal */ calcNormalAndPos(scope, inPos, outPos, outNormal) {
        const pb = scope.$builder;
        const that = this;
        pb.func('calcPositionAndNormal', [
            pb.vec3('inPos'),
            pb.vec3('outPos').out(),
            pb.vec3('outNormal').out()
        ], function() {
            this.outNormal = that.calcWaveNormal(this, this.inPos.xz, ShaderHelper.getElapsedTime(this), this.windVelocity, this.waveAmplitude, this.waveFrequency, this.waveGain, this.waveLacunarity, this.numOctaves);
            this.$l.h = that.calcWaveHeight(this, this.inPos.xz, this.windVelocity, this.waveAmplitude, this.waveFrequency, this.waveGain, this.waveLacunarity, this.numOctaves);
            this.outPos = pb.add(this.inPos, pb.vec3(0, this.h, 0));
        });
        scope.calcPositionAndNormal(inPos, outPos, outNormal);
    }
    /** {@inheritDoc WaveGenerator.calcVertexPositionAndNormal} */ calcVertexPositionAndNormal(scope, inPos, outPos, outNormal) {
        this.calcNormalAndPos(scope, inPos, outPos, outNormal);
    }
    /** {@inheritDoc WaveGenerator.applyWaterBindGroup} */ applyWaterBindGroup(bindGroup) {
        bindGroup.setValue('numOctaves', this._numOctaves);
        bindGroup.setValue('windVelocity', this._windVelocity);
        bindGroup.setValue('waveAmplitude', this._amplitude);
        bindGroup.setValue('waveFrequency', this._frequency);
        bindGroup.setValue('waveGain', this._gain);
        bindGroup.setValue('waveLacunarity', this._lacunarity);
    }
    /** {@inheritDoc WaveGenerator.isOk} */ isOk() {
        return true;
    }
    /** {@inheritDoc WaveGenerator.getHash} */ getHash() {
        return 'FBMWaveGenerator';
    }
}

export { FBMWaveGenerator };
//# sourceMappingURL=fbm_wavegenerator.js.map
