<template>
    <div>
        <div id="containerThreeVehicleModel"></div>
    </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
// import {
//     showFullScreenLoading,
//     tryHideFullScreenLoading,
// } from "../../../axios/filter";
import { mapState } from "vuex";

export default {
    name: "threeVehicleModel", // 动力学配置中的threeJS

    data() {
        return {
            publicPath: process.env.BASE_URL,
            scene: null,
            camera: null,
            renderer: null,
            light: null,
            geometryName: null,
            controls: null,
            mesh: null,
            cube: null,
            container: null,
            car: null,
            cubeTexture: null,
            raf: null,
            // scale: 2, // 物体加载换算倍数
            isLoading: false, // 判断是否正在记载车辆
            lastCarModel: "", // 记录上一次加载的carModel
        };
    },

    computed: {
        ...mapState(["scale", "scaleRate"]),
    },

    props: {
        carModel: {
            type: String,
            default: "",
        },
    },

    watch: {
        carModel(newVal, oldVal) {
            if (newVal != oldVal && !this.isLoading) {
                this.lastCarModel = newVal;
                this.initCar(newVal);
            }
        },
    },

    methods: {
        // 场景
        initScene() {
            this.scene = new THREE.Scene();
            const gridHelper = new THREE.GridHelper(1000, 100);
            gridHelper.material.opacity = 0.25;
            gridHelper.material.transparent = true;
            this.scene.add(gridHelper);
        },
        // 相机
        initCamera() {
            this.camera = new THREE.PerspectiveCamera(
                75,
                this.container.clientWidth / this.container.clientHeight,
                0.1,
                1000
            );
            this.camera.position.set(200, 200, 200);
            this.scene.add(this.camera);
        },
        // 渲染器
        initRenderer() {
            this.renderer = new THREE.WebGLRenderer({
                antialias: true,
                alpha: true,
            });
            this.renderer.setSize(
                this.container.clientWidth,
                this.container.clientHeight
            );
            this.renderer.setClearColor("#272727");
            this.container.appendChild(this.renderer.domElement);
        },
        // 初始化灯光
        initLight() {
            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
            hemiLight.position.set(0, 20, 0);
            this.scene.add(hemiLight);

            // 环境光会均匀的照亮场景中的所有物体
            const light = new THREE.AmbientLight(0x5c5c5c, 0.4); // soft white light
            this.scene.add(light);

            // 平行光是沿着特定方向发射的光
            const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
            this.scene.add(dirLight);
        },
        // 初始化车模型
        initCar(model) {
            if (this.car) {
                this.scene.remove(this.car);
                this.removeObj(this.car);
                this.car = null;
            }

            if (!model) return;
            if (!model.includes(".glb")) return;

            let that = this;
            let loader = new GLTFLoader();

            const dracoLoader = new DRACOLoader(); //
            dracoLoader.setDecoderPath(`${this.publicPath}draco/`); // 设置public下的解码路径，注意最后面的/
            dracoLoader.setDecoderConfig({ type: "js" }); // 使用兼容性强的draco_decoder.js解码器
            dracoLoader.preload(); // 初始化_initDecoder 解码器
            loader.setDRACOLoader(dracoLoader); // gltfloader使用dracoLoader

            // showFullScreenLoading();

            this.$emit('loadingDone', 'carStart');
            this.isLoading = true;

            loader.load(
                model,
                function (obj) {
                    // tryHideFullScreenLoading();

                    obj.scene.rotation.set(
                        (-90 * Math.PI) / 180,
                        0,
                        (-180 * Math.PI) / 180
                    );

                    let s = 0;
                    if (model.includes("928.glb")) {
                        s = that.scale / that.scaleRate;
                    } else {
                        s = that.scale;
                    }

                    let scale = 30 * s;
                    obj.scene.scale.set(scale, scale, scale);
                    that.scene.add(obj.scene);
                    that.car = obj.scene;

                    that.$emit('loadingDone', 'carDone');
                    that.isLoading = false;
                    that.needCar();
                },
                (xhr) => {
                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
                },
                (error) => {
                    // tryHideFullScreenLoading();
                    that.$emit('loadingDone', 'carDone');
                    that.isLoading = false;
                    that.needCar();
                    console.error(error);
                }
            );
        },
        // 判断是否继续加载car，为了应对上一个car正在加载中的时候，用户选择了新的car
        needCar() {
            if (this.carModel != this.lastCarModel) {
                this.lastCarModel = this.carModel;
                this.initCar(this.carModel);
            }
        },
        // 初始化
        init() {
            this.initScene();
            this.initCamera();
            this.initRenderer();
            this.initLight();

            this.controls = new OrbitControls(
                this.camera,
                this.renderer.domElement
            ); //创建控件对象
            this.controls.minDistance = 30;
            this.controls.maxDistance = 600;
            this.controls.update();
        },
        animate() {
            this.raf = requestAnimationFrame(this.animate);
            this.renderer.render(this.scene, this.camera);

            this.controls.update();
        },
        onWindowResize() {
            this.camera.aspect =
                this.container.clientWidth / this.container.clientHeight;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(
                this.container.clientWidth,
                this.container.clientHeight
            );
        },
        go() {
            this.container = document.getElementById(
                "containerThreeVehicleModel"
            );
            this.init();
            this.animate();

            window.addEventListener("resize", this.onWindowResize);
        },
        removeScene() {
            this.clearScene();
        },
        clearCache(item) {
            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
            if (item.material && item.material.dispose) item.material.dispose();
        },
        clearScene() {
            this.removeObj(this.scene);
        },
        removeObj(obj) {
            let arr = obj.children.filter((x) => x);
            arr.forEach((item) => {
                if (item.children.length) {
                    this.removeObj(item);
                } else {
                    this.clearCache(item);
                    item.clear();
                }
            }),
                obj.clear();
            arr = null;
        },
    },

    mounted() {
        this.go();
    },
    destroyed() {
        window.removeEventListener("resize", this.onWindowResize);
        cancelAnimationFrame(this.raf);

        if (this.renderer) {
            this.renderer.renderLists.dispose();
            this.renderer.dispose();
            this.renderer.forceContextLoss();
            this.renderer.domElement = null;
            this.renderer.content = null;
            this.renderer = null;
        }

        if (this.controls) {
            this.controls.dispose();
            this.controls = null;
        }

        this.clearScene();

        this.scene = null;
        this.camera = null;
        this.light = null;
        this.geometryName = null;
        this.mesh = null;
        this.cube = null;
        this.container = null;
        this.car = null;
        this.cubeTexture = null;
        this.raf = null;

        THREE.Cache.clear();
    },
};
</script>

<style lang="less" scoped>
#containerThreeVehicleModel {
    width: 100%;
    height: calc(100vh - 180px);
}
</style>
