Three.jsでドラッグアンドドロップでテクスチャーを貼る

# Three.jsの投稿があったので自分も
画像ファイルJPEGやPNGとかをドロップするとトンネルの中にテクスチャが貼られます。

# タイトルまんまのソース

``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" /><title>トーラスのなかを進む テクスチャ版 - js do it</title>
<meta name="Description" content="jsdo.it - share JavaScript, HTML5 and CSS - " />
<meta name="Keywords"  content="JavaScript,HTML5,CSS" />
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">

<style type="text/css">body { background-color: #DDDDDD; font: 30px sans-serif; }</style></head>
<body>
<div id="dropImg">ここに画像をドロップ</div>
<script type="text/javascript" src="/lib/Three.js-r42/js"></script>

<script type="text/javascript">
window.onload=function(){

var renderer = new THREE.WebGLRenderer();
renderer.setSize( 450, 300 );
document.body.appendChild( renderer.domElement );

var scene = new THREE.Scene();

var camera = new THREE.Camera(
                75,     // Field of view
                450 / 300,  // Aspect ratio
                0.02,   // Near
                1000    // Far
            );
camera.position.set( 0, 5, 5.5 );
scene.addLight( new THREE.AmbientLight( 0x101010 ) );

var directionalLight = new THREE.DirectionalLight(  0xffffff );
    			directionalLight.position.x = 0;
                directionalLight.position.y = 0;
				directionalLight.position.z = 1;
				directionalLight.position.normalize();
//scene.addLight( directionalLight );
var directionalLight2 = new THREE.DirectionalLight(  0x00ff00 );
        		directionalLight2.position.x = 1;
                directionalLight2.position.y = 0;
				directionalLight2.position.z = 0;
				directionalLight2.position.normalize();
//scene.addLight( directionalLight2 );
var directionalLight3 = new THREE.DirectionalLight(  0x0000ff );
         	directionalLight3.position.x = 0;
                directionalLight3.position.y = 1;
				directionalLight3.position.z = 0;
				directionalLight3.position.normalize();
			//	scene.addLight( directionalLight3 );

var ptlight = new THREE.PointLight( 0xFFFFFF );
ptlight.position.set( 0, 100, 0 );
scene.addLight( ptlight);

material=new THREE.MeshLambertMaterial( { color: 0xaaaaaa, shading: THREE.FlatShading } );
//material=new THREE.MeshBasicMaterial( { color:0x0afaaf, vertexColors:false, shading: THREE.FlatShading } );

object = new THREE.Mesh( MakeTorus(), material);
object.overdraw=true;
object.doubleSided = true;
object.flipSided=true;
//object.matrixAutoUpdate=false;
scene.addChild(object);

if ( !window.requestAnimationFrame ) {

    window.requestAnimationFrame = ( function() {

		return window.webkitRequestAnimationFrame ||
		window.mozRequestAnimationFrame ||
		window.oRequestAnimationFrame ||
		window.msRequestAnimationFrame ||
		function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {

			window.setTimeout( callback, 1000 / 60 );

		};

	} )();

}
animate();

function Cross( v1, v2 )
	{
        var vx = v1.y * v2.z - v1.z * v2.y;
        var vy = v1.z * v2.x - v1.x * v2.z;
        var vz = v1.x * v2.y - v1.y * v2.x;
        
	    return new THREE.Vector3(vx,vy,vz);
}

// calculate Roman surface by u,v parameter.
function TorusSurface(u,v) {
    var a=1/3;
    var c = 1;
     var x = Math.cos(u)*(Math.cos(v)*a+c);
     var y = Math.sin(v)*a;
     var z = Math.sin(u)*(Math.cos(v)*a+c);
     //document.write(x +"," + y + ","+z +"\n");
     return new THREE.Vector3(x, y, z);
}

function animate() {
    requestAnimationFrame( animate );
    render();
}

function render() {
    var a=1/2;
    var c = 1;
    var time = new Date().getTime() * 0.001;
    
    camera.position.set( 1.1*Math.cos(time), 0.34*Math.sin(time/10),  1.1*Math.sin(time) );
    camera.target.position.set( 1.0*Math.cos(time+0.5), 0,  1.0*Math.sin(time+0.5) );
    //camera.target.position.set(0, 0.1, 0);
    ptlight.position.set(1.0*Math.cos(time+0.5), 0,  1.0*Math.sin(time+0.5) );
    renderer.render( scene, camera );
}
var dropArea = document.getElementById('dropImg');
var image = new Image();

// テクスチャ画像がドロップされた場合の処理
image.onload = function() {
    console.log('image.onload start');
    var canvas = document.createElement("canvas");
    canvas.width=image.width;
    canvas.height=image.height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(image,0,0,image.width,image.height,0,0,image.width,image.height);
    
    var texture = new THREE.Texture( canvas, new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping,
				   THREE.NearestFilter,THREE.NearestMipMapLinearFilter);
    texture.needsUpdate = true;
    var material1 = [
	new THREE.MeshLambertMaterial( { map: texture } ),
	new THREE.MeshBasicMaterial( { color: 0xffffff, wireframe: false, opacity: 0.1 } )
    ];
    scene.removeObject(object);

    object = new THREE.Mesh( MakeTorus(), material1 );
    object.overdraw=true;
    object.doubleSided = true;
    object.flipSided=true;

    scene.addObject(object);
 
};

//dropArea.addEventListener('dragenter', function(e) {
//  e.stopPropagation();
//}, false);

dropArea.addEventListener('dragleave', function(e) {
  e.preventDefault();
}, false);

dropArea.addEventListener('drop', function(e) {
    	console.log('Drop start');
e.preventDefault();    
	var file = e.dataTransfer.files[0];
	if( !file.type.match(/image/)) {
	    console.log("oops!");
		e.stopPropagation();
		return false;
	}
  
	var reader = new FileReader();
	reader.onload = function(e) {
		console.innerText = 'Reader onload start';	
		var fileContent = reader.result;
		image.src = fileContent;
   		console.log('Reader onload end');
	};

	reader.readAsDataURL(file);
	//e.stopPropagation();
      	console.log('Drop end');
}, false);

document.body.addEventListener('dragover', function(e) {
  e.preventDefault();
}, false);

function MakeTorus() {
    var geom = new THREE.Geometry();
    var ustep = 0.1;
    var vstep = 0.1;
    
    var pCount = 0;
    var geom = new THREE.Geometry();
    var vv,uu;
    
    for(var v =0; v < Math.PI*2; v+=vstep) {
	for(var u =0; u < 2*Math.PI; u+=ustep) {
	    v1 = TorusSurface(u, v);
	    v2 = TorusSurface(u+ustep, v);
	    v3 = TorusSurface(u+ustep, v+vstep);
	    v4 = TorusSurface(u, v+vstep);
	    
	    geom.vertices.push(new THREE.Vertex(v1));
	    geom.vertices.push(new THREE.Vertex(v2));
	    geom.vertices.push(new THREE.Vertex(v3));
	    geom.vertices.push(new THREE.Vertex(v4));
	    
	    var nvx1 = v1.x - v2.x;
	    var nvy1 = v1.y - v2.y;
	    var nvz1 = v1.z - v2.z;
	    
	    var nvx2 = v4.x - v2.x;
	    var nvy2 = v4.y - v2.y;
	    var nvz2 = v4.z - v2.z;
	    
	    var faceVertical = Cross(
                    new THREE.Vector3(nvx1,nvy1,nvz1), 
                    new THREE.Vector3(nvx2,nvy2,nvz2)
            );
	    
	    var length = Math.sqrt( (faceVertical.x * faceVertical.x
    	                        + faceVertical.y * faceVertical.y
	                        + faceVertical.z * faceVertical.z) );
	    var faceNormal = new THREE.Vector3(-faceVertical.x / length, -faceVertical.y / length, -faceVertical.z / length );
	    
            var face1 = new THREE.Face3( pCount, pCount + 2 , pCount + 1 );
	    face1.normal = faceNormal;
	    geom.faces.push(face1  );
	    geom.faceVertexUvs[ 0 ].push( [
		new THREE.UV( (u/(2*Math.PI)), (v/(2*Math.PI)) ),
		new THREE.UV( ((u+ustep)/(2*Math.PI)), ((v+vstep)/(2*Math.PI)) ),
		new THREE.UV( ((u+ustep)/(2*Math.PI)), (v/(2*Math.PI)) )
	    ] );

	    var face2 = new THREE.Face3( pCount, pCount + 3 , pCount + 2 );
	    face2.normal = faceNormal;
	    geom.faces.push( face2 );
	    geom.faceVertexUvs[ 0 ].push( [
		new THREE.UV( (u/(2*Math.PI)), (v/(2*Math.PI) )),
		new THREE.UV( (u/(2*Math.PI)),( (v+vstep)/(2*Math.PI)) ),
		new THREE.UV( ((u+ustep)/(2*Math.PI)), ((v+vstep)/(2*Math.PI) ))
	    ] );
	    
	    pCount = pCount + 4;
	 }
    }
    return geom;
}
};

</script>
</body>
</html>
```

# お試しはこちら
[http://jsdo.it/kjunichi/3t5w](http://jsdo.it/kjunichi/3t5w)

# 関連記事
- [Three.jsでポリゴンを描画するには](http://abrakatabura.hatenablog.com/entry/2011/08/20/092439)
- [BlenderでエクスポートしたJSONがThree.jsで変な色になる問題が解決](http://abrakatabura.hatenablog.com/entry/2015/12/21/071507)
- [ElectronでWebGL(three.js)使ってデスクトップマスコット作った際に分かったこと](http://abrakatabura.hatenablog.com/entry/2016/01/09/134348)
- [Windows10のBlenderをアップデートしたらThree.jsのアドオンが無くなったが、見つけた](http://abrakatabura.hatenablog.com/entry/2017/12/10/231431)
- [Three.jsでexamplesの高級車たちをblenderにExportしたが残念な結果に](http://abrakatabura.hatenablog.com/entry/2017/12/16/223318)
- [gopherくんのjsonデータがthree.jsのr85以降表示されなくてはまったjsonデータがthree.jsのr85以降表示されなくてはまった](http://abrakatabura.hatenablog.com/entry/2017/12/19/222952)