准确的对象边界框

时间:2017-05-09 13:47:43

标签: 3d three.js autodesk autodesk-forge autodesk-viewer

我试图创建一个对象的精确边界框,但看起来如果该对象未与轴对齐(我认为)该框未与该对象对齐。

例如:

enter image description here

粉红色和更接近橙色的顶点是这面墙的Box3.min,Box3.max,但是你看到那面墙上没有红色,绿色和蓝色。你可以忽略浅绿色顶点。

这是创建边界框的代码(返回Box3):

  static getWorldBoundingBox(model, dbId) {

    return new Promise(async(resolve, reject)=>{

      try{

        var fragIds = await ViewerToolkit.getFragIds(
          model, dbId);

        if(!fragIds.length){

          return reject('No geometry, invalid dbId?');
        }

        var fragList = model.getFragmentList();

        var fragbBox = new THREE.Box3();
        var nodebBox = new THREE.Box3();

        fragIds.forEach(function(fragId) {

          fragList.getWorldBounds(fragId, fragbBox);
          nodebBox.union(fragbBox);
        });

        return resolve(nodebBox);
      }
      catch(ex){

        return reject(ex);
      }
    });
  }

这就是我如何从min,max创建框:

    let ddd = new THREE.Vector3(min.x, min.y, min.z);
    let ddu = new THREE.Vector3(min.x, min.y, max.z);
    let dud = new THREE.Vector3(min.x, max.y, min.z);
    let udd = new THREE.Vector3(max.x, min.y, min.z);

    let duu = new THREE.Vector3(min.x, max.y, max.z);
    let uud = new THREE.Vector3(max.x, max.y, min.z);
    let udu = new THREE.Vector3(max.x, min.y, max.z);
    let uuu = new THREE.Vector3(max.x, max.y, max.z);

    this.drawVertices([ddd,ddu,dud,udd,duu,uud,udu,uuu]);

    let facesPoints = [
        {
            BL: ddd.clone(),
            UL: ddu.clone(),
            UR: udu.clone(),
            BR: udd.clone()
        },
        {
            BL: udd.clone(),
            UL: udu.clone(),
            UR: uuu.clone(),
            BR: uud.clone()
        },
        {
            BL: uud.clone(),
            UL: uuu.clone(),
            UR: duu.clone(),
            BR: dud.clone()
        },
        {
            BL: dud.clone(),
            UL: duu.clone(),
            UR: ddu.clone(),
            BR: ddd.clone()
        }
    ];

我想避免使用蛮力方法对所有顶点对的所有距离进行排序,然后选择前两个顶点。

是否有另一个数据结构会暴露一个多维数据集的8个点,而不是只有2个,我可以给它多边形来构建它,就像在上面的函数中一样?

2 个答案:

答案 0 :(得分:2)

边界框是世界轴对齐的。如果您的形状在空间中旋转,只需将形状的世界矩阵应用于其边界框的(副本)。这应该会给你世界的形状框。

在下面的示例中,红色立方体的边界框是根据局部空间计算的,我将红色立方体的矩阵应用于边界框。绿色立方体的每个框架都会重新计算其边界框,从而形成一个世界轴对齐的框,随着框的旋转而增大和缩小。

var renderer, scene, camera, controls, stats, rotationMatrix, tmpPos, cube1, cube2, cube1BBox, cube2BBox;

var WIDTH = window.innerWidth,
	HEIGHT = window.innerHeight,
	FOV = 35,
	NEAR = 1,
	FAR = 1000;
  
