<!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.
    
	-->
	
		<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()
				{% load static %}
				loader.load( '{% static 'structure_viewer/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
    						{% load static %}
    						'{% static 'structure_viewer/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>