const scene = new THREE.Scene();
scene.background = new THREE.Color(
// 0xcccccc
'white');
const clock = new THREE.Clock();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(0, 30, 50);
camera.lookAt(0, 3, 0);
const controls = new THREE.OrbitControls(camera);
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const stats = new Stats();
document.body.appendChild(stats.dom);
/// Anim mixer
const mixers = [];
class Assets {
    static loadEggMtl() {
        return new Promise((resolve, reject) => {
            const loader = new THREE.MTLLoader();
            loader.load(`models/Egg_from_Poly_uovo/Egg from Poly uovo.mtl`, (materials) => {
                materials.preload();
                resolve(materials);
            }, (xhr) => { }, reject);
        });
    }
    static loadEggObj(materials) {
        return new Promise((resolve, reject) => {
            const loader = new THREE.OBJLoader();
            loader.setMaterials(materials);
            loader.load(`models/Egg_from_Poly_uovo/Egg from Poly uovo.obj`, (object) => {
                resolve(object);
            }, (xhr) => {
                // c.log(`${ xhr.loaded / xhr.total * 100 }% loaded`);
            }, (error) => {
                c.error(error);
                reject(error);
            });
        });
    }
    static async loadEgg() {
        const materialCreator = await this.loadEggMtl();
        return this.loadEggObj(materialCreator);
    }
    static loadEggGltf() {
        return new Promise((resolve, reject) => {
            const loader = new THREE.GLTFLoader();
            loader.load(`models/Egg_gltf/Egg from Poly uovo copy.gltf`, (gltf) => {
                c.log(gltf);
                resolve(gltf.scene);
            });
        });
    }
    static loadDogDae() {
        /// In Dae/Collada: did not manage to get 
        /// either the anims or the texture.
        return new Promise((resolve, reject) => {
            const loader = new THREE.ColladaLoader();
            loader.load(`models/dog/pup_lohound.dae`, (collada) => {
                resolve(collada);
            });
        });
    }
    static loadDogFbx() {
        return new Promise((resolve, reject) => {
            const loader = new THREE.FBXLoader();
            loader.load(`models/dog_fbx/puppy-snapchat.fbx`, (fbx) => {
                resolve(fbx);
            });
        });
    }
    static loadBoloss() {
        /// In Dae/Collada: did not manage to get 
        /// either the anims or the texture.
        return new Promise((resolve, reject) => {
            const loader = new THREE.ColladaLoader();
            loader.load(`models/boloss/Boloss-3d v10.dae`, (collada) => {
                resolve(collada);
            });
        });
    }
}
class TUtils {
    static boundingBox(o) {
        const bbox = new THREE.Box3().setFromObject(o);
        return bbox;
    }
    static flushYZero(o) {
        o.position.y = -(this.boundingBox(o)).min.y;
    }
    static perform(tween) {
        return new Promise(resolve => {
            tween.onComplete(resolve).start();
        });
    }
}
(async () => {
    /**
     * scene construction
     */
    const gridHelper = new THREE.GridHelper(100, 100);
    scene.add(gridHelper);
    const axesHelper = new THREE.AxesHelper(50);
    scene.add(axesHelper);
    {
        const egg = await Assets.loadEgg();
        c.log(egg);
        egg.scale.setScalar(.2);
        egg.rotateX(-Math.PI / 2);
        egg.position.x = -18;
        TUtils.flushYZero(egg);
        const box = new THREE.BoxHelper(egg);
        scene.add(box);
        scene.add(egg);
        ///// Manually set the material, for fun.
        // const eggFace = egg.getObjectByName("CallKit-IconMask") as THREE.Mesh;
        // c.log(eggFace.material);
        // (<THREE.MeshPhongMaterial>(eggFace.material)).color.set(0x000000);
    }
    {
        const egg = await Assets.loadEggGltf();
        c.log(egg);
        egg.scale.setScalar(100);
        egg.position.x = -28;
        TUtils.flushYZero(egg);
        egg.remove(egg.getObjectByName('Camera'));
        scene.add(egg);
        // c.log(Utils.boundingBox(egg));
        const box = new THREE.BoxHelper(egg, new THREE.Color('red'));
        scene.add(box);
    }
    {
        ////// dog_fbx
        const dog = await Assets.loadDogFbx();
        // c.log((<any>dog).animations);
        const mixer = new THREE.AnimationMixer(dog);
        const clip = dog.animations.find(clip => clip.name === "lohound|lohoundAction");
        /// ^^ this is the main parent animation! Do not play all children.
        c.log(clip);
        mixer.clipAction(clip).play();
        mixers.push(mixer);
        const container = new THREE.Group();
        container.add(dog);
        container.scale.setScalar(0.007); /// <- scale a container, not the dog itself or it'll fuck the anims.
        container.position.x = -6;
        scene.add(container);
        const box = new THREE.BoxHelper(container, new THREE.Color('green'));
        scene.add(box);
    }
    {
        const boloss = (await Assets.loadBoloss()).scene;
        c.log(boloss);
        boloss.position.x = 16;
        TUtils.flushYZero(boloss);
        scene.add(boloss);
        const box = new THREE.BoxHelper(boloss, new THREE.Color('blue'));
        scene.add(box);
        /// Anims like in AudioBoloss
        const rootModel = boloss.getObjectByName(`SketchUp`);
        const pupilL = boloss.getObjectByName(`Pupil-left`);
        const pupilR = boloss.getObjectByName(`Pupil-right`);
        const pupils = new THREE.Group();
        pupils.add(pupilL, pupilR);
        rootModel.add(pupils);
        (async () => {
            while (true) {
                const translatePupil = new TWEEN.Tween(pupils.position)
                    .to({ x: "-1", y: "-1" }, 200)
                    .easing(TWEEN.Easing.Quadratic.Out);
                const translatePupilRev = new TWEEN.Tween(pupils.position)
                    .to({ x: "+1", y: "+1" }, 200)
                    .easing(TWEEN.Easing.Quadratic.Out);
                await TUtils.perform(translatePupil);
                await Utils.wait(4, 1);
                await TUtils.perform(translatePupilRev);
                await Utils.wait(8, 3);
            }
        })();
        const eyebrowL = boloss.getObjectByName(`Eyebrow-left`);
        const eyebrowR = boloss.getObjectByName(`Eyebrow-right`);
        const eyebrows = new THREE.Group();
        eyebrows.add(eyebrowL, eyebrowR);
        rootModel.add(eyebrows);
        (async () => {
            while (true) {
                const scaleEyebrow = new TWEEN.Tween(eyebrows.scale)
                    .to({ x: 1.08, y: 1.08, z: 1.08 }, 100)
                    .easing(TWEEN.Easing.Quadratic.InOut);
                const scaleEyebrowRev = new TWEEN.Tween(eyebrows.scale)
                    .to({ x: 1, y: 1, z: 1 }, 100)
                    .easing(TWEEN.Easing.Quadratic.InOut);
                await Utils.wait(6, 6);
                await TUtils.perform(scaleEyebrow);
                await TUtils.perform(scaleEyebrowRev);
                await Utils.wait(0.14);
                await TUtils.perform(scaleEyebrow);
                await TUtils.perform(scaleEyebrowRev);
            }
        })();
        (async () => {
            while (true) {
                const angle = Utils.randomFloat(-0.2, 0.3);
                const dummyL = new THREE.Object3D();
                dummyL.rotateOnAxis(new THREE.Vector3(0, 1, 0.8), angle);
                const dummyR = new THREE.Object3D();
                dummyR.rotateOnAxis(new THREE.Vector3(0, -1, -0.8), angle);
                /// ^^ exact same result as keeping the same vector and negating the angle.
                const rotateBrowL = new TWEEN.Tween(eyebrowL.rotation)
                    .to({
                    x: dummyL.rotation.x,
                    y: dummyL.rotation.y,
                    z: dummyL.rotation.z,
                }, 300);
                const rotateBrowR = new TWEEN.Tween(eyebrowR.rotation)
                    .to({
                    x: dummyR.rotation.x,
                    y: dummyR.rotation.y,
                    z: dummyR.rotation.z,
                }, 300);
                await Promise.all([
                    TUtils.perform(rotateBrowL),
                    TUtils.perform(rotateBrowR),
                ]);
                await Utils.wait(1, 1);
                await Promise.all([
                    TUtils.perform(new TWEEN.Tween(eyebrowL.rotation).to({ x: 0, y: 0, z: 0 }, 300)),
                    TUtils.perform(new TWEEN.Tween(eyebrowR.rotation).to({ x: 0, y: 0, z: 0 }, 300)),
                ]);
                await Utils.wait(1, 1);
                /// ^^ not the exact same behavior as in AudioBoloss (all waits are actually randoms there.)
            }
        })();
    }
})();
/**
 * MAIN()
 */
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}
function render() {
    const delta = clock.getDelta();
    for (const mixer of mixers) {
        mixer.update(delta);
    }
    renderer.render(scene, camera);
}
function animate() {
    requestAnimationFrame(animate);
    TWEEN.update();
    render();
    stats.update();
}
animate();