function populateExample(){
  rotationMatrix = new THREE.Matrix4().makeRotationY(0.5 * (Math.PI / 180));
	var cubeGeo = new THREE.BoxBufferGeometry(10, 10, 10),
		cube1Mat = new THREE.MeshPhongMaterial({ color: "red" }),
    cube2Mat = new THREE.MeshPhongMaterial({ color: "green" });
  cube1Mat.polygonOffset = true;
  cube1Mat.polygonOffsetFactor = 1;
  cube1Mat.polygonOffsetUnits = 0.5;
  cube2Mat.polygonOffset = true;
  cube2Mat.polygonOffsetFactor = 1;
  cube2Mat.polygonOffsetUnits = 0.5;
  
	cube1 = new THREE.Mesh(cubeGeo, cube1Mat);
	scene.add(cube1);
  
  cube2 = new THREE.Mesh(cubeGeo, cube2Mat);
	scene.add(cube2);
  
  cube1BBox = new THREE.BoxHelper(cube1, 0xffffff);
  scene.add(cube1BBox);
  
  cube2BBox = new THREE.BoxHelper(cube2, 0xffffff);
  scene.add(cube2BBox);
  
  cube1.position.set(-10, 0, 0);
  cube2.position.set(10, 0, 0);
  
  cube1BBox.position.set(-10, 0, 0);
}

function exampleRenderAction(){
  tmpPos.copy(cube1.position);
  
  cube1.position.sub(tmpPos);
  cube1.updateMatrix();
  cube1.applyMatrix(rotationMatrix);
  cube1.position.add(tmpPos);
  cube1.updateMatrix();
  
  cube1BBox.matrix.copy(cube1.matrix);
  
  tmpPos.copy(cube2.position);
  
  cube2.position.sub(tmpPos);
  cube2.updateMatrix();
  cube2.applyMatrix(rotationMatrix);
  cube2.position.add(tmpPos);
  cube2.updateMatrix();
  
  cube2BBox.update();
}

function init() {
  tmpPos = new THREE.Vector3();
  rotation = 0;
  rotationSpeed = 0.5;
	document.body.style.backgroundColor = "slateGray";

	renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });

	document.body.appendChild(renderer.domElement);
	document.body.style.overflow = "hidden";
	document.body.style.margin = "0";
	document.body.style.padding = "0";

	scene = new THREE.Scene();

	camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
	camera.position.set(0, 40, 40);
	scene.add(camera);

	controls = new THREE.TrackballControls(camera, renderer.domElement);
	controls.dynamicDampingFactor = 0.5;
	controls.rotateSpeed = 3;

	var light = new THREE.PointLight(0xffffff, 1, Infinity);
	camera.add(light);

	stats = new Stats();
	stats.domElement.style.position = 'absolute';
	stats.domElement.style.top = '0';
	document.body.appendChild(stats.domElement);

	resize();
	window.onresize = resize;

	populateExample();

	animate();
}

function resize() {
	WIDTH = window.innerWidth;
	HEIGHT = window.innerHeight;
	if (renderer && camera && controls) {
		renderer.setSize(WIDTH, HEIGHT);
		camera.aspect = WIDTH / HEIGHT;
		camera.updateProjectionMatrix();
		controls.handleResize();
	}
}

function render() {
	renderer.render(scene, camera);
}

function animate() {
	requestAnimationFrame(animate);
	render();
  exampleRenderAction();
	controls.update();
	stats.update();
}

function threeReady() {
	init();
}

(function () {
	function addScript(url, callback) {
		callback = callback || function () { };
		var script = document.createElement("script");
		script.addEventListener("load", callback);
		script.setAttribute("src", url);
		document.head.appendChild(script);
	}

	addScript("https://threejs.org/build/three.js", function () {
		addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function () {
			addScript("https://threejs.org/examples/js/libs/stats.min.js", function () {
				threeReady();
			})
		})
	})
})();

根据评论中的说明扩展答案:

边界框是THREE.Box3,其中包含minmax THREE.Vector3 s。所以要像你已经做的那样得到边界框的8个角:

var corners = [
    new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z),
    new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.max.z),
    new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.max.z),
    new THREE.Vector3(bbox.min.x, bbox.max.y, bbox.min.z),
    new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.max.z),
    new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z),
    new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.min.z),
    new THREE.Vector3(bbox.max.x, bbox.max.y, bbox.min.z)
];

您可以按照自己喜欢的方式安排。要将这些转换为世界坐标,您需要再执行一步。 请注意,以下步骤具有破坏性,因此如果您需要保留原始角点值,则需要保存其副本。

