检查点在多边形内部-无法正常工作

时间:2019-04-27 20:39:53

标签: three.js collision-detection raycasting

我基于任何带有点的数组创建一个房间。可以移动的对象将添加到房间中。 目的是阻止将物体移出房间的可能性。

移动对象时,我检查所有4个底点是否都属于地板-如果是,则可以将对象进一步移动-如果不是,则意味着该对象将在墙后面,因此必须还原移到先前的位置。

负责创建房间的函数(wallsPoint是具有点的数组)

var wallPoint = [
 {
   "X": 0,
   "Y": 0
 },
 {
   "X": 5,
   "Y": 0
},
 {
  "X": 5,
  "Y": 3
},
{
  "X": 7,
  "Y": 3
},
{
  "X": 7,
  "Y": 5
},
{
  "X": 5,
  "Y": 5
},
{
  "X": 5,
  "Y": 7
},
{
  "X": 0,
  "Y": 7
}
 ];

function createRoom() {

// create a walls
var walls = new THREE.Geometry();
var floorPoints = [];


wallsPoint.forEach(r => {
    walls.vertices.push(new THREE.Vector3(r.X, r.Y, 0));
walls.vertices.push(new THREE.Vector3(r.X, r.Y, 3));
floorPoints.push(new THREE.Vector3(r.X, r.Y, 0));

walls.name = "wall";
});
    var previousVertexIndex = walls.vertices.length - 2;
    for (i = 0; i < walls.vertices.length; i += 2) {
        walls.faces.push(new THREE.Face3(i + 1, i, 
previousVertexIndex));
       walls.faces.push(new THREE.Face3(previousVertexIndex + 1, i + 
1, previousVertexIndex));
       previousVertexIndex = i;

    }
    walls.computeFaceNormals();

var wallsTexture = new THREE.TextureLoader().load( 'walls.jpg' );//  
var mesh = new THREE.Mesh(walls, new THREE.MeshBasicMaterial({
    map: wallsTexture
}));
mesh.rotation.x= -Math.PI /2;

//create a floor
var floorShape = new THREE.Shape(floorPoints);
var floorGeometry = new THREE.ShapeGeometry(floorShape);
var floorTexture = new THREE.TextureLoader().load( 'wood.jpg' ); 
floor = new THREE.Mesh(floorGeometry, new THREE.MeshBasicMaterial({
    map: floorTexture,
    side: THREE.DoubleSide
}));


floor.geometry.vertices = floorPoints;
floor.geometry.name = "floor";
floor.rotation.x =-Math.PI/2;

group = new THREE.Object3D();
group.add(mesh);
group.add( floor );
group.add(ceiling) ;
scene.add( group );
  }

负责检查点是否在地板上的功能。 点是helperBox.geometry.attributes.position.array(我们用鼠标移动的对象的帮助盒)

function checkPoints(points){

var arrayToCheck = [new THREE.Vector3(points[6],points[7],points[8]),new THREE.Vector3(points[9],points[10],points[11]),new THREE.Vector3(points[18],points[19], points[20]),new THREE.Vector3(points[21],points[22],points[23])];


var inside = [];
var raycaster = new THREE.Raycaster();

for(var i = 0; i<arrayToCheck.length; i++){
    var pointToCheck = arrayToCheck[i];
    raycaster.set(pointToCheck, new THREE.Vector3(1,1,1));
    var intersects = raycaster.intersectObject(floor);

    if( intersects.length %2 === 1) {
        inside.push(true);
        console.log(true);
    }
    else{
        inside.push(false);
        console.log(false);
    }
}
if(!inside.includes(false)){
    return true
}
else{
    return false
}
 }

我希望checkPoints函数根据所有4个点是否都在地板上来显示true或false。 当前,在大多数情况下,该功能运行良好。不幸的是,并非所有人。每次坏时-大约在第3点和/或第4点。

如何解决? 或者,如何不让对象(模型为obj格式-形状不规则)超出地面?

1 个答案:

答案 0 :(得分:0)

该算法的实现非常粗糙的概念,取自https://github.com/substack/point-in-polygon

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(3.5, 10, 3.5);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(3.5, 0, -3.5);
controls.update();

var grid = new THREE.GridHelper(20, 20, 0x44ff44);
grid.position.set(0, -0.01, 0);
scene.add(grid);

var wallPoints = wallsPoint.map(w => {
  return new THREE.Vector3(w.X, 0, -w.Y)
});
var geom = new THREE.BufferGeometry().setFromPoints(wallPoints);
var points = new THREE.Points(geom, new THREE.PointsMaterial({
  size: 1,
  color: "red"
}));
var lines = new THREE.LineLoop(geom, new THREE.LineBasicMaterial({
  color: "yellow"
}));

scene.add(points, lines);

var marker = new THREE.Mesh(new THREE.SphereBufferGeometry(0.25, 8, 2), new THREE.MeshBasicMaterial({
  color: "aqua"
}));
marker.position.set(3.5, 3.5, 0);
scene.add(marker);

document.addEventListener("mousemove", onMouseMove, false);

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);
var iPoint = new THREE.Vector3();

function onMouseMove(event) {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse, camera);
  raycaster.ray.intersectPlane(plane, marker.position);
  marker.material.color.set(isInside(marker.position, wallPoints) ? "aqua" : "magenta");
}

function isInside(point, vs) {

  var x = point.x,
    y = point.z;

  var inside = false;
  for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
    var xi = vs[i].x,
      yi = vs[i].z;
    var xj = vs[j].x,
      yj = vs[j].z;

    var intersect = ((yi > y) != (yj > y)) &&
      (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
    if (intersect) inside = !inside;
  }

  return inside;
}

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera)
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
  var wallsPoint = [{
      "X": 0,
      "Y": 0
    },
    {
      "X": 5,
      "Y": 0
    },
    {
      "X": 5,
      "Y": 3
    },
    {
      "X": 7,
      "Y": 3
    },
    {
      "X": 7,
      "Y": 5
    },
    {
      "X": 5,
      "Y": 5
    },
    {
      "X": 5,
      "Y": 7
    },
    {
      "X": 0,
      "Y": 7
    }
  ];
</script>