getting pick ray

This commit is contained in:
Val Erastov 2014-08-14 19:30:27 -07:00
parent 8aa85d9219
commit 2c42974329
17 changed files with 1684 additions and 388 deletions

View file

@ -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<Plane> initObjects = Utils3D.createCube(1);
static TIntObjectMap<Plane> 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);
}
}

349
src/cad/Cad3.java Normal file
View file

@ -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<Polygon> initObjects = Utils3D.createCube(1);
static TIntObjectMap<Polygon> 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);
}
}

View file

@ -36,7 +36,7 @@ public class AppCtrl implements Initializable {
}
private void setInitObject(Group parent) {
List<Plane> cube = Utils3D.createCube(100);
List<Polygon> cube = Utils3D.createCube(100);
parent.getChildren().addAll(cadContext.toNodes(cube));
//
// CSG init = new Cube(100).toCSG().difference(new Cylinder(30, 100, 10).toCSG());

View file

@ -6,6 +6,6 @@ import javafx.scene.shape.TriangleMesh;
public class CSGMesh extends TriangleMesh {
public final TIntObjectMap<Plane> polygons = new TIntObjectHashMap<>();
public final TIntObjectMap<Polygon> polygons = new TIntObjectHashMap<>();
}

View file

@ -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<Plane, Sketch> sketches = new HashMap<>();
public final Map<Polygon, Sketch> 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;
}

View file

@ -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<Vector> polygon : sketch.polygons) {
if (polygon.isEmpty()) {
continue;
}
Plane plane = new Plane(sketch.owner.normal, polygon, Collections.emptyList());
List<Plane> extruded = Plane.extrude(plane, dir);
Polygon poly = new Polygon(sketch.owner.normal, polygon, Collections.emptyList());
List<Polygon> 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<CSGNode> toNodes(List<Plane> extruded) {
public List<CSGNode> toNodes(List<Polygon> 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);
}
}
}

View file

@ -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<Vector> shell;
@ -23,15 +22,15 @@ public class Plane {
private List<Vector[]> triangles;
public Plane(List<Vector> shell) {
public Polygon(List<Vector> shell) {
this(shell, Collections.emptyList());
}
public Plane(List<Vector> shell, List<List<Vector>> holes) {
public Polygon(List<Vector> shell, List<List<Vector>> holes) {
this(normalOfCCWSeq(shell.get(0), shell.get(1), shell.get(2)), shell, holes);
}
public Plane(Vector normal, List<Vector> shell, List<List<Vector>> holes) {
public Polygon(Vector normal, List<Vector> shell, List<List<Vector>> 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<Vector> 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<Vector> 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<Plane> extrude(Plane source, Vector target) {
public static List<Polygon> 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<Plane> planes = new ArrayList<>();
planes.add(source);
List<Polygon> 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> T get(List<T> 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<Vector> shell = this.shell.stream().map(vector -> vector.plus(target)).collect(toList());
List<List<Vector>> holes = new ArrayList<>();
for (List<Vector> 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);
}
}

View file

@ -8,11 +8,11 @@ import java.util.List;
public class Sketch {
public final Plane owner;
public final Polygon owner;
public final List<List<Vector>> polygons = new ArrayList<>();
public final Group drawLayer = new Group();
public Sketch(Plane owner) {
public Sketch(Polygon owner) {
this.owner = owner;
}
}

View file

@ -32,16 +32,16 @@ public class Utils3D {
}
public static CSGMesh getMesh(List<Plane> planes) {
public static CSGMesh getMesh(List<Polygon> 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<Plane> createCube(double width) {
Plane square = createSquare(width);
return Plane.extrude(square, square.normal.scale(width));
public static List<Polygon> 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);
}
}

477
src/cad/gl/BBox.java Normal file
View file

@ -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 + "}";
}
}

16
src/cad/gl/Camera.java Normal file
View file

@ -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;
}

View file

@ -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();
}
}

230
src/cad/gl/MeshNode.java Normal file
View file

@ -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<Polygon> faces;
static boolean DRAW_LINES = false;
private BBox cachedBounds;
public MeshNode(List<Polygon> 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;
}
}

27
src/cad/gl/Node.java Normal file
View file

@ -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;
// }
}

152
src/cad/gl/PickRay.java Normal file
View file

@ -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;
}
}

318
src/cad/gl/Scene.java Normal file
View file

@ -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<Node> toCompile = new ArrayList<>();
private final List<CompiledNode> 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;
}
}

View file

@ -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);
}
}