<template>
    <div class="c-shader" ref="container"></div>
</template>

<script>
import gsap from "gsap";
import * as THREE from "three";
import * as Stats from "stats.js";

import colorVertex from "@/assets/shader/color_vertex.glsl";
import colorFrag from "@/assets/shader/color_fragment.glsl";
import { GUI } from "three/examples/jsm/libs/dat.gui.module.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";

export default {
    name: "color",
    props: {
        msg: String,
    },
    data() {
        return {
            container: null,
            camera: null,
            scene: null,
            renderer: null,
            mouse: new THREE.Vector2(),
            raycaster: new THREE.Raycaster(),
            gui: new GUI(),

            ENTIRE_SCENE: 0,
            BLOOM_SCENE: 1,
            materials: {},
            params: {
                exposure: 1,
                bloomStrength: 5,
                bloomThreshold: 0,
                bloomRadius: 0,
                scene: "Scene with Glow",
            },
        };
    },
    created() {},
    mounted() {
        this.initStage();
        this.addAnimation();
    },
    methods: {
        initStage() {
            //基础参数
            this.container = this.$refs.container;
            this.camera = new THREE.Camera();
            this.camera.position.z = 1;
            this.scene = new THREE.Scene();

            this.bloomLayer = new THREE.Layers();
            this.bloomLayer.set(this.BLOOM_SCENE);

            this.darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });

            // 定义 webglrender
            this.renderer = new THREE.WebGLRenderer({ antialias: true });
            this.renderer.setPixelRatio(window.devicePixelRatio);
            this.renderer.setSize(window.innerWidth, window.innerHeight);
            this.renderer.toneMapping = THREE.ReinhardToneMapping;
            this.container.appendChild(this.renderer.domElement);

            // 定义相机
            this.camera = new THREE.PerspectiveCamera(
                40,
                window.innerWidth / window.innerHeight,
                1,
                200
            );
            this.camera.position.set(0, 0, 30);
            this.camera.lookAt(0, 0, 0);

            // 定义操控
            this.controls = new OrbitControls(
                this.camera,
                this.renderer.domElement
            );
            this.controls.maxPolarAngle = Math.PI * 0.5;
            this.controls.minDistance = 1;
            this.controls.maxDistance = 200;
            this.controls.addEventListener("change", this.render);

            // 添加全局环境光
            this.scene.add(new THREE.AmbientLight(0x404040));

            this.renderScene = new RenderPass(this.scene, this.camera);

            this.bloomPass = new UnrealBloomPass(
                new THREE.Vector2(window.innerWidth, window.innerHeight),
                1.5,
                0.4,
                0.85
            );
            this.bloomPass.threshold = this.params.bloomThreshold;
            this.bloomPass.strength = this.params.bloomStrength;
            this.bloomPass.radius = this.params.bloomRadius;

            this.bloomComposer = new EffectComposer(this.renderer);
            this.bloomComposer.renderToScreen = false;
            this.bloomComposer.addPass(this.renderScene);
            this.bloomComposer.addPass(this.bloomPass);

            this.finalPass = new ShaderPass(
                new THREE.ShaderMaterial({
                    uniforms: {
                        baseTexture: { value: null },
                        bloomTexture: {
                            value: this.bloomComposer.renderTarget2.texture,
                        },
                    },
                    vertexShader: colorVertex,
                    fragmentShader: colorFrag,
                    defines: {},
                }),
                "baseTexture"
            );
            this.finalPass.needsSwap = true;

            this.finalComposer = new EffectComposer(this.renderer);
            this.finalComposer.addPass(this.renderScene);
            this.finalComposer.addPass(this.finalPass);

            // 添加点击事件
            window.addEventListener("pointerdown", this.onPointerDown);

            this.gui
                .add(this.params, "scene", [
                    "Scene with Glow",
                    "Glow only",
                    "Scene only",
                ])
                .onChange((value) => {
                    switch (value) {
                        case "Scene with Glow":
                            this.bloomComposer.renderToScreen = false;
                            break;
                        case "Glow only":
                            this.bloomComposer.renderToScreen = true;
                            break;
                        case "Scene only":
                            // nothing to do
                            break;
                    }

                    this.render();
                });

            this.folder = this.gui.addFolder("Bloom Parameters");

            this.folder
                .add(this.params, "exposure", 0.1, 2)
                .onChange((value) => {
                    this.renderer.toneMappingExposure = Math.pow(value, 4.0);
                    this.render();
                });

            this.folder
                .add(this.params, "bloomThreshold", 0.0, 1.0)
                .onChange((value) => {
                    this.bloomPass.threshold = Number(value);
                    this.render();
                });

            this.folder
                .add(this.params, "bloomStrength", 0.0, 10.0)
                .onChange((value) => {
                    this.bloomPass.strength = Number(value);
                    this.render();
                });

            this.folder
                .add(this.params, "bloomRadius", 0.0, 1.0)
                .step(0.01)
                .onChange((value) => {
                    this.bloomPass.radius = Number(value);
                    this.render();
                });

            // 设置场景
            this.setupScene();
        },
        onPointerDown(event) {
            this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

            this.raycaster.setFromCamera(this.mouse, this.camera);
            this.intersects = this.raycaster.intersectObjects(
                this.group.children,
                false
            );
            if (this.intersects.length > 0) {
                const object = this.intersects[0].object;
                gsap.to(object.scale, {
                    x: 0,
                    y: 0,
                    z: 0,
                    onUpdate: () => {
                        this.render();
                    },
                });
                object.layers.toggle(this.BLOOM_SCENE);
                this.render();
            }
        },
        onWindowResize(event) {
            const width = window.innerWidth;
            const height = window.innerHeight;

            this.camera.aspect = width / height;
            this.camera.updateProjectionMatrix();

            this.renderer.setSize(width, height);

            this.bloomComposer.setSize(width, height);
            this.finalComposer.setSize(width, height);

            this.render();
        },
        setupScene() {
            this.scene.traverse(this.disposeMaterial);
            this.scene.children.length = 0;

            const geometry = new THREE.IcosahedronGeometry(1, 15);
            this.group = new THREE.Group();
            for (let i = 0; i < 50; i++) {
                const color = new THREE.Color();
                color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.05);

                const material = new THREE.MeshBasicMaterial({ color: color });
                const sphere = new THREE.Mesh(geometry, material);
                sphere.position.x = Math.random() * 30 - 15;
                sphere.position.y = Math.random() * 30 - 15;
                sphere.position.z = Math.random() * 30 - 15;
                sphere.position
                    .normalize()
                    .multiplyScalar(Math.random() * 5.0 + 2.5);
                sphere.scale.setScalar(Math.random() * Math.random() + 0.5);
                this.scene.add(sphere);

                // if (Math.random() < 0.25)
                sphere.layers.enable(this.BLOOM_SCENE);

                this.group.add(sphere);
            }
            this.scene.add(this.group);
            this.render();
        },
        disposeMaterial(obj) {
            if (obj.material) {
                obj.material.dispose();
            }
        },
        animate() {
            // requestAnimationFrame(this.animate);
            // this.render();
        },
        render() {
            switch (this.params.scene) {
                case "Scene only":
                    this.renderer.render(this.scene, this.camera);
                    break;
                case "Glow only":
                    this.renderBloom(false);
                    break;
                case "Scene with Glow":
                default:
                    // render scene with bloom
                    this.renderBloom(true);

                    // render the entire scene, then render bloom scene on top
                    this.finalComposer.render();
                    break;
            }
            // this.renderer.render(this.scene, this.camera);
        },
        renderBloom(mask) {
            if (mask === true) {
                this.scene.traverse(this.darkenNonBloomed);
                this.bloomComposer.render();
                this.scene.traverse(this.restoreMaterial);
            } else {
                this.camera.layers.set(this.BLOOM_SCENE);
                this.bloomComposer.render();
                this.camera.layers.set(this.ENTIRE_SCENE);
            }
        },
        darkenNonBloomed(obj) {
            if (obj.isMesh && this.bloomLayer.test(obj.layers) === false) {
                this.materials[obj.uuid] = obj.material;
                obj.material = this.darkMaterial;
            }
        },
        restoreMaterial(obj) {
            if (this.materials[obj.uuid]) {
                obj.material = this.materials[obj.uuid];
                delete this.materials[obj.uuid];
            }
        },
        addAnimation() {
            // 自旋转
            gsap.to(this.group.rotation, {
                y: -Math.PI * 2,
                duration: 2,
                // yoyo: true,
                // repeat: -1,
                onUpdate: () => {
                    // this.renderer.toneMappingExposure = Math.pow(
                    //     this.params.exposure,
                    //     4.0
                    // );
                    this.render();
                    // console.log(this.params.scene);
                },
            });
            // 光闪烁
            // gsap.to(this.params, {
            //     exposure: 1.1,
            //     duration: 2,
            //     yoyo: true,
            //     repeat: -1,
            //     onUpdate: () => {
            //         this.renderer.toneMappingExposure = Math.pow(
            //             this.params.exposure,
            //             4.0
            //         );
            //         this.render();
            //         // console.log(this.params.scene);
            //     },
            // });
        },
    },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.c-shader {
    width: 100vw;
    height: 100vh;
    position: relative;
    z-index: 0;
    .select-btn {
        // background-color: #fff;
        .paCenterBottom(2%,50%,auto,10);
    }
}
</style>