顶点当前是对象的本地,因此您需要使用对象的矩阵更新它们:

for(var i = 0, len = corners.length; i < len; ++0){
    // this will apply all transformations from all parents
    corners[i].applyMatrix4(myObj.matrixWorld);
}

或者,您可以使用localToWorld将点转换为世界坐标。

for(var i = 0, len = corners.length; i < len; ++0){
    // this literally does the same thing as the code above
    myObj.localToWorld(corners[i]);
}

但是不要两者兼顾,否则你最终会得到不正确的值。

答案 1 :(得分:0)

我找到了一种方法,首先我使用Autodesk的查看器扩展(网格数据)中的这个函数将所有顶点收集到general set

  static getMeshVertices(viewer, fragId) {

    var fragProxy = viewer.impl.getFragmentProxy(
      viewer.model,
      fragId);

    var renderProxy = viewer.impl.getRenderProxy(
      viewer.model,
      fragId);

    fragProxy.updateAnimTransform();

    var matrix = new THREE.Matrix4();
    fragProxy.getWorldMatrix(matrix);

    const verticesSet = new GeneralSet();
    const geometry = renderProxy.geometry;
    const attributes = geometry.attributes;

    if (attributes && attributes.index !== undefined) {

      const indices = attributes.index.array || geometry.ib;
      const positions = geometry.vb ? geometry.vb : attributes.position.array;
      const stride = geometry.vb ? geometry.vbstride : 3;
      let offsets = geometry.offsets;

      if (!offsets || offsets.length === 0) {

        offsets = [{ start: 0, count: indices.length, index: 0 }];
      }

      for (var oi = 0, ol = offsets.length; oi < ol; ++oi) {

        const start = offsets[oi].start;
        const count = offsets[oi].count;
        const index = offsets[oi].index;

        for (var i = start, il = start + count; i < il; i += 3) {

          const vA = new THREE.Vector3();
          const vB = new THREE.Vector3();
          const vC = new THREE.Vector3();
          const a = index + indices[i];
          const b = index + indices[i + 1];
          const c = index + indices[i + 2];

          vA.fromArray(positions, a * stride);
          vB.fromArray(positions, b * stride);
          vC.fromArray(positions, c * stride);

          vA.applyMatrix4(matrix);
          vB.applyMatrix4(matrix);
          vC.applyMatrix4(matrix);

          verticesSet.add(vA);
          verticesSet.add(vB);
          verticesSet.add(vC);
        }
      }

      return verticesSet;
    }
    else {

      var positions = geometry.vb ? geometry.vb : attributes.position.array;
      var stride = geometry.vb ? geometry.vbstride : 3;

      for (var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9) {
        let vA = new THREE.Vector3();
        let vB = new THREE.Vector3();
        let vC = new THREE.Vector3();
        var a = i;
        var b = i + 1;
        var c = i + 2;

        vA.fromArray(positions, a * stride);
        vB.fromArray(positions, b * stride);
        vC.fromArray(positions, c * stride);

        vA.applyMatrix4(matrix);
        vB.applyMatrix4(matrix);
        vC.applyMatrix4(matrix);

        verticesSet.add(vA);
        verticesSet.add(vB);
        verticesSet.add(vC);
      }

      return verticesSet;
    }
  }

然后我从这里使用了一个图形直径算法:https://cs.stackexchange.com/a/213/43035一次在顶点集上(就好像集合代表一个完整的图形)得到两个相对的角落,让我们称它们为w,

然后我从顶点集合中移除了你,并再次运行了图形直径,得到了另外两个角。

现在有四个角可以生成所有其余的角色,并对它们进行排序,有3个条件可以检查4个角中的每个角(这将显示其他4个角),距离相机的距离(更近或更远) ,高度(上角或下角)以及从对象中间向左或向右(使用十字和点,如此https://forum.unity3d.com/threads/left-right-test-function.31420/(它们有js代码))。

这将为您提供8个角,这样您就可以知道哪个角落在哪里,并且角落在对象上,对象如何与轴对齐无关紧要。

相关问题