From 2c429743291bfeec573f72ee128ae9b46d60008d Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Thu, 14 Aug 2014 19:30:27 -0700 Subject: [PATCH] getting pick ray --- src/cad/Cad.java | 324 +--------------- src/cad/Cad3.java | 349 +++++++++++++++++ src/cad/fx/AppCtrl.java | 2 +- src/cad/fx/CSGMesh.java | 2 +- src/cad/fx/CSGNode.java | 15 +- src/cad/fx/CadContext.java | 38 +- src/cad/fx/{Plane.java => Polygon.java} | 41 +- src/cad/fx/Sketch.java | 4 +- src/cad/fx/Utils3D.java | 18 +- src/cad/gl/BBox.java | 477 ++++++++++++++++++++++++ src/cad/gl/Camera.java | 16 + src/cad/gl/CompiledNode.java | 20 + src/cad/gl/MeshNode.java | 230 ++++++++++++ src/cad/gl/Node.java | 27 ++ src/cad/gl/PickRay.java | 152 ++++++++ src/cad/gl/Scene.java | 318 ++++++++++++++++ src/cad/math/Vector.java | 39 +- 17 files changed, 1684 insertions(+), 388 deletions(-) create mode 100644 src/cad/Cad3.java rename src/cad/fx/{Plane.java => Polygon.java} (81%) create mode 100644 src/cad/gl/BBox.java create mode 100644 src/cad/gl/Camera.java create mode 100644 src/cad/gl/CompiledNode.java create mode 100644 src/cad/gl/MeshNode.java create mode 100644 src/cad/gl/Node.java create mode 100644 src/cad/gl/PickRay.java create mode 100644 src/cad/gl/Scene.java diff --git a/src/cad/Cad.java b/src/cad/Cad.java index 02951e4a..c553c1ac 100644 --- a/src/cad/Cad.java +++ b/src/cad/Cad.java @@ -1,77 +1,34 @@ package cad; -import cad.fx.Plane; import cad.fx.Utils3D; -import cad.math.Vector; +import cad.gl.MeshNode; +import cad.gl.Scene; import com.jogamp.newt.opengl.GLWindow; -import com.jogamp.opengl.util.gl2.GLUT; -import gnu.trove.map.TIntObjectMap; -import gnu.trove.map.hash.TIntObjectHashMap; -import javax.media.opengl.GL; -import javax.media.opengl.GL2; -import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; -import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; -import javax.media.opengl.Threading; -import javax.media.opengl.glu.GLU; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; - -public class Cad implements GLEventListener, com.jogamp.newt.event.MouseListener { +public class Cad { static { GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations } - public static List initObjects = Utils3D.createCube(1); - - static TIntObjectMap scene = new TIntObjectHashMap<>(); - - private static GLWindow window; - - ExecutorService updater = Executors.newSingleThreadExecutor(); - public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException { - // Get the default OpenGL profile, reflecting the best for your running platform GLProfile glp = GLProfile.getDefault(); - // Specifies a set of OpenGL capabilities, based on your profile. GLCapabilities caps = new GLCapabilities(glp); - // Create the OpenGL rendering canvas - window = GLWindow.create(caps); - - // Create a animator that drives canvas' display() at the specified FPS. -// final FPSAnimator animator = new FPSAnimator(window, 60, true); -//// final Animator animator = new Animator(window); -// window.addWindowListener(new com.jogamp.newt.event.WindowAdapter() { -// @Override -// public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent arg0) { -// // Use a dedicate thread to run the stop() to ensure that the -// // animator stops before program exits. -// new Thread() { -// @Override -// public void run() { -// if (animator.isStarted()) -// animator.stop(); // stop the animator loop -// } -// }.start(); -// } -// }); - - - window.addGLEventListener(new Cad()); + GLWindow window = GLWindow.create(caps); + Scene scene = new Scene(window); + scene.addNode(new MeshNode(Utils3D.createCube(1))); window.setSize(640, 480); window.setTitle("CAD"); window.setVisible(true); + Executors.newSingleThreadExecutor().execute(() -> { Object monitor = new Object(); while (true) { @@ -83,272 +40,5 @@ public class Cad implements GLEventListener, com.jogamp.newt.event.MouseListener } } }); - -// animator.start(); - } - - float red[] = {0.8f, 0.1f, 0.0f, 1.0f}; - float green[] = {0.0f, 0.8f, 0.2f, 1.0f}; - float blue[] = {0.2f, 0.2f, 1.0f, 1.0f}; - float white[] = {1.0f, 1.0f, 1.0f}; - - - private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; - - private int prevMouseX, prevMouseY; - private boolean mouseRButtonDown = false; - - public void init(GLAutoDrawable drawable) { - // Use debug pipeline - // drawable.setGL(new DebugGL(drawable.getGL())); - - GL2 gl = drawable.getGL().getGL2(); - - System.err.println("INIT GL IS: " + gl.getClass().getName()); - - System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); - - gl.setSwapInterval(0); - - float pos0[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, pos0, 0); - - gl.glEnable(GL2.GL_CULL_FACE); - gl.glEnable(GL2.GL_DEPTH_TEST); - gl.glEnable(GL2.GL_LIGHTING); - gl.glEnable(GL2.GL_LIGHT0); - - initNodes(gl); - - gl.glEnable(GL2.GL_NORMALIZE); - - if (drawable instanceof GLWindow) { - GLWindow awtDrawable = (GLWindow) drawable; - awtDrawable.addMouseListener(this); - } - } - - private void initNodes(GL2 gl) { - for (Plane plane : initObjects) { - int id = gl.glGenLists(1); - scene.put(id, plane); - gl.glNewList(id, GL2.GL_COMPILE); - - //http://devernay.free.fr/cours/opengl/materials.html -// float[] amb = {0f, 0f, 0f, 0f}; -// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT, amb, 0); - - float[] diff = {0.6901961f, 0.76862746f, 0.87058824f}; - gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_DIFFUSE, diff, 0); - -// float[] spec = {0f, 0f, 0f}; -// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, spec, 0); -// float shine = 0.6f; -// gl.glMaterialf(GL2.GL_FRONT, GL2.GL_SHININESS, shine * 128.0f); -// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, blue, 0); - - gl.glShadeModel(GL2.GL_SMOOTH); - - gl.glEnable(GL2.GL_LIGHTING); - - gl.glBegin(GL2.GL_TRIANGLES); - gl.glNormal3d(plane.normal.x, plane.normal.y, plane.normal.z); //very important!! - for (Vector[] tr : plane.getTriangles()) { - gl.glVertex3d(tr[0].x, tr[0].y, tr[0].z); - gl.glVertex3d(tr[1].x, tr[1].y, tr[1].z); - gl.glVertex3d(tr[2].x, tr[2].y, tr[2].z); - } - gl.glEnd(); - - gl.glEndList(); - } - for (Plane plane : initObjects) { - int id = gl.glGenLists(1); - scene.put(id, plane); - gl.glNewList(id, GL2.GL_COMPILE); - gl.glShadeModel(GL2.GL_FLAT); - gl.glLineWidth(1.5f); - gl.glColor3f(255.0f, 255.0f, 255.0f); - gl.glDisable(GL2.GL_LIGHTING); - - gl.glNormal3d(plane.normal.x, plane.normal.y, plane.normal.z); - for (int i = 0; i < plane.shell.size(); i++) { - gl.glBegin(GL2.GL_LINES); - Vector a = Plane.get(plane.shell, i); - Vector b = Plane.get(plane.shell, i + 1); - gl.glVertex3d(a.x, a.y, a.z); - gl.glVertex3d(b.x, b.y, b.z); - gl.glEnd(); - } - gl.glEndList(); - } - } - - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - GL2 gl = drawable.getGL().getGL2(); - - float h = (float) height / (float) width; - - gl.glMatrixMode(GL2.GL_PROJECTION); - gl.glLoadIdentity(); - gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f); - gl.glMatrixMode(GL2.GL_MODELVIEW); - gl.glLoadIdentity(); - gl.glTranslatef(0.0f, 0.0f, -40.0f); - } - - public void dispose(GLAutoDrawable drawable) { - System.out.println("Gears.dispose: " + drawable); - } - - public void display(GLAutoDrawable drawable) { - GL2 gl = drawable.getGL().getGL2(); - - gl.glClearColor(0.5019608f, 0.5019608f, 0.5019608f, 0f); - - gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); - - gl.glPushMatrix(); - gl.glScalef(scale, scale, scale); - - gl.glPushMatrix(); - - gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); - gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); - gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); - - for (int id : scene.keys()) { - gl.glCallList(id); - } - -// -// gl.glPushMatrix(); -// gl.glScalef(3,3,3); -// gl.glBegin(GL2.GL_QUADS); -// gl.glNormal3f( 0.0F, 0.0F, 1.0F); -// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f(-0.5F, 0.5F, 0.5F); -// gl.glVertex3f(-0.5F,-0.5F, 0.5F); gl.glVertex3f( 0.5F,-0.5F, 0.5F); -// -// gl.glNormal3f( 0.0F, 0.0F,-1.0F); -// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f(-0.5F, 0.5F,-0.5F); -// gl.glVertex3f( 0.5F, 0.5F,-0.5F); gl.glVertex3f( 0.5F,-0.5F,-0.5F); -// -// gl.glNormal3f( 0.0F, 1.0F, 0.0F); -// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f( 0.5F, 0.5F,-0.5F); -// gl.glVertex3f(-0.5F, 0.5F,-0.5F); gl.glVertex3f(-0.5F, 0.5F, 0.5F); -// -// gl.glNormal3f( 0.0F,-1.0F, 0.0F); -// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f( 0.5F,-0.5F,-0.5F); -// gl.glVertex3f( 0.5F,-0.5F, 0.5F); gl.glVertex3f(-0.5F,-0.5F, 0.5F); -// -// gl.glNormal3f( 1.0F, 0.0F, 0.0F); -// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f( 0.5F,-0.5F, 0.5F); -// gl.glVertex3f( 0.5F,-0.5F,-0.5F); gl.glVertex3f( 0.5F, 0.5F,-0.5F); -// -// gl.glNormal3f(-1.0F, 0.0F, 0.0F); -// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f(-0.5F,-0.5F, 0.5F); -// gl.glVertex3f(-0.5F, 0.5F, 0.5F); gl.glVertex3f(-0.5F, 0.5F,-0.5F); -// gl.glEnd(); -// gl.glPopMatrix(); - - - gl.glPopMatrix(); - gl.glPopMatrix(); - } - - @Override - public void mouseClicked(com.jogamp.newt.event.MouseEvent e) { - } - - @Override - public void mouseEntered(com.jogamp.newt.event.MouseEvent e) { - } - - @Override - public void mouseExited(com.jogamp.newt.event.MouseEvent e) { - } - - @Override - public void mousePressed(com.jogamp.newt.event.MouseEvent e) { - prevMouseX = e.getX(); - prevMouseY = e.getY(); - if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { - mouseRButtonDown = true; - } - } - - @Override - public void mouseReleased(com.jogamp.newt.event.MouseEvent e) { - if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { - mouseRButtonDown = false; - } - - } - - @Override - public void mouseMoved(com.jogamp.newt.event.MouseEvent e) { - } - - @Override - public void mouseDragged(com.jogamp.newt.event.MouseEvent e) { - int x = e.getX(); - int y = e.getY(); - - int width = window.getWidth(); - int height = window.getHeight(); - - float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) width); - float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) height); - - prevMouseX = x; - prevMouseY = y; - - view_rotx += thetaX; - view_roty += thetaY; - - update(window::display); - } - - - - volatile boolean updating = false; - - private void update(Runnable op) { - if (updating) { - return; - } - - Threading.invokeOnOpenGLThread(false, new Runnable() { - @Override - public void run() { - try { - updating = true; - - op.run(); - } finally { - updating = false; - } - } - }); - -// updater.execute(() -> { -// try { -// updating = true; -// -// op.run(); -// } finally { -// updating = false; -// } -// }); - } - - final double SCALE_DELTA = 1.1; - float scale = 1; - - @Override - public void mouseWheelMoved(com.jogamp.newt.event.MouseEvent e) { - double scaleFactor = e.getRotation()[1] > 0 ? SCALE_DELTA : 1 / SCALE_DELTA; - scale *= scaleFactor; - update(window::display); } } diff --git a/src/cad/Cad3.java b/src/cad/Cad3.java new file mode 100644 index 00000000..7fdf550d --- /dev/null +++ b/src/cad/Cad3.java @@ -0,0 +1,349 @@ +package cad; + +import cad.fx.Polygon; +import cad.fx.Utils3D; +import cad.math.Vector; +import com.jogamp.newt.opengl.GLWindow; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; + +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.Threading; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class Cad3 implements GLEventListener, com.jogamp.newt.event.MouseListener { + + + static { + GLProfile.initSingleton(); // The method allows JOGL to prepare some Linux-specific locking optimizations + } + + public static List initObjects = Utils3D.createCube(1); + + static TIntObjectMap scene = new TIntObjectHashMap<>(); + + private static GLWindow window; + + ExecutorService updater = Executors.newSingleThreadExecutor(); + + public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException { + + + // Get the default OpenGL profile, reflecting the best for your running platform + GLProfile glp = GLProfile.getDefault(); + // Specifies a set of OpenGL capabilities, based on your profile. + GLCapabilities caps = new GLCapabilities(glp); + // Create the OpenGL rendering canvas + window = GLWindow.create(caps); + + // Create a animator that drives canvas' display() at the specified FPS. +// final FPSAnimator animator = new FPSAnimator(window, 60, true); +//// final Animator animator = new Animator(window); +// window.addWindowListener(new com.jogamp.newt.event.WindowAdapter() { +// @Override +// public void windowDestroyNotify(com.jogamp.newt.event.WindowEvent arg0) { +// // Use a dedicate thread to run the stop() to ensure that the +// // animator stops before program exits. +// new Thread() { +// @Override +// public void run() { +// if (animator.isStarted()) +// animator.stop(); // stop the animator loop +// } +// }.start(); +// } +// }); + + + window.addGLEventListener(new Cad3()); + + window.setSize(640, 480); + window.setTitle("CAD"); + window.setVisible(true); + + Executors.newSingleThreadExecutor().execute(() -> { + Object monitor = new Object(); + while (true) { + try { + synchronized (monitor) { + monitor.wait(); + } + } catch (InterruptedException e) { + } + } + }); + +// animator.start(); + } + + float red[] = {0.8f, 0.1f, 0.0f, 1.0f}; + float green[] = {0.0f, 0.8f, 0.2f, 1.0f}; + float blue[] = {0.2f, 0.2f, 1.0f, 1.0f}; + float white[] = {1.0f, 1.0f, 1.0f}; + + + private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; + + private int prevMouseX, prevMouseY; + private boolean mouseRButtonDown = false; + + public void init(GLAutoDrawable drawable) { + // Use debug pipeline + // drawable.setGL(new DebugGL(drawable.getGL())); + + GL2 gl = drawable.getGL().getGL2(); + + System.err.println("INIT GL IS: " + gl.getClass().getName()); + + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + + gl.setSwapInterval(0); + + float pos0[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, pos0, 0); + + gl.glEnable(GL2.GL_CULL_FACE); + gl.glEnable(GL2.GL_DEPTH_TEST); + gl.glEnable(GL2.GL_LIGHTING); + gl.glEnable(GL2.GL_LIGHT0); + + initNodes(gl); + + gl.glEnable(GL2.GL_NORMALIZE); + + if (drawable instanceof GLWindow) { + GLWindow awtDrawable = (GLWindow) drawable; + awtDrawable.addMouseListener(this); + } + } + + private void initNodes(GL2 gl) { + for (Polygon poly : initObjects) { + int id = gl.glGenLists(1); + scene.put(id, poly); + gl.glNewList(id, GL2.GL_COMPILE); + + //http://devernay.free.fr/cours/opengl/materials.html +// float[] amb = {0f, 0f, 0f, 0f}; +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT, amb, 0); + + float[] diff = {0.6901961f, 0.76862746f, 0.87058824f}; + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_DIFFUSE, diff, 0); + +// float[] spec = {0f, 0f, 0f}; +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, spec, 0); +// float shine = 0.6f; +// gl.glMaterialf(GL2.GL_FRONT, GL2.GL_SHININESS, shine * 128.0f); +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, blue, 0); + + gl.glShadeModel(GL2.GL_SMOOTH); + + gl.glEnable(GL2.GL_LIGHTING); + + gl.glBegin(GL2.GL_TRIANGLES); + gl.glNormal3d(poly.normal.x, poly.normal.y, poly.normal.z); //very important!! + for (Vector[] tr : poly.getTriangles()) { + gl.glVertex3d(tr[0].x, tr[0].y, tr[0].z); + gl.glVertex3d(tr[1].x, tr[1].y, tr[1].z); + gl.glVertex3d(tr[2].x, tr[2].y, tr[2].z); + } + gl.glEnd(); + + gl.glEndList(); + } + for (Polygon poly : initObjects) { + int id = gl.glGenLists(1); + scene.put(id, poly); + gl.glNewList(id, GL2.GL_COMPILE); + gl.glShadeModel(GL2.GL_FLAT); + gl.glLineWidth(1.5f); + gl.glColor3f(255.0f, 255.0f, 255.0f); + gl.glDisable(GL2.GL_LIGHTING); + + gl.glNormal3d(poly.normal.x, poly.normal.y, poly.normal.z); + for (int i = 0; i < poly.shell.size(); i++) { + gl.glBegin(GL2.GL_LINES); + Vector a = Polygon.get(poly.shell, i); + Vector b = Polygon.get(poly.shell, i + 1); + gl.glVertex3d(a.x, a.y, a.z); + gl.glVertex3d(b.x, b.y, b.z); + gl.glEnd(); + } + gl.glEndList(); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2 gl = drawable.getGL().getGL2(); + + float h = (float) height / (float) width; + + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glFrustum(-1.0f, 1.0f, -h, h, 5.0f, 60.0f); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glTranslatef(0.0f, 0.0f, -40.0f); + } + + public void dispose(GLAutoDrawable drawable) { + System.out.println("Gears.dispose: " + drawable); + } + + public void display(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); + + gl.glClearColor(0.5019608f, 0.5019608f, 0.5019608f, 0f); + + gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); + + gl.glPushMatrix(); + gl.glScalef(scale, scale, scale); + + gl.glPushMatrix(); + + gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); + gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); + gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); + + for (int id : scene.keys()) { + gl.glCallList(id); + } + +// +// gl.glPushMatrix(); +// gl.glScalef(3,3,3); +// gl.glBegin(GL2.GL_QUADS); +// gl.glNormal3f( 0.0F, 0.0F, 1.0F); +// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f(-0.5F, 0.5F, 0.5F); +// gl.glVertex3f(-0.5F,-0.5F, 0.5F); gl.glVertex3f( 0.5F,-0.5F, 0.5F); +// +// gl.glNormal3f( 0.0F, 0.0F,-1.0F); +// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f(-0.5F, 0.5F,-0.5F); +// gl.glVertex3f( 0.5F, 0.5F,-0.5F); gl.glVertex3f( 0.5F,-0.5F,-0.5F); +// +// gl.glNormal3f( 0.0F, 1.0F, 0.0F); +// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f( 0.5F, 0.5F,-0.5F); +// gl.glVertex3f(-0.5F, 0.5F,-0.5F); gl.glVertex3f(-0.5F, 0.5F, 0.5F); +// +// gl.glNormal3f( 0.0F,-1.0F, 0.0F); +// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f( 0.5F,-0.5F,-0.5F); +// gl.glVertex3f( 0.5F,-0.5F, 0.5F); gl.glVertex3f(-0.5F,-0.5F, 0.5F); +// +// gl.glNormal3f( 1.0F, 0.0F, 0.0F); +// gl.glVertex3f( 0.5F, 0.5F, 0.5F); gl.glVertex3f( 0.5F,-0.5F, 0.5F); +// gl.glVertex3f( 0.5F,-0.5F,-0.5F); gl.glVertex3f( 0.5F, 0.5F,-0.5F); +// +// gl.glNormal3f(-1.0F, 0.0F, 0.0F); +// gl.glVertex3f(-0.5F,-0.5F,-0.5F); gl.glVertex3f(-0.5F,-0.5F, 0.5F); +// gl.glVertex3f(-0.5F, 0.5F, 0.5F); gl.glVertex3f(-0.5F, 0.5F,-0.5F); +// gl.glEnd(); +// gl.glPopMatrix(); + + + gl.glPopMatrix(); + gl.glPopMatrix(); + } + + @Override + public void mouseClicked(com.jogamp.newt.event.MouseEvent e) { + } + + @Override + public void mouseEntered(com.jogamp.newt.event.MouseEvent e) { + } + + @Override + public void mouseExited(com.jogamp.newt.event.MouseEvent e) { + } + + @Override + public void mousePressed(com.jogamp.newt.event.MouseEvent e) { + prevMouseX = e.getX(); + prevMouseY = e.getY(); + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = true; + } + } + + @Override + public void mouseReleased(com.jogamp.newt.event.MouseEvent e) { + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = false; + } + + } + + @Override + public void mouseMoved(com.jogamp.newt.event.MouseEvent e) { + } + + @Override + public void mouseDragged(com.jogamp.newt.event.MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + + int width = window.getWidth(); + int height = window.getHeight(); + + float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) width); + float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) height); + + prevMouseX = x; + prevMouseY = y; + + view_rotx += thetaX; + view_roty += thetaY; + + update(window::display); + } + + + + volatile boolean updating = false; + + private void update(Runnable op) { + if (updating) { + return; + } + + Threading.invokeOnOpenGLThread(false, new Runnable() { + @Override + public void run() { + try { + updating = true; + + op.run(); + } finally { + updating = false; + } + } + }); + +// updater.execute(() -> { +// try { +// updating = true; +// +// op.run(); +// } finally { +// updating = false; +// } +// }); + } + + final double SCALE_DELTA = 1.1; + float scale = 1; + + @Override + public void mouseWheelMoved(com.jogamp.newt.event.MouseEvent e) { + double scaleFactor = e.getRotation()[1] > 0 ? SCALE_DELTA : 1 / SCALE_DELTA; + scale *= scaleFactor; + update(window::display); + } +} diff --git a/src/cad/fx/AppCtrl.java b/src/cad/fx/AppCtrl.java index d862ec0d..e1218179 100644 --- a/src/cad/fx/AppCtrl.java +++ b/src/cad/fx/AppCtrl.java @@ -36,7 +36,7 @@ public class AppCtrl implements Initializable { } private void setInitObject(Group parent) { - List cube = Utils3D.createCube(100); + List cube = Utils3D.createCube(100); parent.getChildren().addAll(cadContext.toNodes(cube)); // // CSG init = new Cube(100).toCSG().difference(new Cylinder(30, 100, 10).toCSG()); diff --git a/src/cad/fx/CSGMesh.java b/src/cad/fx/CSGMesh.java index 5edfc12b..f2e3a468 100644 --- a/src/cad/fx/CSGMesh.java +++ b/src/cad/fx/CSGMesh.java @@ -6,6 +6,6 @@ import javafx.scene.shape.TriangleMesh; public class CSGMesh extends TriangleMesh { - public final TIntObjectMap polygons = new TIntObjectHashMap<>(); + public final TIntObjectMap polygons = new TIntObjectHashMap<>(); } diff --git a/src/cad/fx/CSGNode.java b/src/cad/fx/CSGNode.java index 7f312cbf..30e35c3e 100644 --- a/src/cad/fx/CSGNode.java +++ b/src/cad/fx/CSGNode.java @@ -1,6 +1,5 @@ package cad.fx; -import eu.mihosoft.vrl.v3d.Polygon; import javafx.scene.Group; import javafx.scene.shape.MeshView; @@ -26,22 +25,22 @@ public class CSGNode extends MeshView { }); } - private void highlight(Polygon poly) { + private void highlight(eu.mihosoft.vrl.v3d.Polygon poly) { System.out.println(poly); } - private void select(Polygon poly) { + private void select(eu.mihosoft.vrl.v3d.Polygon poly) { System.out.println(poly); } - public final Map sketches = new HashMap<>(); + public final Map sketches = new HashMap<>(); - public Sketch getSketch(Plane plane) { - Sketch sketch = sketches.get(plane); + public Sketch getSketch(Polygon poly) { + Sketch sketch = sketches.get(poly); if (sketch == null) { - sketch = new Sketch(plane); + sketch = new Sketch(poly); ((Group) getParent()).getChildren().add(sketch.drawLayer); - sketches.put(plane, sketch); + sketches.put(poly, sketch); } return sketch; } diff --git a/src/cad/fx/CadContext.java b/src/cad/fx/CadContext.java index 5173b81b..d1b449fd 100644 --- a/src/cad/fx/CadContext.java +++ b/src/cad/fx/CadContext.java @@ -72,20 +72,20 @@ public class CadContext { PickResult pickResult = e.getPickResult(); int face = pickResult.getIntersectedFace(); CSGMesh csgMesh = (CSGMesh) csgNode.getMesh(); - Plane plane = csgMesh.polygons.get(face); - System.out.println(plane); - if (plane != null) { + Polygon poly = csgMesh.polygons.get(face); + System.out.println(poly); + if (poly != null) { if (selection != null) { - boolean isSameNode = selection.sameTo(csgNode, plane); + boolean isSameNode = selection.sameTo(csgNode, poly); if (sketcher == null && !isSameNode) { - selection = new Selection(csgNode, plane); + selection = new Selection(csgNode, poly); } if (sketcher != null && isSameNode) { sketcher.addPoint(pickResult.getIntersectedPoint()); } } else { if (sketcher == null) { - selection = new Selection(csgNode, plane); + selection = new Selection(csgNode, poly); } } } @@ -95,7 +95,7 @@ public class CadContext { if (sketcher != null || selection == null) { return; } - sketcher = new Sketcher(selection.csgNode.getSketch(selection.plane)); + sketcher = new Sketcher(selection.csgNode.getSketch(selection.poly)); } public void endSketching() { @@ -111,43 +111,43 @@ public class CadContext { return; } - Sketch sketch = selection.csgNode.getSketch(selection.plane); + Sketch sketch = selection.csgNode.getSketch(selection.poly); Vector dir = sketch.owner.normal.scale(height); for (List polygon : sketch.polygons) { if (polygon.isEmpty()) { continue; } - Plane plane = new Plane(sketch.owner.normal, polygon, Collections.emptyList()); - List extruded = Plane.extrude(plane, dir); + Polygon poly = new Polygon(sketch.owner.normal, polygon, Collections.emptyList()); + List extruded = Polygon.extrude(poly, dir); - for (Plane s : extruded) { + for (Polygon s : extruded) { sketch.drawLayer.getChildren().addAll(toNodes(extruded));// fixme } // CSG pad = Extrude.points(dir, polygon); } } - public List toNodes(List extruded) { + public List toNodes(List extruded) { return extruded.stream().map(this::toNode).collect(toList()); } - public CSGNode toNode(Plane plane) { - return new CSGNode(Utils3D.getMesh(Collections.singletonList(plane)), this); + public CSGNode toNode(Polygon poly) { + return new CSGNode(Utils3D.getMesh(Collections.singletonList(poly)), this); } public static class Selection { public final CSGNode csgNode; - public final Plane plane; + public final Polygon poly; - public Selection(CSGNode csgNode, Plane plane) { + public Selection(CSGNode csgNode, Polygon poly) { this.csgNode = csgNode; - this.plane = plane; + this.poly = poly; } - public boolean sameTo(CSGNode csgNode, Plane plane) { - return this.csgNode.equals(csgNode) && this.plane.equals(plane); + public boolean sameTo(CSGNode csgNode, Polygon poly) { + return this.csgNode.equals(csgNode) && this.poly.equals(poly); } } } diff --git a/src/cad/fx/Plane.java b/src/cad/fx/Polygon.java similarity index 81% rename from src/cad/fx/Plane.java rename to src/cad/fx/Polygon.java index ecb7a1c6..a6b465c1 100644 --- a/src/cad/fx/Plane.java +++ b/src/cad/fx/Polygon.java @@ -4,7 +4,6 @@ import cad.math.HMath; import cad.math.Matrix; import cad.math.Vector; import org.poly2tri.Poly2Tri; -import org.poly2tri.geometry.polygon.Polygon; import org.poly2tri.geometry.polygon.PolygonPoint; import java.util.ArrayList; @@ -15,7 +14,7 @@ import java.util.stream.Collectors; import static java.util.stream.Collectors.toList; -public class Plane { +public class Polygon { public final Vector normal; public final List shell; @@ -23,15 +22,15 @@ public class Plane { private List triangles; - public Plane(List shell) { + public Polygon(List shell) { this(shell, Collections.emptyList()); } - public Plane(List shell, List> holes) { + public Polygon(List shell, List> holes) { this(normalOfCCWSeq(shell.get(0), shell.get(1), shell.get(2)), shell, holes); } - public Plane(Vector normal, List shell, List> holes) { + public Polygon(Vector normal, List shell, List> holes) { this.normal = normal.normalize(); this.shell = shell; this.holes = holes; @@ -43,11 +42,11 @@ public class Plane { } } - public Plane fixCCW() { + public Polygon fixCCW() { if (!normal.slightlyEqualTo(normalOfCCWSeq(shell.get(0), shell.get(1), shell.get(2)))) { List shell = new ArrayList<>(this.shell); Collections.reverse(shell); - return new Plane(normal, shell, holes); + return new Polygon(normal, shell, holes); } return this; } @@ -82,7 +81,7 @@ public class Plane { .map(vector -> new PolygonPoint(vector.x, vector.y, vector.z)) .collect(toList()); - Polygon polygon = new Polygon(shellPoints); + org.poly2tri.geometry.polygon.Polygon polygon = new org.poly2tri.geometry.polygon.Polygon(shellPoints); for (List hole : holes) { @@ -91,7 +90,7 @@ public class Plane { .map(vector -> new PolygonPoint(vector.x, vector.y, vector.z)) .collect(toList()); - polygon.addHole(new Polygon(holePoints)); + polygon.addHole(new org.poly2tri.geometry.polygon.Polygon(holePoints)); } Poly2Tri.triangulate(polygon); @@ -128,11 +127,11 @@ public class Plane { triangle[2] = first; } - public Plane flip() { - return new Plane(normal.negate(), shell, holes); + public Polygon flip() { + return new Polygon(normal.negate(), shell, holes); } - public static List extrude(Plane source, Vector target) { + public static List extrude(Polygon source, Vector target) { double dotProduct = target.normalize().dot(source.normal); if (dotProduct == 0) { @@ -143,22 +142,22 @@ public class Plane { } source = source.fixCCW(); - List planes = new ArrayList<>(); - planes.add(source); + List poly = new ArrayList<>(); + poly.add(source); - Plane lid = source.shift(target).flip(); - planes.add(lid); + Polygon lid = source.shift(target).flip(); + poly.add(lid); for (int i = 0; i < source.shell.size(); i++) { - Plane face = new Plane(Arrays.asList( + Polygon face = new Polygon(Arrays.asList( get(source.shell, i - 1), get(lid.shell, i - 1), get(lid.shell, i), get(source.shell, i) )); - planes.add(face); + poly.add(face); } - return planes; + return poly; } public static T get(List list, int i) { @@ -169,12 +168,12 @@ public class Plane { return list.get(i); } - public Plane shift(Vector target) { + public Polygon shift(Vector target) { List shell = this.shell.stream().map(vector -> vector.plus(target)).collect(toList()); List> holes = new ArrayList<>(); for (List hole : this.holes) { holes.add(hole.stream().map(vector -> vector.plus(target)).collect(toList())); } - return new Plane(normal, shell, holes); + return new Polygon(normal, shell, holes); } } diff --git a/src/cad/fx/Sketch.java b/src/cad/fx/Sketch.java index 2ad2c399..7c4fc58f 100644 --- a/src/cad/fx/Sketch.java +++ b/src/cad/fx/Sketch.java @@ -8,11 +8,11 @@ import java.util.List; public class Sketch { - public final Plane owner; + public final Polygon owner; public final List> polygons = new ArrayList<>(); public final Group drawLayer = new Group(); - public Sketch(Plane owner) { + public Sketch(Polygon owner) { this.owner = owner; } } diff --git a/src/cad/fx/Utils3D.java b/src/cad/fx/Utils3D.java index fd1fcf63..bde9d94e 100644 --- a/src/cad/fx/Utils3D.java +++ b/src/cad/fx/Utils3D.java @@ -32,16 +32,16 @@ public class Utils3D { } - public static CSGMesh getMesh(List planes) { + public static CSGMesh getMesh(List polygons) { CSGMesh mesh = new CSGMesh(); int faceCounter = 0; - for (Plane plane : planes) { + for (Polygon poly : polygons) { - for (Vector[] triangle : plane.getTriangles()) { + for (Vector[] triangle : poly.getTriangles()) { mesh.getPoints().addAll( @@ -82,7 +82,7 @@ public class Utils3D { counter + 2, // third vertex 0 // texture (not covered) ); - mesh.polygons.put(faceCounter, plane); + mesh.polygons.put(faceCounter, poly); ++faceCounter; } // end if #verts >= 3 @@ -147,12 +147,12 @@ public class Utils3D { return mesh; } - public static List createCube(double width) { - Plane square = createSquare(width); - return Plane.extrude(square, square.normal.scale(width)); + public static List createCube(double width) { + Polygon square = createSquare(width); + return Polygon.extrude(square, square.normal.scale(width)); } - public static Plane createSquare(double width) { + public static Polygon createSquare(double width) { width /= 2; @@ -173,6 +173,6 @@ public class Utils3D { // // polygon.addHole(hole); - return new Plane(shell); + return new Polygon(shell); } } diff --git a/src/cad/gl/BBox.java b/src/cad/gl/BBox.java new file mode 100644 index 00000000..eed258f8 --- /dev/null +++ b/src/cad/gl/BBox.java @@ -0,0 +1,477 @@ +package cad.gl; + +import cad.math.Vector; + +public class BBox { + private double minX; + private double maxX; + private double minY; + private double maxY; + private double minZ; + private double maxZ; + + /** + * Create an axis aligned bounding box object, with an empty bounds + * where maxX < minX, maxY < minY and maxZ < minZ. + */ + public BBox() { + minX = minY = minZ = 0.0f; + maxX = maxY = maxZ = -1.0f; + } + + public BBox copy() { + return new BBox(minX, minY, minZ, maxX, maxY, maxZ); + } + + /** + * Creates an axis aligned bounding box based on the minX, minY, minZ, maxX, maxY, + * and maxZ values specified. + */ + public BBox(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + setBounds(minX, minY, minZ, maxX, maxY, maxZ); + } + + /** + * Creates an axis aligned bounding box as a copy of the specified + * BoxBounds object. + */ + public BBox(BBox other) { + setBounds(other); + } + + + public boolean is2D() { + return false; + } + + /** + * Convenience function for getting the width of this bounds. + * The dimension along the X-Axis. + */ + public double getWidth() { + return maxX - minX; + } + + /** + * Convenience function for getting the height of this bounds. + * The dimension along the Y-Axis. + */ + public double getHeight() { + return maxY - minY; + } + + /** + * Convenience function for getting the depth of this bounds. + * The dimension along the Z-Axis. + */ + public double getDepth() { + return maxZ - minZ; + } + + public double getMinX() { + return minX; + } + + public void setMinX(double minX) { + this.minX = minX; + } + + public double getMinY() { + return minY; + } + + public void setMinY(double minY) { + this.minY = minY; + } + + public double getMinZ() { + return minZ; + } + + public void setMinZ(double minZ) { + this.minZ = minZ; + } + + public double getMaxX() { + return maxX; + } + + public void setMaxX(double maxX) { + this.maxX = maxX; + } + + public double getMaxY() { + return maxY; + } + + public void setMaxY(double maxY) { + this.maxY = maxY; + } + + public double getMaxZ() { + return maxZ; + } + + public void setMaxZ(double maxZ) { + this.maxZ = maxZ; + } + + public Vector getMin(Vector min) { + if (min == null) { + min = new Vector(); + } + min.x = minX; + min.y = minY; + min.z = minZ; + return min; + + } + + public Vector getMax(Vector max) { + if (max == null) { + max = new Vector(); + } + max.x = maxX; + max.y = maxY; + max.z = maxZ; + return max; + + } + + public BBox deriveWithUnion(BBox other) { + unionWith(other); + return this; + } + + public BBox deriveWithNewBounds(BBox other) { + if (other.isEmpty()) { + return makeEmpty(); + } + minX = other.getMinX(); + minY = other.getMinY(); + minZ = other.getMinZ(); + maxX = other.getMaxX(); + maxY = other.getMaxY(); + maxZ = other.getMaxZ(); + return this; + } + + public BBox deriveWithNewBounds(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + if ((maxX < minX) || (maxY < minY) || (maxZ < minZ)) { + return makeEmpty(); + } + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + return this; + } + + public BBox deriveWithNewBoundsAndSort(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + setBoundsAndSort(minX, minY, minZ, maxX, maxY, maxZ); + return this; + } + + /** + * Set the bounds to match that of the BoxBounds object specified. The + * specified bounds object must not be null. + */ + public final void setBounds(BBox other) { + minX = other.getMinX(); + minY = other.getMinY(); + minZ = other.getMinZ(); + maxX = other.getMaxX(); + maxY = other.getMaxY(); + maxZ = other.getMaxZ(); + } + + /** + * Set the bounds to the given values. + */ + public final void setBounds(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + } + + public void setBoundsAndSort(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + setBounds(minX, minY, minZ, maxX, maxY, maxZ); + sortMinMax(); + } + + public void setBoundsAndSort(Vector p1, Vector p2) { + setBoundsAndSort(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); + } + + public void unionWith(BBox other) { + // Short circuit union if either bounds is empty. + if (other.isEmpty()) { + return; + } + if (this.isEmpty()) { + setBounds(other); + return; + } + + minX = Math.min(minX, other.getMinX()); + minY = Math.min(minY, other.getMinY()); + minZ = Math.min(minZ, other.getMinZ()); + maxX = Math.max(maxX, other.getMaxX()); + maxY = Math.max(maxY, other.getMaxY()); + maxZ = Math.max(maxZ, other.getMaxZ()); + } + + + public void unionWith(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + // Short circuit union if either bounds is empty. + if ((maxX < minX) || (maxY < minY) || (maxZ < minZ)) { + return; + } + if (this.isEmpty()) { + setBounds(minX, minY, minZ, maxX, maxY, maxZ); + return; + } + + this.minX = Math.min(this.minX, minX); + this.minY = Math.min(this.minY, minY); + this.minZ = Math.min(this.minZ, minZ); + this.maxX = Math.max(this.maxX, maxX); + this.maxY = Math.max(this.maxY, maxY); + this.maxZ = Math.max(this.maxZ, maxZ); + } + + public void add(double x, double y, double z) { + unionWith(x, y, z, x, y, z); + } + + public void add(Vector p) { + add(p.x, p.y, p.z); + } + + public void intersectWith(BBox other) { + // Short circuit intersect if either bounds is empty. + if (this.isEmpty()) { + return; + } + if (other.isEmpty()) { + makeEmpty(); + return; + } + + minX = Math.max(minX, other.getMinX()); + minY = Math.max(minY, other.getMinY()); + minZ = Math.max(minZ, other.getMinZ()); + maxX = Math.min(maxX, other.getMaxX()); + maxY = Math.min(maxY, other.getMaxY()); + maxZ = Math.min(maxZ, other.getMaxZ()); + } + + public void intersectWith(double minX, double minY, double minZ, + double maxX, double maxY, double maxZ) { + // Short circuit intersect if either bounds is empty. + if (this.isEmpty()) { + return; + } + if ((maxX < minX) || (maxY < minY) || (maxZ < minZ)) { + makeEmpty(); + return; + } + + this.minX = Math.max(this.minX, minX); + this.minY = Math.max(this.minY, minY); + this.minZ = Math.max(this.minZ, minZ); + this.maxX = Math.min(this.maxX, maxX); + this.maxY = Math.min(this.maxY, maxY); + this.maxZ = Math.min(this.maxZ, maxZ); + } + + public boolean contains(Vector p) { + if ((p == null) || isEmpty()) { + return false; + } + return contains(p.x, p.y, p.z); + } + + public boolean contains(double x, double y, double z) { + if (isEmpty()) { + return false; + } + return (x >= minX && x <= maxX && y >= minY && y <= maxY + && z >= minZ && z <= maxZ); + } + + public boolean contains(double x, double y, double z, + double width, double height, double depth) { + if (isEmpty()) { + return false; + } + return contains(x, y, z) && contains(x + width, y + height, z + depth); + } + + public boolean intersects(double x, double y, double z, + double width, double height, double depth) { + if (isEmpty()) { + return false; + } + return (x + width >= minX && + y + height >= minY && + z + depth >= minZ && + x <= maxX && + y <= maxY && + z <= maxZ); + } + + public boolean intersects(BBox other) { + if ((other == null) || other.isEmpty() || isEmpty()) { + return false; + } + return (other.getMaxX() >= minX && + other.getMaxY() >= minY && + other.getMaxZ() >= minZ && + other.getMinX() <= maxX && + other.getMinY() <= maxY && + other.getMinZ() <= maxZ); + } + + public boolean disjoint(double x, double y, double width, double height) { + return disjoint(x, y, 0f, width, height, 0f); + } + + public boolean disjoint(double x, double y, double z, + double width, double height, double depth) { + if (isEmpty()) { + return true; + } + return (x + width < minX || + y + height < minY || + z + depth < minZ || + x > maxX || + y > maxY || + z > maxZ); + } + + public boolean isEmpty() { + return maxX < minX || maxY < minY || maxZ < minZ; + } + + /** + * Adjusts the edges of this BoxBounds "outward" toward integral boundaries, + * such that the rounded bounding box will always full enclose the original + * bounding box. + */ + public void roundOut() { + minX = Math.floor(minX); + minY = Math.floor(minY); + minZ = Math.floor(minZ); + maxX = Math.ceil(maxX); + maxY = Math.ceil(maxY); + maxZ = Math.ceil(maxZ); + } + + public void grow(double h, double v, double d) { + minX -= h; + maxX += h; + minY -= v; + maxY += v; + minZ -= d; + maxZ += d; + } + + public BBox deriveWithPadding(double h, double v, double d) { + grow(h, v, d); + return this; + } + + // for convenience, this function returns a reference to itself, so we can + // change from using "bounds.makeEmpty(); return bounds;" to just + // "return bounds.makeEmpty()" + public BBox makeEmpty() { + minX = minY = minZ = 0.0f; + maxX = maxY = maxZ = -1.0f; + return this; + } + + protected void sortMinMax() { + if (minX > maxX) { + double tmp = maxX; + maxX = minX; + minX = tmp; + } + if (minY > maxY) { + double tmp = maxY; + maxY = minY; + minY = tmp; + } + if (minZ > maxZ) { + double tmp = maxZ; + maxZ = minZ; + minZ = tmp; + } + } + + public void translate(double x, double y, double z) { + setMinX(getMinX() + x); + setMinY(getMinY() + y); + setMaxX(getMaxX() + x); + setMaxY(getMaxY() + y); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final BBox other = (BBox) obj; + if (minX != other.getMinX()) { + return false; + } + if (minY != other.getMinY()) { + return false; + } + if (minZ != other.getMinZ()) { + return false; + } + if (maxX != other.getMaxX()) { + return false; + } + if (maxY != other.getMaxY()) { + return false; + } + if (maxZ != other.getMaxZ()) { + return false; + } + return true; + } + + @Override + public int hashCode() { + long hash = 7; + hash = 79 * hash + Double.doubleToLongBits(minX); + hash = 79 * hash + Double.doubleToLongBits(minY); + hash = 79 * hash + Double.doubleToLongBits(minZ); + hash = 79 * hash + Double.doubleToLongBits(maxX); + hash = 79 * hash + Double.doubleToLongBits(maxY); + hash = 79 * hash + Double.doubleToLongBits(maxZ); + + return (int) hash; + } + + @Override + public String toString() { + return "BBox { minX:" + minX + ", minY:" + minY + ", minZ:" + minZ + ", maxX:" + maxX + ", maxY:" + maxY + ", maxZ:" + maxZ + "}"; + } +} diff --git a/src/cad/gl/Camera.java b/src/cad/gl/Camera.java new file mode 100644 index 00000000..1aec5492 --- /dev/null +++ b/src/cad/gl/Camera.java @@ -0,0 +1,16 @@ +package cad.gl; + +/** + * Created by verastov + */ +public class Camera { + + public float aspect = 1; + public double near = 5.0f; + public double far = 60.0f; + + public int sceneW; + public int sceneH; + + public double near_width = 1; +} diff --git a/src/cad/gl/CompiledNode.java b/src/cad/gl/CompiledNode.java new file mode 100644 index 00000000..27020372 --- /dev/null +++ b/src/cad/gl/CompiledNode.java @@ -0,0 +1,20 @@ +package cad.gl; + +import javax.media.opengl.GL2; + +/** + * Created by verastov + */ +public class CompiledNode { + + public final int glId; + public final Node node; + + public CompiledNode(Node node, GL2 gl) { + this.node = node; + glId = gl.glGenLists(1); + gl.glNewList(glId, GL2.GL_COMPILE); + node.draw(gl); + gl.glEndList(); + } +} diff --git a/src/cad/gl/MeshNode.java b/src/cad/gl/MeshNode.java new file mode 100644 index 00000000..a7ae11f5 --- /dev/null +++ b/src/cad/gl/MeshNode.java @@ -0,0 +1,230 @@ +package cad.gl; + +import cad.fx.Polygon; +import cad.math.Vector; +import com.sun.javafx.scene.input.PickResultChooser; +import javafx.geometry.Point3D; +import javafx.scene.input.PickResult; +import javafx.scene.shape.CullFace; + +import javax.media.opengl.GL2; +import java.util.List; + +/** + * Created by verastov + */ +public class MeshNode extends Node { + + public final List faces; + + static boolean DRAW_LINES = false; + private BBox cachedBounds; + + public MeshNode(List faces) { + this.faces = faces; + } + + @Override + void draw(GL2 gl) { + for (Polygon face : faces) { + + //http://devernay.free.fr/cours/opengl/materials.html +// float[] amb = {0f, 0f, 0f, 0f}; +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT, amb, 0); + + float[] diff = {0.6901961f, 0.76862746f, 0.87058824f}; + gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_DIFFUSE, diff, 0); + +// float[] spec = {0f, 0f, 0f}; +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, spec, 0); +// float shine = 0.6f; +// gl.glMaterialf(GL2.GL_FRONT, GL2.GL_SHININESS, shine * 128.0f); +// gl.glMaterialfv(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE, blue, 0); + + gl.glShadeModel(GL2.GL_SMOOTH); + gl.glEnable(GL2.GL_LIGHTING); + gl.glBegin(GL2.GL_TRIANGLES); + + gl.glNormal3d(face.normal.x, face.normal.y, face.normal.z); //very important!! + for (Vector[] tr : face.getTriangles()) { + gl.glVertex3d(tr[0].x, tr[0].y, tr[0].z); + gl.glVertex3d(tr[1].x, tr[1].y, tr[1].z); + gl.glVertex3d(tr[2].x, tr[2].y, tr[2].z); + } + gl.glEnd(); + + if (DRAW_LINES) { + gl.glShadeModel(GL2.GL_FLAT); + gl.glLineWidth(1.5f); + gl.glColor3f(255.0f, 255.0f, 255.0f); + gl.glDisable(GL2.GL_LIGHTING); + + gl.glNormal3d(face.normal.x, face.normal.y, face.normal.z); + for (int i = 0; i < face.shell.size(); i++) { + gl.glBegin(GL2.GL_LINES); + Vector a = Polygon.get(face.shell, i); + Vector b = Polygon.get(face.shell, i + 1); + gl.glVertex3d(a.x, a.y, a.z); + gl.glVertex3d(b.x, b.y, b.z); + gl.glEnd(); + } + } + } + } + + + public BBox computeBounds() { + if (cachedBounds == null) { + cachedBounds = new BBox(); + for (Polygon face : faces) { + for (Vector vector : face.shell) { + cachedBounds.add(vector); + } + } + } + return cachedBounds; + } + + protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult, + javafx.scene.Node candidate, CullFace cullFace, + boolean reportFace) { + + boolean found = false; + + final Vector o = pickRay.getOriginNoClone(); + final Vector d = pickRay.getDirectionNoClone(); + + for (Polygon face : faces) { + for (Vector[] triangle : face.getTriangles()) { + if (computeIntersectsFace(pickRay, o, d, triangle, cullFace, candidate, + reportFace, pickResult)) { + found = true; + } + } + } + return found; + } + + private Point3D computeCentroid( + double v0x, double v0y, double v0z, + double v1x, double v1y, double v1z, + double v2x, double v2y, double v2z) { + +// Point3D center = v1.midpoint(v2); +// Point3D vec = center.subtract(v0); +// return v0.add(new Point3D(vec.getX() / 3.0, vec.getY() / 3.0, vec.getZ() / 3.0)); + + return new Point3D( + v0x + (v2x + (v1x - v2x) / 2.0 - v0x) / 3.0, + v0y + (v2y + (v1y - v2y) / 2.0 - v0y) / 3.0, + v0z + (v2z + (v1z - v2z) / 2.0 - v0z) / 3.0); + } + + private boolean computeIntersectsFace( + PickRay pickRay, Vector origin, Vector dir, Vector[] face, + CullFace cullFace, javafx.scene.Node candidate, boolean reportFace, + PickResultChooser result) {//, BoxBounds rayBounds) { + + final float v0x = (float) face[0].x; + final float v0y = (float) face[0].y; + final float v0z = (float) face[0].z; + final float v1x = (float) face[1].x; + final float v1y = (float) face[1].y; + final float v1z = (float) face[1].z; + final float v2x = (float) face[2].x; + final float v2y = (float) face[2].y; + final float v2z = (float) face[2].z; + + // e1 = v1.subtract(v0) + final float e1x = v1x - v0x; + final float e1y = v1y - v0y; + final float e1z = v1z - v0z; + // e2 = v2.subtract(v0) + final float e2x = v2x - v0x; + final float e2y = v2y - v0y; + final float e2z = v2z - v0z; + + // h = dir.crossProduct(e2) + final double hx = dir.y * e2z - dir.z * e2y; + final double hy = dir.z * e2x - dir.x * e2z; + final double hz = dir.x * e2y - dir.y * e2x; + + // a = e1.dotProduct(h) + final double a = e1x * hx + e1y * hy + e1z * hz; + if (a == 0.0) { + return false; + } + final double f = 1.0 / a; + + // s = origin.subtract(v0) + final double sx = origin.x - v0x; + final double sy = origin.y - v0y; + final double sz = origin.z - v0z; + + // u = f * (s.dotProduct(h)) + final double u = f * (sx * hx + sy * hy + sz * hz); + + if (u < 0.0 || u > 1.0) { + return false; + } + + // q = s.crossProduct(e1) + final double qx = sy * e1z - sz * e1y; + final double qy = sz * e1x - sx * e1z; + final double qz = sx * e1y - sy * e1x; + + // v = f * dir.dotProduct(q) + double v = f * (dir.x * qx + dir.y * qy + dir.z * qz); + + if (v < 0.0 || u + v > 1.0) { + return false; + } + + // t = f * e2.dotProduct(q) + final double t = f * (e2x * qx + e2y * qy + e2z * qz); + + if (t >= pickRay.getNearClip() && t <= pickRay.getFarClip()) { + // This branch is entered only for hit triangles (not so often), + // so we can get smoothly back to the nice code using Point3Ds. + + if (cullFace != CullFace.NONE) { + // normal = e1.crossProduct(e2) + final Point3D normal = new Point3D( + e1y * e2z - e1z * e2y, + e1z * e2x - e1x * e2z, + e1x * e2y - e1y * e2x); + + final double nangle = normal.angle( + new Point3D(-dir.x, -dir.y, -dir.z)); + if ((nangle >= 90 || cullFace != CullFace.BACK) && + (nangle <= 90 || cullFace != CullFace.FRONT)) { + // hit culled face + return false; + } + } + + if (Double.isInfinite(t) || Double.isNaN(t)) { + // we've got a nonsense pick ray or triangle + return false; + } + + if (result == null || !result.isCloser(t)) { + // it intersects, but we are not interested in the result + // or we already have a better (closer) result + // so we can omit the point and texture computation + return true; + } + +// Point3D point = PickResultChooser.computePoint(pickRay, t); + +// result.offer(candidate, t, +// reportFace ? faceIndex / vertexFormat.getFaceElementSize() : PickResult.FACE_UNDEFINED, +// point, txCoords); + return true; + } + + return false; + } + + +} diff --git a/src/cad/gl/Node.java b/src/cad/gl/Node.java new file mode 100644 index 00000000..47903457 --- /dev/null +++ b/src/cad/gl/Node.java @@ -0,0 +1,27 @@ +package cad.gl; + +import com.sun.javafx.geom.PickRay; +import com.sun.javafx.scene.input.PickResultChooser; + +import javax.media.opengl.GL2; + +public abstract class Node { + + abstract void draw(GL2 gl); + +// protected final boolean impl_intersects(PickRay pickRay, PickResultChooser pickResult) { +// double boundsDistance = impl_intersectsBounds(pickRay); +// if (!Double.isNaN(boundsDistance)) { +// if (isPickOnBounds()) { +// if (pickResult != null) { +// pickResult.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance)); +// } +// return true; +// } else { +// return impl_computeIntersects(pickRay, pickResult); +// } +// } +// return false; +// } + +} diff --git a/src/cad/gl/PickRay.java b/src/cad/gl/PickRay.java new file mode 100644 index 00000000..57599689 --- /dev/null +++ b/src/cad/gl/PickRay.java @@ -0,0 +1,152 @@ + +package cad.gl; + +import cad.math.Vector; + +/** + * A ray used for picking. + */ +public class PickRay { + private Vector origin = new Vector(); + private Vector direction = new Vector(); + private double nearClip = 0.0; + private double farClip = Double.POSITIVE_INFINITY; + + // static final double EPS = 1.0e-13; + static final double EPS = 1.0e-5f; + + public PickRay() { + } + + public PickRay(Vector origin, Vector direction, double nearClip, double farClip) { + set(origin, direction, nearClip, farClip); + } + + public PickRay(double x, double y, double z, double nearClip, double farClip) { + set(x, y, z, nearClip, farClip); + } + + + public final void set(Vector origin, Vector direction, double nearClip, double farClip) { + setOrigin(origin); + setDirection(direction); + this.nearClip = nearClip; + this.farClip = farClip; + } + + public final void set(double x, double y, double z, double nearClip, double farClip) { + setOrigin(x, y, -z); + setDirection(0, 0, z); + this.nearClip = nearClip; + this.farClip = farClip; + } + + + public void setPickRay(PickRay other) { + setOrigin(other.origin); + setDirection(other.direction); + nearClip = other.nearClip; + farClip = other.farClip; + } + + public PickRay copy() { + return new PickRay(origin, direction, nearClip, farClip); + } + + /** + * Sets the origin of the pick ray in world coordinates. + * + * @param origin the origin (in world coordinates). + */ + public void setOrigin(Vector origin) { + this.origin.set(origin); + } + + /** + * Sets the origin of the pick ray in world coordinates. + * + * @param x the origin X coordinate + * @param y the origin Y coordinate + * @param z the origin Z coordinate + */ + public void setOrigin(double x, double y, double z) { + this.origin.set(x, y, z); + } + + public Vector getOrigin(Vector rv) { + if (rv == null) { + rv = new Vector(); + } + rv.set(origin); + return rv; + } + + public Vector getOriginNoClone() { + return origin; + } + + /** + * Sets the direction vector of the pick ray. This vector need not + * be normalized. + * + * @param direction the direction vector + */ + public void setDirection(Vector direction) { + this.direction.set(direction); + } + + /** + * Sets the direction of the pick ray. The vector need not be normalized. + * + * @param x the direction X magnitude + * @param y the direction Y magnitude + * @param z the direction Z magnitude + */ + public void setDirection(double x, double y, double z) { + this.direction.set(x, y, z); + } + + public Vector getDirection(Vector rv) { + if (rv == null) { + rv = new Vector(); + } + rv.set(direction); + return rv; + } + + public Vector getDirectionNoClone() { + return direction; + } + + public double getNearClip() { + return nearClip; + } + + public double getFarClip() { + return farClip; + } + + public double distance(Vector iPnt) { + double x = iPnt.x - origin.x; + double y = iPnt.y - origin.y; + double z = iPnt.z - origin.z; + return Math.sqrt(x * x + y * y + z * z); + } + + + private static final double EPSILON_ABSOLUTE = 1.0e-5; + + static boolean almostZero(double a) { + return ((a < EPSILON_ABSOLUTE) && (a > -EPSILON_ABSOLUTE)); + } + + private static boolean isNonZero(double v) { + return ((v > EPS) || (v < -EPS)); + + } + + @Override + public String toString() { + return "origin: " + origin + " direction: " + direction; + } +} diff --git a/src/cad/gl/Scene.java b/src/cad/gl/Scene.java new file mode 100644 index 00000000..02b0a5f6 --- /dev/null +++ b/src/cad/gl/Scene.java @@ -0,0 +1,318 @@ +package cad.gl; + +import cad.fx.Polygon; +import cad.math.Vector; +import com.jogamp.newt.event.MouseEvent; +import com.jogamp.newt.event.MouseListener; +import com.jogamp.newt.opengl.GLWindow; +import com.sun.javafx.geom.*; +import javafx.scene.input.PickResult; + +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.Threading; +import javax.media.opengl.glu.GLU; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Scene implements GLEventListener, MouseListener { + + private final List toCompile = new ArrayList<>(); + private final List scene = new ArrayList<>(); + private final GLWindow window; + private final Camera camera = new Camera(); + private Vector[] pickRay = {new Vector(), new Vector()}; + private float winMouseY; + private float winMouseX; + private boolean pickRequest = false; + + public Scene(GLWindow window) { + this.window = window; + window.addGLEventListener(this); + } + + float red[] = {0.8f, 0.1f, 0.0f, 1.0f}; + float green[] = {0.0f, 0.8f, 0.2f, 1.0f}; + float blue[] = {0.2f, 0.2f, 1.0f, 1.0f}; + float white[] = {1.0f, 1.0f, 1.0f}; + + private float view_rotx = 0.0f, view_roty = 0.0f, view_rotz = 0.0f; +// private float view_rotx = 20.0f, view_roty = 30.0f, view_rotz = 0.0f; + + private int prevMouseX, prevMouseY; + private boolean mouseRButtonDown = false; + + public void init(GLAutoDrawable drawable) { + // Use debug pipeline + // drawable.setGL(new DebugGL(drawable.getGL())); + + GL2 gl = drawable.getGL().getGL2(); + + System.err.println("INIT GL IS: " + gl.getClass().getName()); + + System.err.println("Chosen GLCapabilities: " + drawable.getChosenGLCapabilities()); + + gl.setSwapInterval(0); + + float pos0[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, pos0, 0); + + gl.glEnable(GL2.GL_CULL_FACE); + gl.glEnable(GL2.GL_DEPTH_TEST); + gl.glEnable(GL2.GL_LIGHTING); + gl.glEnable(GL2.GL_LIGHT0); + + compileNodes(gl); + + gl.glEnable(GL2.GL_NORMALIZE); + + window.addMouseListener(this); + } + + private void compileNodes(GL2 gl) { + if (toCompile.isEmpty()) { + return; + } + for (Node node : toCompile) { + scene.add(new CompiledNode(node, gl)); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2 gl = drawable.getGL().getGL2(); + + camera.sceneW = width; + camera.sceneH = height; + camera.aspect = (float) height / (float) width; + + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glFrustum(-camera.near_width, camera.near_width, + -camera.aspect * camera.near_width, camera.aspect * camera.near_width, + camera.near, camera.far); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glTranslatef(0.0f, 0.0f, -40.0f); + } + + public void dispose(GLAutoDrawable drawable) { + System.out.println("Gears.dispose: " + drawable); + } + + public void display(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); + + + + gl.glClearColor(0.5019608f, 0.5019608f, 0.5019608f, 0f); + + gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); + + + + gl.glPushMatrix(); + gl.glScalef(scale, scale, scale); + + gl.glPushMatrix(); + + gl.glRotatef(view_rotx, 1.0f, 0.0f, 0.0f); + gl.glRotatef(view_roty, 0.0f, 1.0f, 0.0f); + gl.glRotatef(view_rotz, 0.0f, 0.0f, 1.0f); + + updatePickRay(gl); + + drawPickRay(gl); + + for (CompiledNode cn : scene) { + gl.glCallList(cn.glId); + } + + + gl.glPopMatrix(); + gl.glPopMatrix(); + } + + private void updatePickRay(GL2 gl) { + if (!pickRequest) { + return; + } + pickRequest = false; + float[] model = new float[16]; + float[] proj = new float[16]; + int[] viewport = new int[16]; + + gl.glGetIntegerv(GL2.GL_VIEWPORT, viewport, 0); + gl.glGetFloatv(GL2.GL_MODELVIEW_MATRIX, model, 0); + gl.glGetFloatv(GL2.GL_PROJECTION_MATRIX, proj, 0); + + float[] out = new float[3]; + float y = viewport[3] - winMouseY; + glu.gluUnProject(winMouseX, y, 0, model, 0, proj, 0, viewport, 0, out, 0); + pickRay[0].set3(out); + + glu.gluUnProject(winMouseX, y, 1, model, 0, proj, 0, viewport, 0, out, 0); + pickRay[1].set3(out); + + pickRay[1] = pickRay[1].minus(pickRay[0]).scale(700); + } + + public static float[] fixW(float[] v) { + float w = v[3]; + for (int i = 0; i < 4; i++) + v[i] = v[i] / w; + return v; + } + + private void drawPickRay(GL2 gl) { + + if (pickRay != null) { + + gl.glShadeModel(GL2.GL_FLAT); + gl.glLineWidth(1.5f); + gl.glColor3f(255.0f, 255.0f, 255.0f); + gl.glDisable(GL2.GL_LIGHTING); + + gl.glBegin(GL2.GL_LINES); +// Vector a = pickRay; +// Vector b = pickRay.plus(pickRay.normalize().scale(-60)); + vertex(gl, pickRay[0]); + vertex(gl, pickRay[1]); + System.out.println(Arrays.toString(pickRay)); + gl.glEnd(); + } + } + + private void vertex(GL2 gl, Vector vector) { + gl.glVertex3d(vector.x, vector.y, vector.z); + } + + @Override + public void mouseClicked(MouseEvent e) { + computePickRay(e.getX(), e.getY()); + update(window::display); + pickRequest = true; + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + prevMouseX = e.getX(); + prevMouseY = e.getY(); + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = true; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if ((e.getModifiers() & e.BUTTON3_MASK) != 0) { + mouseRButtonDown = false; + } + + } + + @Override + public void mouseMoved(MouseEvent e) { + } + + @Override + public void mouseDragged(MouseEvent e) { + int x = e.getX(); + int y = e.getY(); + + int width = window.getWidth(); + int height = window.getHeight(); + + float thetaY = 360.0f * ((float) (x - prevMouseX) / (float) width); + float thetaX = 360.0f * ((float) (prevMouseY - y) / (float) height); + + prevMouseX = x; + prevMouseY = y; + + view_rotx += thetaX; + view_roty += thetaY; + + update(window::display); + } + + volatile boolean updating = false; + + private void update(Runnable op) { + if (updating) { + return; + } + + Threading.invokeOnOpenGLThread(false, new Runnable() { + @Override + public void run() { + try { + updating = true; + op.run(); + } finally { + updating = false; + } + } + }); + } + + final double SCALE_DELTA = 1.1; + float scale = 1; + + @Override + public void mouseWheelMoved(MouseEvent e) { + double scaleFactor = e.getRotation()[1] > 0 ? SCALE_DELTA : 1 / SCALE_DELTA; + scale *= scaleFactor; + update(window::display); + } + + public void addNode(Node node) { + toCompile.add(node); + } + + +// private PickResult pick(final double x, final double y) { +// final PickRay pickRay = computePickRay(x, y); +// final double mag = pickRay.getDirectionNoClone().length(); +// pickRay.getDirectionNoClone().normalize(); +// final PickResult res = pickNode(pickRay); +// return res; +// } + + GLU glu = new GLU(); + + private void computePickRay(float mx, float my) { + +// winMouseX = mx - camera.sceneW / 2; +// winMouseY = (camera.sceneH - my) - camera.sceneH / 2; + + winMouseX = mx; + winMouseY = my; +// +// +// float winY = (camera.sceneH - my) - camera.sceneH/2; +// double norm_y = winY/(camera.sceneH/2); +// +// float winX = mx - camera.sceneW/2; +// double norm_x = winX/(camera.sceneW/2); + +// +// double y = camera.near_width * norm_y * camera.aspect; +// double x = camera.near_width * norm_x; +// System.out.println(x + ":" + y); +// return new Vector(x, y, camera.near); + } + + private PickResult pickNode(PickRay ray) { + return null; + } + +} diff --git a/src/cad/math/Vector.java b/src/cad/math/Vector.java index d6ea149d..baca8eb8 100644 --- a/src/cad/math/Vector.java +++ b/src/cad/math/Vector.java @@ -6,6 +6,14 @@ public class Vector { public double y; public double z; + public static Vector fromArr3(float[] data) { + return new Vector(data[0], data[1], data[2]); + } + + public static Vector fromArr3(double[] data) { + return new Vector(data[0], data[1], data[2]); + } + public Vector() { } @@ -23,16 +31,26 @@ public class Vector { this(x, y, 0); } - public Vector(double[] data) { - if (data.length > 0) { - x = data[0]; - if (data.length > 1) { - y = data[1]; - if (data.length > 2) { - z = data[2]; - } - } - } + public void set(Vector vector) { + set(vector.x, vector.y, vector.z); + } + + public void set(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } + + public void set3(float[] data) { + this.x = data[0]; + this.y = data[1]; + this.z = data[2]; + } + + public void set3(double[] data) { + this.x = data[0]; + this.y = data[1]; + this.z = data[2]; } public Vector scale(double factor) { @@ -102,4 +120,5 @@ public class Vector { public Vector negate() { return scale(-1); } + }