2013-11-24 5 views
1

Я использую Three.js, и мне интересно, как получить все объекты в данной области?Threejs - Как выбрать все объекты в области?

Например, получить все объекты, которые найдены в зеленом-квадрат: Mass object selection

Решение:

 getEntitiesInSelection: function(x, z, width, height, inGroup) { 
      var self = this, 
       entitiesMap = [], 
       color = 0, 
       colors = [], 
       ids = [], 
       pickingGeometry = new THREE.Geometry(), 
       pickingMaterial = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors }), 
       pickingScene = new THREE.Scene(), 
       pickingTexture = new THREE.WebGLRenderTarget(this._renderer.domElement.width, this._renderer.domElement.height), 
       cloneMesh, 
       entities = inGroup ? 
        engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities(); 

      pickingTexture.generateMipmaps = false; 

      //Go over each entity, change its color into its ID 
      _.forEach(entities, function(entity) { 
       if(undefined == entity.threeRenderable) { 
        return ; 
       } 

       //Clone entity 
       cloneMesh = entity.threeRenderable.mesh().clone(); 
       cloneMesh.material = entity.threeRenderable.mesh().material.clone(); 
       cloneMesh.material.map = null; 
       cloneMesh.material.vertexColors = THREE.VertexColors; 
       cloneMesh.geometry = entity.threeRenderable.mesh().geometry.clone(); 
       cloneMesh.position.copy(entity.threeRenderable.mesh().position); 
       cloneMesh.rotation.copy(entity.threeRenderable.mesh().rotation); 
       cloneMesh.scale.copy(entity.threeRenderable.mesh().scale); 

       //Cancel shadow 
       cloneMesh.castShadow = false; 
       cloneMesh.receiveShadow = false; 

       //Set color as entity ID 
       entitiesMap[color] = entity.id(); 
       self._applyVertexColors(cloneMesh.geometry, new THREE.Color(color)); 
       color++; 

       THREE.GeometryUtils.merge(pickingGeometry, cloneMesh); 
      }); 

      pickingScene.add(new THREE.Mesh(pickingGeometry, pickingMaterial)); 

      //render the picking scene off-screen 
      this._renderer.render(pickingScene, this._objs[this._mainCamera], pickingTexture); 
      var gl = this._renderer.getContext(); 

      //read the pixel under the mouse from the texture 
      var pixelBuffer = new Uint8Array(4 * width * height); 
      gl.readPixels(x, this._renderer.domElement.height - z, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixelBuffer); 

      //Convert RGB in the selected area back to color 
      for(var i=0; i<pixelBuffer.length; i+=4) { 
       if(0 == pixelBuffer[i] && 0 == pixelBuffer[i+1] && 0 == pixelBuffer[i+2] && 0 == pixelBuffer[i+3]) { 
        continue; 
       } 

       color = (pixelBuffer[i] << 16) | (pixelBuffer[i+1] << 8) | (pixelBuffer[i+2]); 
       colors.push(color); 
      } 
      colors = _.unique(colors); 

      //Convert colors to ids 
      _.forEach(colors, function(color) { 
       ids.push(entitiesMap[color]); 
      }); 

      return ids; 
     } 

Линия engine.getObjectsByGroup(inGroup) : engine.getRegisteredEntities(); просто возвращает массив сущностей, которые, в свою очередь, I итерации по объектам:

