Three.js によるドラッグ&ドロップによるTriファイルの描画 (イトカワの形状データ)
先日(Three.js による頂点データの取り込みと描画 (イトカワの形状データ))に引き続き、小惑星イトカワの頂点データを利用したネタです。 本稿では、データファイルをウェブブラウザにドラッグ&ドロップで読み込ませて描画する手法についてです。 本サイトでは今後、ウェブブラウザ(HTML5+WebGL)を様々な計算結果を3次元ビューアとして行くことを念頭に進めていきます。
実行結果
WebGLが実行可能な環境で実行ページをご覧下さい。 下記の図はデモ画面です。
以下の図はデモ画面です → 実行ページこちら
Javascript のプログラムソースコード
本稿ではドラック&ドロップのマウスイベントと、HTML5で追加された FILE API を利用して、HTML5 のキャンバス要素に WebGLのライブラリであるThree.js を用いて描画します。
var width, height; var renderer; function initThree() { width = document.getElementById('canvas-frame').clientWidth; height = document.getElementById('canvas-frame').clientHeight; try {renderer = new THREE.WebGLRenderer({antialias: true});} catch (e) {} if(!renderer) document.getElementById("errer").innerHTML = '<p style="text-align:center;font-size:small; color:red">お使いの環境ではWebGLはご利用いただけません。<br />WebGLに対応していない方のためにGIFファイルを以下に用意しました。<br /><br /><img src="http://www.natural-science.or.jp/images/tutorial6.gif" alt="WEBGLデモ" /></p>'; renderer.setSize(width, height ); document.getElementById('canvas-frame').appendChild(renderer.domElement); renderer.setClearColorHex(0x000000, 1.0); } var camera; function initCamera() { camera = new THREE.PerspectiveCamera( 45 , width / height , 1 , 10000 ); camera.position.x = 70; camera.position.y = 50; camera.position.z = 0; camera.up.x = 0; camera.up.y = 0; camera.up.z = 1; } var scene; function initScene() { scene = new THREE.Scene(); } var light, light2; function initLight() { light = new THREE.DirectionalLight(0xFFFFFF, 1.0, 0); light.position.set( 100, 100, 100 ); scene.add(light); light2 = new THREE.AmbientLight(0x333333); scene.add(light2); } function initObject(){} var t=0; function loop() { t++; if((typeof itokawa)=="object") itokawa.rotation.set( 0, 0, t/100 ); renderer.clear(); camera.lookAt( {x:0, y:0, z:0 } ); renderer.render(scene, camera); window.requestAnimationFrame(loop); } function initEvent(){ window.addEventListener("mousedown", onMousedown, false); window.addEventListener("mouseup", onMouseup, false); window.addEventListener("mousemove", onMousemove, false); window.addEventListener("mousewheel", onMousewheel, false); window.addEventListener("dragover", onCancel, false); window.addEventListener("dragenter", onCancel, false); window.addEventListener("drop", onDropFile, false); } function threeStart() { initThree(); initCamera(); initScene(); initLight(); initObject(); initEvent(); loop(); } //3点で形成される三角形の位置ベクトルから法線ベクトルを計算する関数 function Normal( v1, v2, v3 ){ var vx = (v1.y - v3.y) * (v2.z - v3.z) - (v1.z - v3.z) * (v2.y - v3.y); var vy = (v1.z - v3.z) * (v2.x - v3.x) - (v1.x - v3.x) * (v2.z - v3.z); var vz = (v1.x - v3.x) * (v2.y - v3.y) - (v1.y - v3.y) * (v2.x - v3.x); var va = Math.sqrt( Math.pow(vx,2) +Math.pow(vy,2)+Math.pow(vz,2)); var v = {x:vx/va, y:vy/va, z:vz/va}; //規格化する return v; } var itokawa; function parse_stl(stl_data) { var lines = stl_data.split("\n"); var str= "" ; var geom = new THREE.Geometry(); for (var i=0; i<lines.length; i++) { var vertexes = lines[i].split(" ");//三角形の頂点の各座標 3点×3座標 var p1 = {x:vertexes[0], y:vertexes[1], z:vertexes[2]}; var p2 = {x:vertexes[3], y:vertexes[4], z:vertexes[5]}; var p3 = {x:vertexes[6], y:vertexes[7], z:vertexes[8]}; var n = Normal( p1, p2, p3 ); var v1 = new THREE.Vector3(p1.x, p1.y, p1.z); var v2 = new THREE.Vector3(p2.x, p2.y, p2.z); var v3 = new THREE.Vector3(p3.x, p3.y, p3.z); geom.vertices.push(new THREE.Vertex(v1)); geom.vertices.push(new THREE.Vertex(v2)); geom.vertices.push(new THREE.Vertex(v3)); var face = new THREE.Face3( 3*i, 3*i+1, 3*i+2 ); var faceNormal = new THREE.Vector3( n.x, n.y, n.z ); face.normal = faceNormal; geom.faces.push( face ); } var material = new THREE.MeshLambertMaterial({color: 0xffffff, ambient:0xffffff}); //材質の設定 itokawa = new THREE.Mesh( geom, material); //イトカワ itokawa.scale = new THREE.Vector3( 100, 100, 100 ); //イトカワのスケールを等方的に100倍する scene.add(itokawa); itokawa.position.set(0,0,0); } // ファイル読み込み var readFile = function(file){ var reader = new FileReader(); // ファイルリーダー生成 reader.onload = function(e) { parse_stl(e.target.result); }; // テキストとしてファイルを読み込む reader["readAsText"](file, "utf-8"); }; ////マウスイベント//////////////////////////////////////////////////// var down = false; var sy = 0, sz = 0; function onMousedown (ev){ //マウスダウン時イベント if (ev.target == renderer.domElement) { down = true; sy = ev.clientX; sz = ev.clientY; } }; function onMouseup (){ //マウスアップ時イベント down = false; }; function onMousemove (ev) { //マウスムーブ時イベント var speed = 0.5; if (down) { if (ev.target == renderer.domElement) { var dy = -(ev.clientX - sy); var dz = -(ev.clientY - sz); camera.position.y += dy*speed; camera.position.z -= dz*speed; sy -= dy; sz -= dz; } } } function onMousewheel(ev){//マウスホイール時イベント var speed = 0.5; camera.position.x += ev.wheelDelta * speed ; } var onDropFile = function(ev){ // ファイルドロップ時イベント ev.preventDefault(); var file = ev.dataTransfer.files[0]; // File オブジェクトを取得 readFile(file); // ファイル読み込み }; var onCancel = function(ev){ // デフォル処理をキャンセル if(ev.preventDefault) { ev.preventDefault(); } return false; }; /////////////////////////////////////////////////////////////////////// window.onload = function(){ threeStart(); }
参考ページ
■ HTML5 File API を使ってファイル読み込み(TM Life)
■ JavaScriptでファイル操作!? File APIを使いこなそう(@IT)
■ Three.js による頂点データの取り込みと描画 (イトカワの形状データ)
■ HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(1/3)~
■ HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(2/3)~
■ HTML5による物理シミュレーション環境の構築 ~WebGLライブラリThree.js 入門(3/3)~