forked from: 視線とポリゴンメッシュの交差判定
視線とメッシュの交差判定のテスト(処理が重い版)
(追記)176行目のgetWorldTransform()の第二引数書き忘れてました。。
/**
* Copyright tepe ( http://wonderfl.net/user/tepe )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ntHC
*/
// forked from tencho's 視線とポリゴンメッシュの交差判定
/**
* 視線とメッシュの交差判定のテスト(処理が重い版)
* (追記)176行目のgetWorldTransform()の第二引数書き忘れてました。。
*/
package {
import flash.events.*;
import flash.utils.*;
import org.papervision3d.core.geom.*;
import org.papervision3d.core.geom.renderables.*;
import org.papervision3d.core.math.*;
import org.papervision3d.materials.*;
import org.papervision3d.materials.special.*;
import org.papervision3d.materials.utils.*;
import org.papervision3d.objects.*;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.view.*;
import org.papervision3d.view.stats.*;
public class CollideRayToMesh extends BasicView {
private var cube:Cube;
private var point:Sphere;
private var lines:Lines3D;
private var line:Line3D;
private var wire:WireframeMaterial;
private var color:ColorMaterial;
//コンストラクタ
public function CollideRayToMesh() {
stage.frameRate = 60;
opaqueBackground = 0x000000;
addChild(new StatsView(renderer));
wire = new WireframeMaterial(0xFF8800);//キューブの色
color = new ColorMaterial(0xFF88ff, 0.7);//交差面の色
lines = new Lines3D(new LineMaterial());
lines.addNewLine(2, 0, 0, 0, 0, 550, 0);
line = lines.lines[0] as Line3D;
cube = new Cube(new MaterialsList( { all:wire } ), 200, 200, 200, 2, 2, 2);
point = new Sphere(new ColorMaterial(0xFFFF00), 10, 8, 6);//接点
cube.position = new Number3D(0, 200, 0);
var plane:Plane = new Plane(new WireframeMaterial(0x007700), 1000, 1000, 5, 5);//グリッド
plane.rotationX = 90;
scene.addChild(cube);
scene.addChild(point);
scene.addChild(plane);
scene.addChild(lines);
camera.position = new Number3D(0, 700, -400);
addEventListener(Event.ENTER_FRAME, onEnter);
}
/**
* 毎フレーム処理
*/
private function onEnter(e:Event):void {
//キューブを回転・変形させる
var tm:int = getTimer();
cube.scaleY = (Math.cos(Math.PI/180 * tm / 30) + 1) *0.8;
cube.rotationZ = tm/25;
cube.rotationX = tm/25;
cube.rotationY = tm/25;
//視線用ライン
line.v0.x = (mouseX - stage.stageWidth/2)*2;
line.v0.z = (-mouseY + stage.stageHeight/2)*2;
//line.v1.x = (mouseX - stage.stageWidth/2)*2;
//line.v1.y = (-mouseY + stage.stageHeight/2)*2;
var ray:Number3D = line.v0.toNumber3D()
ray.minusEq(line.v1.toNumber3D());
//交差判定
var res:Array = collideRayToMeshList(line.v1.toNumber3D(), ray, [cube], false, true);
for each(var face:Triangle3D in cube.geometry.faces) face.material = wire;
if (res.length == 0) {
//交差なし
point.visible = false;
} else {
//交差あり
res[0].triangle.material = color;
point.visible = true;
point.position = new Number3D(res[0].position.x, res[0].position.y, res[0].position.z);
}
renderer.renderScene(scene, camera, viewport);
}
/**
* 視線と複数メッシュの交差判定をする
* @param pos レイ放射地点
* @param vec レイの方向
* @param meshes メッシュオブジェクトの配列
* @param isDoubleSided 裏向きのポリゴンも交差判定する
* @param updateTransform モデルが回転・変形していたらtrueにする
* @return
*/
private function collideRayToMeshList(pos:Number3D, vec:Number3D, meshes:Array, isDoubleSided:Boolean = false, updateTransform:Boolean = false):Array {
var res:Array = new Array();
for each(var mesh:TriangleMesh3D in meshes) {
//モデルのワールド空間における変形情報
var transform:Matrix3D = getWorldTransform(mesh, updateTransform);
//メッシュモデル内の全三角ポリゴンをチェック
for each(var face:Triangle3D in mesh.geometry.faces) {
var ps:Array = new Array();
for each(var v:Vertex3D in face.vertices) {
var n:Number3D = v.toNumber3D();
Matrix3D.multiplyVector4x4(transform, n);
ps.push(n);
}
var hit:Object = collideRayToTriangle(pos, vec, ps[0], ps[1], ps[2], isDoubleSided);
if (hit != null) {
hit.triangle = face;
res.push(hit);
}
}
}
res.sortOn("distance", Array.NUMERIC);
return res;
}
/**
* 視線と三角ポリゴンの交差判定をする(ポリゴンは頂点反時計周りで構成)
* @param pos 視線の開始地点
* @param vec 視線の方向ベクトル
* @param p0 三角ポリゴンの頂点1
* @param p1 三角ポリゴンの頂点2
* @param p2 三角ポリゴンの頂点3
* @param isDoubleSided 裏向きのポリゴンも交差判定する
* @return
*/
private function collideRayToTriangle(pos:Object, vec:Object, p0:Object, p1:Object, p2:Object, isDoubleSided:Boolean = false):Object {
//視線ベクトルの正規化
var v:Object = { x:vec.x, y:vec.y, z:vec.z };
normalize(v);
//法線ベクトルを求める
var n:Object = cross3D( { x:p1.x - p0.x, y:p1.y - p0.y, z:p1.z - p0.z }, { x:p2.x - p0.x, y:p2.y - p0.y, z:p2.z - p0.z } );
normalize(n);
var vn:Number = dot3D(v, n);
//視線に対して面が表向きか
var isRightSide:Boolean = (vn < 0);
//1.視線が平面と平行なら交差なし
//2.片面チェックON時に視線と法線が同じ向き(面が裏向き)なら交差なし
if ((isRightSide? -vn : vn) < 0.0000001 || (!isRightSide && !isDoubleSided)) return null;
var xpn:Number = dot3D( { x:pos.x - p0.x, y:pos.y - p0.y, z:pos.z - p0.z }, n);
var distance:Number = -xpn / vn;
//3.交点が視線の後ろなら交差なし
if (distance < 0) return null;
//平面との交点座標を求める
var hit:Object = { x:v.x * distance + pos.x, y:v.y * distance + pos.y, z:v.z * distance + pos.z };
//4.交点が三角形内にあるかチェック
var cross0:Object = cross3D( { x:hit.x - p0.x, y:hit.y - p0.y, z:hit.z - p0.z }, { x:p1.x - p0.x, y:p1.y - p0.y, z:p1.z - p0.z } );
if (dot3D(cross0, n) > 0.000000001) return null;
var cross1:Object = cross3D( { x:hit.x - p1.x, y:hit.y - p1.y, z:hit.z - p1.z }, { x:p2.x - p1.x, y:p2.y - p1.y, z:p2.z - p1.z } );
if (dot3D(cross1, n) > 0.000000001) return null;
var cross2:Object = cross3D( { x:hit.x - p2.x, y:hit.y - p2.y, z:hit.z - p2.z }, { x:p0.x - p2.x, y:p0.y - p2.y, z:p0.z - p2.z } );
if (dot3D(cross2, n) > 0.000000001) return null;
//交差情報を返す{position:交差点、isRightSide:交差面が視線に対して表向きかどうか、distance:視点から交点までの距離}
return { position:hit, isRightSide:vn < 0, distance:distance };
}
/**ベクトルの正規化*/
private function normalize(v:Object):void {
var mod:Number = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
if (mod != 0 && mod != 1) {
mod = 1 / mod;
v.x *= mod;
v.y *= mod;
v.z *= mod;
}
}
/**2つの3Dベクトルの外積を返す*/
private function cross3D(v1:Object, v2:Object):Object {
return { x:v2.y * v1.z - v2.z * v1.y, y:v2.z * v1.x - v2.x * v1.z, z:v2.x * v1.y - v2.y * v1.x };
}
/**2つの3Dベクトルの内積を返す*/
private function dot3D(vec1:Object, vec2:Object):Number {
return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z;
}
/**
* オブジェクトのワールド空間での変形情報を取得
* @param obj 変形情報を取得するモデル
* @param updateTransform モデルが回転・変形していたらtrueにする
* @return
*/
private function getWorldTransform(obj:DisplayObject3D, updateTransform:Boolean = false):Matrix3D {
if (updateTransform) obj.updateTransform();
var res:Matrix3D = new Matrix3D();
res.copy(obj.transform);
var parent3D:DisplayObject3D = obj.parent as DisplayObject3D;
if (parent3D != null) res.calculateMultiply(getWorldTransform(parent3D, updateTransform), res);
return res;
}
}
}