Докуметация Cтарт Статьи Форум Лента Вход
Не официальное русскоязычное сообщество
Главная
    Статьи
        Ввод
            Выделение объектов мышью

Выделение объектов мышью

Опубликованно: 20.08.2015, 0:27
Последняя редакция, AdiDOS: 06.05.2017 23:50

В некоторых случаях разработчики сталкиваются с такой задачей как выделение объектов мышью, в моей практике был такой период времени когда приходилось реализовывать выбор объектов ручками а точней на чистой библиотеке OpenGL , я вам честно скажу это не очень легкий процесс так вот с помощью библиотеки jme3 это намного проще.

Немного кода.

package test;

import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.collision.CollisionResults;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.shape.Sphere;
import com.jme3.util.SkyFactory;

public class MouseTest extends SimpleApplication implements ActionListener {
 Node scene = new Node();
 public static void main(String[] args) {
   new MouseTest().start();

 }

 @Override
 public void simpleInitApp() {

   flyCam.setMoveSpeed(30);
   flyCam.setDragToRotate(true);
   inputManager.addMapping("select", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
   inputManager.addListener(this, "select");

   //Хаотичное добавление моделей на сцену
   for (int i = 0; i < 100; i++) {
     Sphere sphere = new Sphere(32, 32, 4, false, true);
     Geometry geom = new Geometry("Sphere"+i, sphere);
     Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
     mat.setBoolean("UseMaterialColors", true);
     mat.setColor("Diffuse",ColorRGBA.randomColor());
     mat.setColor("Specular",ColorRGBA.randomColor());
     mat.setFloat("Shininess", 64f);
     geom.setMaterial(mat);
     geom.setLocalTranslation(FastMath.rand.nextFloat()*120, FastMath.rand.nextFloat()*120, FastMath.rand.nextFloat()*120);
     scene.attachChild(geom);

 }
   rootNode.attachChild(scene);
   createLight();
   createSkyBox();
 }

 @Override
 public void onAction(String name, boolean isPressed, float tpf) {
   if (name.equals("select") && isPressed) {

     CollisionResults results = new CollisionResults();
     // Позиция курсора мыши в оконных координатах
     Vector2f click2d = inputManager.getCursorPosition();
     // Позиция курсора на сцене относительно камеры (3D)
     Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
     // Направление камеры
     Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
     // Луч пересечения
     Ray ray = new Ray(click3d, dir);

     scene.collideWith(ray, results);

     System.out.println("Количество пересеченных моделе - " + results.size());
   for (int i = 0; i < results.size() - 1; i++) {
     float dist = results.getCollision(i).getDistance(); // Растояние до пересечения
     Vector3f cp = results.getCollision(i).getContactPoint(); // Точка пересечения
     Geometry selModel = results.getCollision(i).getGeometry();

     System.out.println("Имя модели - " + selModel.getName());
     System.out.println("Дистанция до модели - " + dist);
     System.out.println("Координаты пересечения - " + cp);
     //Выделяем первую модель
     drawBoundingBox(results.getCollision(0).getGeometry(), 0.5f, 2f);
   }

 }

 }

 //Освещение
 private void createLight() {
   DirectionalLight sun = new DirectionalLight();
   sun.setColor(ColorRGBA.White);
   sun.setDirection(new Vector3f(0, 1, 0).normalizeLocal());
   rootNode.addLight(sun);
 }

 // Небо
 private void createSkyBox() {
   rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
 }
 //Метод прорисовывает грани модели
 //offset отступ линий от модели
 //lineLen длинна линий
 private void drawBoundingBox(Geometry geom, float offset, float lineLen){
   geom.updateModelBound(); //Пересчитывание коробки
   BoundingBox bb = (BoundingBox)geom.getModelBound();

   Vector3f cent = bb.getCenter(); //Координаты центра модели
   float xex = bb.getXExtent(); //Размер Модели по оси Х
   float xey = bb.getYExtent(); //Размер Модели по оси Y
   float xez = bb.getXExtent(); //Размер Модели по оси Z

   Mesh mesh = new Mesh();
   mesh.setMode(Mesh.Mode.Lines);

   float[] lines = new float[]{
                       //Передний правый верхний
                       cent.x+xex + offset, cent.y+xey+offset, cent.z+xez+offset
                       ,cent.x+xex + offset- lineLen, cent.y+xey+offset, cent.z+xez+offset
                       ,cent.x+xex + offset, cent.y+xey+offset- lineLen, cent.z+xez+offset
                       ,cent.x+xex + offset, cent.y+xey+offset, cent.z+xez+offset- lineLen
                       //Передний левый верхний
                       ,cent.x-xex - offset, cent.y+xey+offset, cent.z+xez+offset
                       ,cent.x-xex - offset+lineLen, cent.y+xey+offset, cent.z+xez+offset
                       ,cent.x-xex - offset, cent.y+xey+offset-lineLen, cent.z+xez+offset
                       ,cent.x-xex - offset, cent.y+xey+offset, cent.z+xez+offset-lineLen
                       //Передний правый нижний
                       ,cent.x+xex+offset, cent.y-xey-offset, cent.z+xez+offset
                       ,cent.x+xex+offset-lineLen, cent.y-xey-offset, cent.z+xez+offset
                       ,cent.x+xex+offset, cent.y-xey-offset+lineLen, cent.z+xez+offset
                       ,cent.x+xex+offset, cent.y-xey-offset, cent.z+xez+offset-lineLen
                       //Передний левый нижний
                       ,cent.x-xex-offset, cent.y-xey-offset, cent.z+xez+offset
                       ,cent.x-xex-offset+lineLen, cent.y-xey-offset, cent.z+xez+offset
                       ,cent.x-xex-offset, cent.y-xey-offset+lineLen, cent.z+xez+offset
                       ,cent.x-xex-offset, cent.y-xey-offset, cent.z+xez+offset-lineLen
                       //Задний правый верхний
                       ,cent.x+xex + offset, cent.y+xey+offset, cent.z-xez-offset
                       ,cent.x+xex + offset- lineLen, cent.y+xey+offset, cent.z-xez-offset
                       ,cent.x+xex + offset, cent.y+xey+offset- lineLen, cent.z-xez-offset
                       ,cent.x+xex + offset, cent.y+xey+offset, cent.z-xez-offset+lineLen
                       //Задний левый верхний
                       ,cent.x-xex - offset, cent.y+xey+offset, cent.z-xez-offset
                       ,cent.x-xex - offset+lineLen, cent.y+xey+offset, cent.z-xez-offset
                       ,cent.x-xex - offset, cent.y+xey+offset-lineLen, cent.z-xez-offset
                       ,cent.x-xex - offset, cent.y+xey+offset, cent.z-xez-offset+lineLen
                       //Задний правый нижний
                       ,cent.x+xex+offset, cent.y-xey-offset, cent.z-xez-offset
                       ,cent.x+xex+offset-lineLen, cent.y-xey-offset, cent.z-xez-offset
                       ,cent.x+xex+offset, cent.y-xey-offset+lineLen, cent.z-xez-offset
                       ,cent.x+xex+offset, cent.y-xey-offset, cent.z-xez-offset+lineLen
                       //Задний левый нижний
                       ,cent.x-xex-offset, cent.y-xey-offset, cent.z-xez-offset
                       ,cent.x-xex-offset+lineLen, cent.y-xey-offset, cent.z-xez-offset
                       ,cent.x-xex-offset, cent.y-xey-offset+lineLen, cent.z-xez-offset
                       ,cent.x-xex-offset, cent.y-xey-offset, cent.z-xez-offset+lineLen};

   //Установка вершинного буфера
   mesh.setBuffer(VertexBuffer.Type.Position, 3,lines);
   //Установка индексного буфера
   mesh.setBuffer(VertexBuffer.Type.Index, 2, new int[]{
                             0,1, 0,2, 0,3, 4,5 ,4,6 ,4,7
                             ,8,9 ,8,10 ,8,11 ,12,13 ,12,14 ,12,15
                             ,16,17 ,16,18 ,16,19 ,20,21 ,20,22 ,20,23
                             ,24,25 ,24,26 ,24,27 ,28,29 ,28,30 ,28,31});
   Geometry geomLines = new Geometry("lines", mesh);
   geomLines.setLocalTranslation(geom.getLocalTranslation());
   Material matlines = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
   matlines.setColor("Color", ColorRGBA.Red);
   geomLines.setMaterial(matlines);
   rootNode.attachChild(geomLines);

 }
}
 

2

Теория

Я знаю два самых оптимальных способов  по выбору объектов мышью  , первый способ вычислялся по цвету суть данного способа такой берем объекты красим все уникальным цветом рисуем их в заднем буфере и вычисляем цвет пикселя под мышью потом сравниваем  цвет с нашими объектами , второй способ по пересечению луча суть такова просчитываем координаты мыши в 3D пространстве и относительно камеры посылаем его в глубь сцены далее для оптимизации по Bounding Box вычисляем какие объекты пересеклись  ну и  в этих объектах вычисляем точку пересечения , так вот  я  и приведу пример вычисления по пересечению луча в Jme3.

