// // WandBehaviorJ3D.java // /* VisAD system for interactive analysis and visualization of numerical data. Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and Tommy Jasmin. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ package visad.java3d; import visad.*; import javax.media.j3d.*; import javax.vecmath.*; import java.util.*; /** WandBehaviorJ3D is the VisAD class for wand behaviors for Java3D it works with ImmersaDeskDisplayRendererJ3D */ /* extend MouseBehaviorJ3D to inherit multiply_matrix, make_matrix, etc */ public class WandBehaviorJ3D extends MouseBehaviorJ3D implements Runnable, MouseBehavior { /** DisplayRenderer for Display */ ImmersaDeskDisplayRendererJ3D display_renderer; DisplayImpl display; private Thread wandThread; // use vpTrans for head motion from tracker, // and for left-button wand motion private TransformGroup vpTrans; // initial Transform3D from vpTrans private Transform3D init_trans; private float[] head_position = new float[3]; private float[] wand_position = new float[3]; private float[] wand_vector = new float[3]; // integral of wand_vector during left == true private float[] travel_position = new float[3]; // wand button states private boolean left = false, center = false, right = false; // previous right wand button state private boolean last_right; private DataRenderer direct_renderer = null; private TrackdJNI hack; public WandBehaviorJ3D(ImmersaDeskDisplayRendererJ3D r, int tracker_shmkey, int controller_shmkey) throws VisADException { super(); display_renderer = r; display = display_renderer.getDisplay(); hack = new TrackdJNI(tracker_shmkey, controller_shmkey); last_right = false; } /* override MouseBehaviorJ3D.initialize() to do start Thread */ public void initialize() { wandThread = new Thread(this); wandThread.start(); vpTrans = display_renderer.getViewTrans(); init_trans = new Transform3D(); vpTrans.getTransform(init_trans); } /* override MouseBehaviorJ3D.processStimulus() to do nothing */ public void processStimulus(Enumeration criteria) { } public void stop() { wandThread = null; } // QUESTION? values of these BOLD variables QUESTION? private static int DELAY = 50; // ms private static int NSENSORS = 4; private static int NBUTTONS = 3; // sensor numbers private static int HEAD = 0; private static int WAND = 1; // button numbers private static int LEFT = 0; private static int CENTER = 1; private static int RIGHT = 2; // angle numbers private static int ELEVATION = 0; private static int AZIMUTH = 1; private static int ROLL = 2; // graphics distance (feet?) per second private float TRAVEL_SPEED = 4.0f; // scale factors for head / wand translation (negative?) // NOTE - you may need to adjust these for your ImmersaDesk private float HEAD_SCALE = 0.10f; private float WAND_SCALE = 1.5f; // offsets for head / wand translation // NOTE - you may need to adjust these for your ImmersaDesk private float HEADX_OFFSET = 0.0f; private float HEADY_OFFSET = -3.0f; private float HEADZ_OFFSET = 5.0f; private float WANDX_OFFSET = -0.5f; private float WANDY_OFFSET = -2.0f; private float WANDZ_OFFSET = 0.0f; // length of direct manipulation ray private float RAY_LENGTH = 2.0f; public void run() { Thread me = Thread.currentThread(); int[] number_of_sensors = new int[1]; float[] sensor_positions = new float[NSENSORS * 3]; float[] sensor_angles = new float[NSENSORS * 3]; float[] sensor_matrices = new float[NSENSORS * 4 * 4]; int[] number_of_buttons = new int[1]; int[] button_states = new int[NBUTTONS]; int nprint = 1000 / DELAY; travel_position[0] = 0.0f; travel_position[1] = 0.0f; travel_position[2] = 0.0f; while (wandThread == me) { try { synchronized (this) { wait(DELAY); } } catch(InterruptedException e) { // control doesn't normally come here } number_of_sensors[0] = NSENSORS; number_of_buttons[0] = NBUTTONS; hack.getTrackd(number_of_sensors, sensor_positions, sensor_angles, sensor_matrices, number_of_buttons, button_states); /* nprint--; if (nprint <= 0) { nprint = 1000 / DELAY; for (int i=0; i<number_of_sensors[0]; i++) { int i3 = i * 3; System.out.println("sensor " + i + " " + sensor_positions[i3] + " " + sensor_positions[i3 + 1] + " " + sensor_positions[i3 + 2] + " " + sensor_angles[i3] + " " + sensor_angles[i3 + 1] + " " + sensor_angles[i3 + 2]); } System.out.println(number_of_buttons[0] + " buttons: " + button_states[0] + " " + button_states[1] + " " + button_states[2]); } */ last_right = right; left = (button_states[LEFT] != 0); center = (button_states[CENTER] != 0); right = (button_states[RIGHT] != 0); head_position[0] = sensor_positions[3 * HEAD]; head_position[1] = sensor_positions[3 * HEAD + 1]; head_position[2] = sensor_positions[3 * HEAD + 2]; wand_position[0] = sensor_positions[3 * WAND]; wand_position[1] = sensor_positions[3 * WAND + 1]; wand_position[2] = sensor_positions[3 * WAND + 2]; float elevation = (float) (sensor_angles[3 * WAND + ELEVATION] * Data.DEGREES_TO_RADIANS); float azimuth = (float) (sensor_angles[3 * WAND + AZIMUTH] * Data.DEGREES_TO_RADIANS); float roll = (float) (sensor_angles[3 * WAND + ROLL] * Data.DEGREES_TO_RADIANS); // QUESTION? angles all 0.0 == wand pointed forward QUESTION? // start with unit vector in (0, 0, 0) wand orientation float x = 0.0f; float y = 0.0f; float z = -1.0f; // QUESTION? is order of rotations: x, y then z QUESTION? // rotate elevation float xx = x; float yy = (float) (Math.cos(elevation) * y - Math.sin(elevation) * z); float zz = (float) (Math.cos(elevation) * z + Math.sin(elevation) * y); // rotate azimuth x = (float) (Math.cos(azimuth) * xx + Math.sin(azimuth) * zz); y = yy; z = (float) (Math.cos(azimuth) * zz - Math.sin(azimuth) * xx); // don't rotate roll wand_vector[0] = x; wand_vector[1] = y; wand_vector[2] = z; // move along wand direction when left button pressed if (left) { float increment = TRAVEL_SPEED * DELAY / 1000.0f; travel_position[0] += increment * wand_vector[0]; travel_position[1] += increment * wand_vector[1]; travel_position[2] += increment * wand_vector[2]; } // change vpTrans based on head_position and travel_position double headx = HEAD_SCALE * (head_position[0] + travel_position[0] + HEADX_OFFSET); double heady = HEAD_SCALE * (head_position[1] + travel_position[1] + HEADY_OFFSET); double headz = HEAD_SCALE * (head_position[2] + travel_position[2] + HEADZ_OFFSET); double[] matrix = MouseBehaviorJ3D.static_make_matrix(0.0, 0.0, 0.0, 1.0, headx, heady, headz); Transform3D temp = new Transform3D(init_trans); Transform3D tm = new Transform3D(matrix); temp.mul(tm); vpTrans.setTransform(temp); Transform3D t3d = new Transform3D(); display_renderer.getTrans().getTransform(t3d); // QUESTION? + or - travel_position QUESTION? float wandx = WAND_SCALE * (wand_position[0] + travel_position[0] + WANDX_OFFSET); float wandy = WAND_SCALE * (wand_position[1] + travel_position[1] + WANDY_OFFSET); float wandz = WAND_SCALE * (wand_position[2] + travel_position[2] + WANDZ_OFFSET); Point3f p3f = new Point3f(wandx, wandy, wandz); t3d.transform(p3f); wandx = p3f.x; wandy = p3f.y; wandz = p3f.z; display_renderer.setCursorOn(center); if (center) { display_renderer.setCursorLoc(wandx, wandy, wandz); } if (right && display_renderer.anyDirects()) { float wand_endx = wandx + WAND_SCALE * (RAY_LENGTH * wand_vector[0]); float wand_endy = wandy + WAND_SCALE * (RAY_LENGTH * wand_vector[1]); float wand_endz = wandz + WAND_SCALE * (RAY_LENGTH * wand_vector[2]); p3f = new Point3f(wand_endx, wand_endy, wand_endz); t3d.transform(p3f); wand_endx = p3f.x; wand_endy = p3f.y; wand_endz = p3f.z; float[] ray_verts = {wandx, wandy, wandz, wand_endx, wand_endy, wand_endz}; display_renderer.setRayOn(true, ray_verts); VisADRay direct_ray = new VisADRay(); direct_ray.position[0] = wandx; direct_ray.position[1] = wandy; direct_ray.position[2] = wandz; // QUESTION? should ray.vector simply equal wand_vector QUESTION? direct_ray.vector[0] = wand_vector[0]; direct_ray.vector[1] = wand_vector[1]; direct_ray.vector[2] = wand_vector[2]; if (!last_right) { // first point direct_renderer = display_renderer.findDirect(direct_ray, 0); if (direct_renderer != null) { display_renderer.setDirectOn(true); direct_renderer.drag_direct(direct_ray, true, 0); } } else { if (direct_renderer != null) { direct_renderer.drag_direct(direct_ray, false, 0); } } } else { // !right || !display_renderer.anyDirects() display_renderer.setRayOn(false, null); direct_renderer = null; } } // end while (wandThread == me) } }