<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Simmate - Structure Viewer</title>
        <meta charset="utf-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    </head>
    <body>
        <!--
	This code is adapted from...
    	https://github.com/mrdoob/three.js/blob/dev/examples/webgl_loader_gltf.html
    
    The main difference right now is that I switched to CDN loading of resources
    and also load static files.
    
    I don't really want the HDR but I also don't understand this code well
    enough to change it.
    
    The controls are limited when rotating vertically, so I'll need to change
    from orbital to trackball controls:
    https://threejs.org/examples/?q=controls#misc_controls_trackball
    
    When rotating, I would like the light source to rotate with it. This will
    avoid unwanted shadows when viewing crystal structures from other angles.
    
-->
        {% load static %}
        {% get_static_prefix as STATIC_PREFIX %}
        <script type="module">
            // Find the latest version by visiting https://cdn.skypack.dev/three
            import * as THREE from 'https://cdn.skypack.dev/three@0.132.2';

            import {
                OrbitControls
            } from 'https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/OrbitControls.js';
            // I want trackball, but don't know how to implement it
            // import { TrackballControls } from 'https://cdn.skypack.dev/three@0.132.2/examples/jsm/controls/TrackballControls.js';
            import {
                GLTFLoader
            } from 'https://cdn.skypack.dev/three@0.132.2/examples/jsm/loaders/GLTFLoader.js';
            import {
                RGBELoader
            } from 'https://cdn.skypack.dev/three@0.132.2/examples/jsm/loaders/RGBELoader.js';
            import {
                RoughnessMipmapper
            } from 'https://cdn.skypack.dev/three@0.132.2/examples/jsm/utils/RoughnessMipmapper.js';

            let camera, scene, renderer, light;

            init();
            animate();

            function init() {

                // first establish our scene which we will add everything else to
                scene = new THREE.Scene();


                // make camera
                camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.25, 2000);
                camera.position.set(-18, 6, 27);


                // Now load the background image and the gltf file
                const loader = new RGBELoader()
                loader.load('{% static '
                    core_components / royal_esplanade_1k.hdr '%}',
                    function(texture) {

                        texture.mapping = THREE.EquirectangularReflectionMapping;


                        scene.background = new THREE.Color('rgb(214, 241, 241)');
                        scene.environment = texture;

                        render();

                        // model

                        // use of RoughnessMipmapper is optional
                        const roughnessMipmapper = new RoughnessMipmapper(renderer);

                        const loader = new GLTFLoader();
                        loader.load(
                            // path to the file we're loading
                            '{% static 3d_structure_filename %}',
                            // ALTERNATIVE FILE FOR TESTING: 
                            // '{% static 'core_components/simmate_structure.glb'%}',

                            // this function is called when the resource is loaded
                            function(gltf) {

                                // I'm not entirely sure what is happening here,
                                // but I believe we are just updating the object's
                                // material to a RoughnessMipmapper
                                gltf.scene.traverse(
                                    function(child) {
                                        if (child.isMesh) {
                                            roughnessMipmapper.generateMipmaps(child.material);
                                        }
                                    });

                                scene.add(gltf.scene);
                                roughnessMipmapper.dispose();
                                render();
                            },

                            // this function is called while loading is progressing
                            function(xhr) {
                                // console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
                            },

                            // this function is called when loading has errors
                            function(error) {
                                console.log('An error happened');
                            },
                        );

                    });

                // BUG: this isn't working and I don't know why...
                // Add a point light to the scene
                // const light = new THREE.PointLight();
                // light.intensity = 100;
                // light.position.set( 0, 0, 0 );
                // scene.add( light );
                // And have that light follow the camera with rotation
                // camera.add( light );

                // This builds the viewport and adds it to the html body
                renderer = new THREE.WebGLRenderer({
                    antialias: true
                });
                renderer.setPixelRatio(window.devicePixelRatio);
                renderer.setSize(window.innerWidth, window.innerHeight);
                renderer.toneMapping = THREE.ACESFilmicToneMapping;
                renderer.toneMappingExposure = 1;
                renderer.outputEncoding = THREE.sRGBEncoding;
                document.body.appendChild(renderer.domElement);


                // Allow the user to control the viewport and rotate about the object
                const controls = new OrbitControls(camera, renderer.domElement);
                // controls.addEventListener( 'change', render ); // use this line if there is no animation loop
                controls.zoomSpeed = 2;
                //controls.minDistance = 2;
                //controls.maxDistance = 15;
                controls.update();

                // Calls our function below every time the window size chages
                window.addEventListener('resize', onWindowResize);

            }


            // updates render window and objects in it when the window changes size
            function onWindowResize() {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
                render();
            }


            // How to render a single frame in the viewport. Note that this is
            // called repeatedly by animate().
            function render() {
                // if you want to calculate something every loop, call it here
                renderer.render(scene, camera);
            }


            // Calling this means this will loop repeatedly, so if you want to
            // call a function over and over again, put it here!
            function animate() {
                requestAnimationFrame(animate);
                render();
            }
        </script>
    </body>
</html>