 О коде

В данном коде я привел пример выборки объектов мышью и для наглядности решил позаимствовать рамкой из урока  Bounding Box ,  ну собственно все относительно просто сначала добавляем обработчик событий мыши  с помощью функции.

 inputManager.addMapping("select", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));

Константа MouseInput.BUTTON_LEFT говорит что мы будем использовать левую кнопку мыши, обработчик событий onAction вызывается каждый клик нажатия мыши, там и определяется объекты ну далее выборка по этапам.

CollisionResults results = new CollisionResults();
 // Позиция курсора мыши в оконных координатах
 Vector2f click2d = inputManager.getCursorPosition();
 // Позиция курсора на сцене относительно камеры (3D)
 Vector3f click3d = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 0f).clone();
 // Направление камеры
 Vector3f dir = cam.getWorldCoordinates(new Vector2f(click2d.x, click2d.y), 1f)
.subtractLocal(click3d).normalizeLocal();
 // Луч пересечения
 Ray ray = new Ray(click3d, dir);

 scene.collideWith(ray, results);

 System.out.println("Количество пересеченных моделе - " + results.size());
 for (int i = 0; i < results.size() - 1; i++) {

CollisionResults  — Класс   хранит все результаты пересечения

getCursorPosition() — Метод возвращает оконные координаты мыши

getWorldCoordinates() — Метод вычисляет координаты в трех мерной сцене относительно камеры

Ray — Класс хранит в себе начальные и конечные координаты луча

collideWith — Именно этот метод вычисляет пересечения в данном узле(Node) и возвращает результат во втором параметре

А далее все просто в цикле обходим все объекты с помощью функции  

results.getCollision(i) и манипулируем с ними как душа желает.

Ну собственно что я хотел добавить  лучь может пересекать не c одним объектом и вам будут возвращаться все пересекаемые объекты , разработчики удобно сделали все сортируются в порядке убывания  так вот если вам нужен самый ближний объект то  он будет расположен под индексом 0 а самый дальний можно вызвать так.

results.getCollision(results.size()-1).getGeometry();

Спасибо за внимание!!

Добавить комментарий

Содержание

jMonkeyEngine.ru © 2017. Все права сохранены.