首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >物体的精确包围盒

物体的精确包围盒
EN

Stack Overflow用户
提问于 2017-05-09 13:47:43
回答 2查看 2.4K关注 0票数 2

我试图为一个对象创建一个精确的包围框,但是如果对象与轴(我认为)没有对齐,那么这个框就不会与对象对齐。

例如:

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

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

代码语言:javascript
复制
  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);
      }
    });
  }

这就是我如何从最小值创建盒子的方法,麦克斯:

代码语言:javascript
复制
    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个点,而不是我可以给它的两个多边形来构建它,就像上面的函数一样?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-14 12:06:25

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

代码语言:javascript
复制
  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一次在顶点集上(就好像这个集合代表一个完整的图)得到两个相反的角,让我们称它们为u,w。

然后我把u,w从顶点集合中移除,然后再运行图的直径,得到另外两个角。

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

这将给你8个角,这样你就可以知道哪个角在哪里,而角在物体上,不管物体是如何与轴对齐的。

票数 0
EN

Stack Overflow用户

发布于 2017-05-09 14:56:31

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

在下面的示例中,红色立方体的边界框是从本地空间计算出来的,我将红色立方体的矩阵应用到边界框中。绿色立方体有它的包围盒重新计算每一帧,从而产生一个世界轴对齐的盒子,增长和收缩随着盒子旋转。

代码语言:javascript
复制
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。因此,要像您已经做的那样,获得包围框的8个角落:

代码语言:javascript
复制
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)
];

你可以按你喜欢的方式安排这些。要将这些转换为世界坐标,您需要再执行一步。注意,下面的步骤是破坏性的,因此如果您需要保留原始的角值,则需要保存它们的副本。

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

代码语言:javascript
复制
for(var i = 0, len = corners.length; i < len; ++0){
    // this will apply all transformations from all parents
    corners[i].applyMatrix4(myObj.matrixWorld);
}

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

代码语言:javascript
复制
for(var i = 0, len = corners.length; i < len; ++0){
    // this literally does the same thing as the code above
    myObj.localToWorld(corners[i]);
}

但不要同时做这两件事,否则你最终会得到不正确的值。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43871636

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档