_.forEach(entities, function(entity) { ... 

только лица, которые имеют свойство «threeRenderable» (объект) открыты, поэтому я игнорирую те, которые не имеют его:

if(undefined == entity.threeRenderable) { 
     return ; 
} 

тогда я сливаю клонированный сетку хозяйствующего субъекта с с pickingGeometry:

THREE.GeometryUtils.merge(pickingGeometry, cloneMesh); 

в конце концов, я добавляю pickingGeometry к pickingScene:

pickingScene.add(new THREE.Mesh(pickingGeometry, pickingMaterial)); 

Затем я прочитал цвета выбранной области, и возвращает массив идентификаторов.

Вы можете проверить Node.js game engine, который я написал тогда.

+0

Какой вид вы используете сейчас? См. Пример GPU-Picking. Вы можете отобразить пропуск с идентификаторами цвета и использовать readPixels для чтения всей информации о цвете в данной области, возвращая все идентификаторы цвета объекта в этом регионе. Это даст вам необходимую вам информацию. Не уверен в других возможностях с raycaster ... btw: выглядит здорово, что вы готовы;) – GuyGood

+0

Я не уверен, как поможет GPU-сбор. в этом примере каждый куб визуализируется с другим цветом - таким образом, используя readPixel (который возвращает все пиксели/цвета в заданной области), вы можете «знать», на каком кубе щелкнул пользователь. WHILE Мне нужно, чтобы вернуть список объектов в данной области. – eldad87

+0

Да, как вы сказали, readPixels возвращает все пиксели/цвета в данной области. Данная область является вашим прямоугольным выбором. Цвета - это ваши идентификаторы объекта.Используя вывод readPixels, вы можете оценить цвета и вернуть список объектов в данной области, что даст именно то, чего вы хотите достичь? Вам нужно всего лишь отобразить все идентификаторы объектов в фреймбуфере/текстуре. – GuyGood

ответ

1

Я хотел реализовать что-то вроде этого, и я выбираю совсем другой метод - может быть, намного хуже, я действительно не знаю, - но гораздо проще сделать ИМО, поэтому я ставлю его здесь, если кто-то захочет этого.

В принципе, я использовал только 2 raycasts знать первые и последние точки прямоугольника выбора, проецируемый на моей земле самолете, и перебирать свои объекты, чтобы знать, какие из них находится в

Некоторые очень простой код.:

function onDocumentMouseDown(event) { 
     // usual Raycaster stuff ... 

     // get the ground intersection 
     var intersects = raycaster.intersectObject(ground); 

     GlobalGroundSelection = { 
     screen: { x: event.clientX, y: event.clientY }, 
     ground: intersects[0].point 
     }; 
    } 

    function onDocumentMouseUp(event) { 
     // ends a ground selection 
     if (GlobalGroundSelection) { 
     // usual Raycaster stuff ... 

     // get the ground intersection 
     var intersects = raycaster.intersectObjects(ground); 

     var selection = { 
      begins: GlobalGroundSelection.ground, 
      ends: intersects[0].point 
     }; 

     GlobalGroundSelection = null; 
     selectCharactersInZone(selection.begins, selection.ends); 
     } 
    } 

    function onDocumentMouseMove(event) { 

     if (GlobalGroundSelection) { 
     // in a selection, draw a rectangle 
     var p1 = GlobalGroundSelection.screen, 
      p2 = { x: event.clientX, y: event.clientY }; 

     /* with these coordinates 
      left: p1.x > p2.x ? p2.x : p1.x, 
      top: p1.y > p2.y ? p2.y : p1.y, 
      width: Math.abs(p1.x - p2.x), 
      height: Math.abs(p1.y - p2.y) 
     */ 
     } 
    } 

Вот мой выбор функции:

function selectCharactersInZone (start, end) { 

    var selected = _.filter(SELECTABLE_OBJECTS , function(object) { 
    // warning: this ignore the Y elevation value 
    var itsin = object.position.x > start.x 
      && object.position.z > start.z 
      && object.position.x < end.x 
      && object.position.z < end.z; 

    return itsin; 
    }); 

    return selected; 
} 

Некоторые предупреждения: насколько я знаю, этот метод можно использовать только, если вы не заботитесь о Y постулировать Ионы И ваш выбор является основным прямоугольником.

Мой 2c

+0

Мне интересно о реализации 'selectCharactersInZone (начинается, заканчивается)'; выполняете ли вы ВСЕ ВСЕ объект и возвращаете только те, которые находятся в выбранной области? – eldad87

+0

Я использую эту функцию для поиска объектов в выбранной области, так что да, я повторяю все ** выбираемые объекты ** - в моем случае несколько символов - так что это достаточно быстро. На мой взгляд, вам решать итерации над приличным количеством объектов и использовать достаточно простой тест «найти». Я отредактировал ответ с помощью функции 'selectCharactersInZone (начинается, заканчивается)'. – vincent

Смежные вопросы