diff --git a/ugs-platform/ugs-platform-visualizer/pom.xml b/ugs-platform/ugs-platform-visualizer/pom.xml index 0abc833807..01bf9a202d 100644 --- a/ugs-platform/ugs-platform-visualizer/pom.xml +++ b/ugs-platform/ugs-platform-visualizer/pom.xml @@ -23,6 +23,15 @@ com.willwinder.ugs.nbm.visualizer.shared com.willwinder.ugs.nbm.visualizer.options com.willwinder.ugs.nbm.visualizer.renderables + org.apache.commons.io + javax.xml.datatype + org.w3c.dom + org.w3c.dom.ls + org.xml.sax + javax.xml.parsers + org.w3c.dom.bootstrap + org.xml.sax.helpers + com.jogamp.opengl @@ -64,6 +73,13 @@ ${netbeans.version} jar + + org.netbeans.modules + org-openide-util-ui-svg + ${netbeans.version} + jar + test + ${project.groupId} ugs-platform-ugslib @@ -91,5 +107,72 @@ ugs-platform-ugscore ${project.version} + + + + com.neuronrobotics + JavaCad + 1.8.8 + + + eu.mihosoft.vvecmath + vvecmath + 0.4.0 + + + org.openjfx + javafx-graphics + 17.0.8 + + + org.openjfx + javafx-base + 17.0.8 + + + org.openjfx + javafx-fxml + 17.0.9 + + + org.openjfx + javafx-controls + 17.0.9 + + + org.openjfx + javafx-swing + 17.0.9 + + + + + org.locationtech.jts + jts-core + ${jts.version} + + + + + org.apache.xmlgraphics + batik-all + ${batik.version} + + + javax.vecmath + vecmath + 1.5.2 + + + + commons-io + commons-io + 2.7 + + + com.google.code.gson + gson + ${gson.version} + diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/RendererInputHandler.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/RendererInputHandler.java index aee1d98acd..5fd6e383cf 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/RendererInputHandler.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/RendererInputHandler.java @@ -63,8 +63,8 @@ This file is part of Universal Gcode Sender (UGS). public class RendererInputHandler implements WindowListener, MouseWheelListener, MouseMotionListener, MouseListener, KeyListener, PreferenceChangeListener, UGSEventListener { - private static final int HIGH_FPS = 15; - private static final int LOW_FPS = 4; + private final int highFps; + private final int lowFps; private final GcodeRenderer gcodeRenderer; private final AnimatorBase animator; private final BackendAPI backend; @@ -76,11 +76,13 @@ public class RendererInputHandler implements private Position selectionStart = null; private Position selectionEnd = null; - public RendererInputHandler(GcodeRenderer gr, AnimatorBase a, BackendAPI backend) { + public RendererInputHandler(GcodeRenderer gr, AnimatorBase a, BackendAPI backend, int lowFps, int highFps) { gcodeRenderer = gr; animator = a; this.backend = backend; settings = backend.getSettings(); + this.highFps = highFps; + this.lowFps = lowFps; gcodeModel = new GcodeModel(Localization.getString("platform.visualizer.renderable.gcode-model"), backend); sizeDisplay = new SizeDisplay(Localization.getString("platform.visualizer.renderable.gcode-model-size")); @@ -271,7 +273,7 @@ public void mouseClicked(MouseEvent e) { */ @Override public void mousePressed(MouseEvent e) { - setFPS(HIGH_FPS); + setFPS(highFps); // Zoom if (e.getButton() == MouseEvent.BUTTON1 && e.isMetaDown()) { selecting = true; @@ -282,7 +284,7 @@ public void mousePressed(MouseEvent e) { @Override public void mouseReleased(MouseEvent e) { - setFPS(LOW_FPS); + setFPS(lowFps); // Finish selecting. if (selecting) { @@ -317,7 +319,7 @@ public void keyTyped(KeyEvent ke) { */ @Override public void keyPressed(KeyEvent ke) { - setFPS(HIGH_FPS); + setFPS(highFps); int DELTA_SIZE = 1; @@ -358,7 +360,7 @@ public void keyPressed(KeyEvent ke) { */ @Override public void keyReleased(KeyEvent ke) { - setFPS(LOW_FPS); + setFPS(lowFps); } public void dispose() { diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/NewtVisualizationPanel.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/NewtVisualizationPanel.java index b7b8a724e2..292a36540b 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/NewtVisualizationPanel.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/NewtVisualizationPanel.java @@ -24,7 +24,7 @@ This file is part of Universal Gcode Sender (UGS). import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLException; -import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.FPSAnimator; import com.willwinder.ugs.nbm.visualizer.RendererInputHandler; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_NEWT_SAMPLES; @@ -134,8 +134,8 @@ private NewtCanvasAWT makeWindow() throws GLException { lock.lock(); lock.unlock(); - Animator animator = new Animator(glWindow); - this.rih = new RendererInputHandler(renderer, animator, backend); + FPSAnimator animator = new FPSAnimator(glWindow, 60); + this.rih = new RendererInputHandler(renderer, animator, backend, 30, 60); Preferences pref = NbPreferences.forModule(VisualizerOptionsPanel.class); pref.addPreferenceChangeListener(this.rih); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/VisualizationPanel.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/VisualizationPanel.java index 69a7b5aa68..47fb38e0c8 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/VisualizationPanel.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/jogl/VisualizationPanel.java @@ -99,7 +99,7 @@ private GLJPanel makeWindow() throws GLException { } FPSAnimator animator = new FPSAnimator(p, 15); - this.rih = new RendererInputHandler(renderer, animator, backend); + this.rih = new RendererInputHandler(renderer, animator, backend, 4, 15); Preferences pref = NbPreferences.forModule(VisualizerOptionsPanel.class); pref.addPreferenceChangeListener(this.rih); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Grid.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Grid.java index 55675adbc3..5f3a106f59 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Grid.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Grid.java @@ -20,14 +20,17 @@ This file is part of Universal Gcode Sender (UGS). import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GLAutoDrawable; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; +import com.willwinder.ugs.nbm.visualizer.shader.PlainShader; +import com.willwinder.ugs.nbm.visualizer.shared.VertexObjectRenderable; +import com.willwinder.universalgcodesender.model.Position; + import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_GRID; import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_X; import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_XY_GRID; import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_Y; import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_Z; -import com.willwinder.ugs.nbm.visualizer.shared.VertexObjectRenderable; -import com.willwinder.universalgcodesender.model.Position; /** * @author wwinder @@ -39,7 +42,7 @@ public class Grid extends VertexObjectRenderable { private float[] zAxisColor; public Grid(String title) { - super(6, title, VISUALIZER_OPTION_GRID); + super(6, title, VISUALIZER_OPTION_GRID, new PlainShader()); reloadPreferences(new VisualizerOptions()); } @@ -53,7 +56,8 @@ public final void reloadPreferences(VisualizerOptions vo) { } @Override - public void render(GL2 gl) { + public void render(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); gl.glLineWidth(1.2f); gl.glDisable(GL.GL_DEPTH_TEST); gl.glDrawArrays(GL.GL_LINES, 0, getVertexCount()); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/OrientationCube.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/OrientationCube.java index b01539d830..9b2610e836 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/OrientationCube.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/OrientationCube.java @@ -19,175 +19,151 @@ This file is part of Universal Gcode Sender (UGS). package com.willwinder.ugs.nbm.visualizer.renderables; -import static com.jogamp.opengl.GL.GL_CULL_FACE; -import static com.jogamp.opengl.GL.GL_DEPTH_TEST; +import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; -import static com.jogamp.opengl.GL2ES3.GL_QUADS; import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; -import com.jogamp.opengl.util.awt.TextRenderer; -import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_ORIENTATION_CUBE; -import com.willwinder.ugs.nbm.visualizer.shared.Renderable; +import com.jogamp.opengl.fixedfunc.GLPointerFunc; +import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; +import com.willwinder.ugs.nbm.visualizer.shader.PlainShader; +import com.willwinder.ugs.nbm.visualizer.shared.VertexObjectRenderable; import com.willwinder.universalgcodesender.model.Position; +import eu.mihosoft.vrl.v3d.CSG; +import eu.mihosoft.vrl.v3d.Cube; +import eu.mihosoft.vrl.v3d.Polygon; +import eu.mihosoft.vrl.v3d.RoundedCube; +import eu.mihosoft.vrl.v3d.Transform; +import eu.mihosoft.vrl.v3d.Vector3d; +import javafx.scene.paint.Color; + +import java.util.List; +import java.util.Optional; -import java.awt.Font; -import java.awt.geom.Rectangle2D; +import static com.jogamp.opengl.GL.GL_DEPTH_TEST; +import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_ORIENTATION_CUBE; /** * Draw a cube with the orientation labeled on the sides. - * + * * @author wwinder */ -public class OrientationCube extends Renderable { - private final float size; - private final float[] color = {0.8f, 0.8f, 0.8f}; - private final float[] border = {0.2f, 0.2f, 0.2f}; - - private TextRenderer renderer; - private float textScaleFactor; - - public OrientationCube(float s, String title) { - super(Integer.MIN_VALUE, title, VISUALIZER_OPTION_ORIENTATION_CUBE); - size = s; - } - - @Override - public boolean rotate() { - return true; - } - - @Override - public boolean center() { - return false; - } - - @Override - public void init(GLAutoDrawable drawable) { - renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 72)); - renderer.setColor(0.2f, 0.2f, 0.2f, 1f); - - // Compute the scale factor of the largest string which will make - // them all fit on the faces of the cube - Rectangle2D bounds = renderer.getBounds("Z+"); - float w = (float) bounds.getWidth(); - - textScaleFactor = size / (w * 1.7f); - } - - @Override - public void draw(GLAutoDrawable drawable, boolean idle, Position machineCoord, Position workCoord, Position focusMin, Position focusMax, double scaleFactor, Position mouseCoordinates, Position rotation) { - GL2 gl = drawable.getGL().getGL2(); - - int ySize = drawable.getDelegatedDrawable().getSurfaceHeight(); - int xSize = drawable.getDelegatedDrawable().getSurfaceWidth(); - - // Set viewport to the corner. - float fromEdge = 0.8f; - int squareSize = ySize-(int)(ySize*fromEdge); - gl.glViewport(0, (int)(ySize*fromEdge), squareSize, squareSize); - - gl.glPushMatrix(); +public class OrientationCube extends VertexObjectRenderable { + private final CSG model; + + protected void clear() { + super.clear(); + } + + public OrientationCube(String title) { + super(Integer.MIN_VALUE, title, VISUALIZER_OPTION_ORIENTATION_CUBE, new PlainShader()); + reloadPreferences(new VisualizerOptions()); + model = generateModel(); + } + + @Override + public boolean center() { + return false; + } + + private CSG generateModel() { + double size = 0.5; + + Color faceColor = Color.LIGHTGRAY; + Color cornerColor = Color.GRAY; + + CSG cube = new Cube(size, size * 0.8, size * 0.8).toCSG() + .union(new Cube(size * 0.8, size * 0.8, size).toCSG()) + .union(new Cube(size * 0.8, size, size * 0.8).toCSG()) + .setColor(faceColor); + + CSG corners = new RoundedCube(size).cornerRadius(0.03).toCSG().setColor(cornerColor); + + CSG result = cube.union(corners); + + CSG text = CSG.text("Z-", 1) + .setColor(Color.BLUE) + .transformed(new Transform().scale(0.01).rot(180, 0, 0).translate(-15, -10, 25)); + result = result.dumbUnion(text); + + text = CSG.text("Z+", 1) + .setColor(Color.BLUE) + .transformed(new Transform().scale(0.01).translate(-16, -10, 25)); + result = result.dumbUnion(text); + + text = CSG.text("Y+", 1) + .setColor(Color.GREEN) + .transformed(new Transform().scale(0.01).rot(90, 0, 180).translate(-16, -10, 25)); + result = result.dumbUnion(text); + + text = CSG.text("Y-", 1) + .setColor(Color.GREEN) + .transformed(new Transform().scale(0.01).rot(270, 0, 0).translate(-14, -10, 25)); + result = result.dumbUnion(text); + + text = CSG.text("X-", 1) + .setColor(Color.RED) + .transformed(new Transform().scale(0.01).rot(270, 90, 0).translate(-14, -10, 25)); + result = result.dumbUnion(text); + + text = CSG.text("X+", 1) + .setColor(Color.RED) + .transformed(new Transform().scale(0.01).rot(270, 270, 0).translate(-16, -10, 25)); + result = result.dumbUnion(text); + + return result.scale(0.8); + } + + private void generateBuffers(CSG csg) { + List polygons = csg.triangulate().getPolygons(); + + for (Polygon polygon : polygons) { + Color color = Optional.ofNullable(polygon.getColor()).orElse(Color.GRAY); + float[] colorArray = new float[]{(float) color.getRed(), (float) color.getGreen(), (float) color.getBlue(), 1f}; + + Vector3d a = polygon.getPoints().get(0); + Vector3d b = polygon.getPoints().get(1); + Vector3d c = polygon.getPoints().get(2); + Vector3d normal = b.minus(a).cross(c.minus(a)).normalized(); + + polygon.getPoints().forEach(point -> { + addVertex(point.getX(), point.getY(), point.getZ()); + addNormal(normal.getX(), normal.getY(), normal.getZ()); + addColor(colorArray); + }); + } + } + + @Override + public void render(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); + int ySize = drawable.getDelegatedDrawable().getSurfaceHeight(); + int xSize = drawable.getDelegatedDrawable().getSurfaceWidth(); + + // Set viewport to the corner. + float fromEdge = 0.8f; + int squareSize = ySize - (int) (ySize * fromEdge); + + + gl.glViewport(0, (int) (ySize * fromEdge), squareSize, squareSize); + gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION); gl.glLoadIdentity(); - gl.glOrtho(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5); //, maxSide, maxSide, maxSide, maxSide, maxSide); + gl.glOrtho(-0.5, 0.5, -0.5, 0.5, -1, 2); gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - - drawCube(gl); - gl.glPopMatrix(); - - gl.glViewport(0, 0, xSize, ySize); - } - - private void drawCube(GL2 gl) { - // Six faces of cube - // Top face - gl.glPushMatrix(); - gl.glRotatef(-90, 1, 0, 0); - gl.glRotatef(180, 0, 0, 1); - drawFace(gl, size, color, border, "Y+"); - gl.glPopMatrix(); - - // Front face - drawFace(gl, size, color, border, "Z+"); - - // Right face - gl.glPushMatrix(); - gl.glRotatef(90, 0, 1, 0); - - gl.glPushMatrix(); - gl.glRotatef(90, 0, 0, 1); - drawFace(gl, size, color, border, "X+"); - gl.glPopMatrix(); - - // Back face - gl.glRotatef(90, 0, 1, 0); - gl.glPushMatrix(); - gl.glRotatef(180, 0, 0, 1); - drawFace(gl, size, color, border, "Z-"); - gl.glPopMatrix(); - - // Left face - gl.glRotatef(90, 0, 1, 0); - gl.glRotatef(-90, 0, 0, 1); - drawFace(gl, size, color, border, "X-"); - gl.glPopMatrix(); - // Bottom face - gl.glPushMatrix(); - gl.glRotatef(90, 1, 0, 0); - drawFace(gl, size, color, border, "Y-"); - gl.glPopMatrix(); - } - - private void drawFace(GL2 gl, - float faceSize, - float[] color, - float[] border, - String text) { - float halfFaceSize = faceSize / 2; - float borderSize = halfFaceSize * 0.8f; - float layer2 = halfFaceSize + 0.001f; - // Face is centered around the local coordinate system's z axis, - // at a z depth of faceSize / 2 - gl.glColor3f(border[0], border[1], border[2]); - gl.glBegin(GL_QUADS); - gl.glVertex3f(-halfFaceSize, -halfFaceSize, halfFaceSize); - gl.glVertex3f( halfFaceSize, -halfFaceSize, halfFaceSize); - gl.glVertex3f( halfFaceSize, halfFaceSize, halfFaceSize); - gl.glVertex3f(-halfFaceSize, halfFaceSize, halfFaceSize); - gl.glEnd(); - - gl.glColor3f(color[0], color[1], color[2]); - gl.glBegin(GL_QUADS); - gl.glVertex3f(-borderSize, -borderSize, layer2); - gl.glVertex3f( borderSize, -borderSize, layer2); - gl.glVertex3f( borderSize, borderSize, layer2); - gl.glVertex3f(-borderSize, borderSize, layer2); - gl.glEnd(); - - // Now draw the overlaid text. In this setting, we don't want the - // text on the backward-facing faces to be visible, so we enable - // back-face culling; and since we're drawing the text over other - // geometry, to avoid z-fighting we disable the depth test. We - // could plausibly also use glPolygonOffset but this is simpler. - // Note that because the TextRenderer pushes the enable state - // internally we don't have to reset the depth test or cull face - // bits after we're done. - renderer.begin3DRendering(); - gl.glDisable(GL_DEPTH_TEST); - gl.glEnable(GL_CULL_FACE); - // Note that the defaults for glCullFace and glFrontFace are - // GL_BACK and GL_CCW, which match the TextRenderer's definition - // of front-facing text. - Rectangle2D bounds = renderer.getBounds(text); - float w = (float) bounds.getWidth(); - float h = (float) bounds.getHeight(); - renderer.draw3D(text, - w / -2.0f * textScaleFactor, - h / -2.0f * textScaleFactor, - layer2, - textScaleFactor); - renderer.end3DRendering(); - gl.glDisable(GL_CULL_FACE); - gl.glEnable(GL_DEPTH_TEST); - } + + gl.glEnable(GL_DEPTH_TEST); + int count = getVertexCount(); + gl.glDrawArrays(GL.GL_TRIANGLES, 0, count); + + gl.glDisableClientState(GLPointerFunc.GL_NORMAL_ARRAY); + gl.glDisableClientState(GLPointerFunc.GL_VERTEX_ARRAY); + gl.glDisableClientState(GLPointerFunc.GL_COLOR_ARRAY); + + gl.glViewport(0, 0, xSize, ySize); + } + + @Override + public void reloadModel(GL2 gl, Position bottomLeft, Position topRight, double scaleFactor) { + generateBuffers(model); + } } diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Plane.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Plane.java index 28446c3e96..73ec254be4 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Plane.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/renderables/Plane.java @@ -20,17 +20,20 @@ This file is part of Universal Gcode Sender (UGS). import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GLAutoDrawable; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; -import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_PLANE_COLOR; +import com.willwinder.ugs.nbm.visualizer.shader.PlainShader; import com.willwinder.ugs.nbm.visualizer.shared.VertexObjectRenderable; import com.willwinder.universalgcodesender.model.Position; +import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_PLANE_COLOR; + public class Plane extends VertexObjectRenderable { private float[] gridPlaneColor; public Plane(String title) { - super(7, title, VisualizerOptions.VISUALIZER_OPTION_PLANE); + super(7, title, VisualizerOptions.VISUALIZER_OPTION_PLANE, new PlainShader()); } @Override @@ -40,7 +43,8 @@ public void reloadPreferences(VisualizerOptions vo) { } @Override - public void render(GL2 gl) { + public void render(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); gl.glDisable(GL.GL_DEPTH_TEST); gl.glDrawArrays(GL.GL_TRIANGLES, 0, getVertexCount()); } diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/PlainShader.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/PlainShader.java index 446296def0..26aa08f3a5 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/PlainShader.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/PlainShader.java @@ -33,36 +33,54 @@ This file is part of Universal Gcode Sender (UGS). * @author Joacim Breiler */ public class PlainShader implements Shader { + private final String vertexShader; + private final String fragmentShader; private int shaderProgramId; - private int shaderVertexIndex; - private int shaderColorIndex; + + public PlainShader() { + this("/shaders/plain.vert.glsl", "/shaders/plain.frag.glsl"); + } + + public PlainShader(String vertexShader, String fragmentShader) { + this.vertexShader = vertexShader; + this.fragmentShader = fragmentShader; + } public void init(GL2 gl) { try { - InputStream vertexShaderInputStream = getClass().getResourceAsStream("/shaders/plain.vert.glsl"); + InputStream vertexShaderInputStream = getClass().getResourceAsStream(vertexShader); if (vertexShaderInputStream == null) { throw new IOException("Could not find vertex shader file"); } - InputStream fragmentShaderInputStream = getClass().getResourceAsStream("/shaders/plain.frag.glsl"); + InputStream fragmentShaderInputStream = getClass().getResourceAsStream(fragmentShader); if (fragmentShaderInputStream == null) { throw new IOException("Could not find fragment shader file"); } shaderProgramId = ShaderLoader.loadProgram(gl, IOUtils.toString(vertexShaderInputStream, StandardCharsets.UTF_8), IOUtils.toString(fragmentShaderInputStream, StandardCharsets.UTF_8)); - shaderVertexIndex = gl.glGetAttribLocation(shaderProgramId, "inPosition"); - shaderColorIndex = gl.glGetAttribLocation(shaderProgramId, "inColor"); + + gl.glBindAttribLocation(shaderProgramId, 0, "vertexPosition"); + gl.glBindAttribLocation(shaderProgramId, 1, "vertexNormal"); + gl.glBindAttribLocation(shaderProgramId, 2, "vertexColor"); } catch (IOException e) { throw new GLException(e); } } + @Override public int getShaderVertexIndex() { - return shaderVertexIndex; + return 0; + } + + @Override + public int getShaderNormalIndex() { + return 1; } + @Override public int getShaderColorIndex() { - return shaderColorIndex; + return 3; } public void dispose(GL2 gl) { diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/Shader.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/Shader.java index 2b2c5af795..d29be97092 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/Shader.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shader/Shader.java @@ -45,4 +45,25 @@ public interface Shader { * @return the id of the shader program */ int getProgramId(); + + /** + * Get the shader parameter index for vertices + * + * @return the index + */ + int getShaderVertexIndex(); + + /** + * Get the shader parameter index for colors + * + * @return the index + */ + int getShaderColorIndex(); + + /** + * Get the shader parameter index for normals + * + * @return the index + */ + int getShaderNormalIndex(); } diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/GcodeRenderer.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/GcodeRenderer.java index 13cd04b6ac..8801828b31 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/GcodeRenderer.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/GcodeRenderer.java @@ -23,11 +23,8 @@ This file is part of Universal Gcode Sender (UGS). import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLEventListener; import com.jogamp.opengl.JoglVersion; -import static com.jogamp.opengl.fixedfunc.GLMatrixFunc.GL_MODELVIEW; -import static com.jogamp.opengl.fixedfunc.GLMatrixFunc.GL_PROJECTION; import com.jogamp.opengl.glu.GLU; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; -import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_BG; import com.willwinder.ugs.nbm.visualizer.renderables.Grid; import com.willwinder.ugs.nbm.visualizer.renderables.MachineBoundries; import com.willwinder.ugs.nbm.visualizer.renderables.MouseOver; @@ -56,6 +53,10 @@ This file is part of Universal Gcode Sender (UGS). import java.util.logging.Level; import java.util.logging.Logger; +import static com.jogamp.opengl.fixedfunc.GLMatrixFunc.GL_MODELVIEW; +import static com.jogamp.opengl.fixedfunc.GLMatrixFunc.GL_PROJECTION; +import static com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions.VISUALIZER_OPTION_BG; + /** * 3D Canvas for GCode Visualizer * @@ -121,7 +122,7 @@ public class GcodeRenderer implements GLEventListener, IRenderableRegistrationSe * Constructor. */ public GcodeRenderer() { - eye = new Position(0, 0, 1.5); + eye = new Position(0, 0, 1.5); center = new Position(0, 0, 0); objectMin = new Position(-10, -10, -10); objectMax = new Position(10, 10, 10); @@ -239,18 +240,15 @@ public void init(GLAutoDrawable drawable) { float[] diffuse = {.6f, .6f, .6f, 1.0f}; float[] position = {0f, 0f, 20f, 1.0f}; + gl.glEnable(GL2.GL_LIGHT0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, ambient, 0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, diffuse, 0); - gl.glEnable(GL2.GL_LIGHT0); gl.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, position, 0); // Allow glColor to set colors gl.glEnable(GL2.GL_COLOR_MATERIAL); gl.glColorMaterial(GL.GL_FRONT, GL2.GL_DIFFUSE); gl.glColorMaterial(GL.GL_FRONT, GL2.GL_AMBIENT); - //gl.glColorMaterial(GL.GL_FRONT_AND_BACK, GL2.GL_AMBIENT_AND_DIFFUSE); - //gl.glColorMaterial(GL.GL_FRONT, GL2.GL_SPECULAR); - float diffuseMaterial[] = {0.5f, 0.5f, 0.5f, 1.0f}; @@ -469,7 +467,7 @@ private void initRenderables() { objects.add(new MachineBoundries(Localization.getString("platform.visualizer.renderable.machine-boundries"))); objects.add(new Tool(Localization.getString("platform.visualizer.renderable.tool-location"))); objects.add(new MouseOver(Localization.getString("platform.visualizer.renderable.mouse-indicator"))); - objects.add(new OrientationCube(0.5f, Localization.getString("platform.visualizer.renderable.orientation-cube"))); + objects.add(new OrientationCube(Localization.getString("platform.visualizer.renderable.orientation-cube"))); objects.add(new Grid(Localization.getString("platform.visualizer.renderable.grid"))); objects.add(new Plane(Localization.getString("platform.visualizer.renderable.grid"))); Collections.sort(objects); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/VertexObjectRenderable.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/VertexObjectRenderable.java index 3d3f34166c..3777d2593b 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/VertexObjectRenderable.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/shared/VertexObjectRenderable.java @@ -3,46 +3,49 @@ import com.jogamp.opengl.GL2; import com.jogamp.opengl.GLAutoDrawable; import com.willwinder.ugs.nbm.visualizer.options.VisualizerOptions; -import com.willwinder.ugs.nbm.visualizer.shader.PlainShader; +import com.willwinder.ugs.nbm.visualizer.shader.Shader; import com.willwinder.ugs.nbm.visualizer.utils.RenderableUtils; -import static com.willwinder.ugs.nbm.visualizer.utils.RenderableUtils.bindColorBuffer; -import static com.willwinder.ugs.nbm.visualizer.utils.RenderableUtils.bindVertexBuffer; -import static com.willwinder.ugs.nbm.visualizer.utils.RenderableUtils.bindVertexObject; import com.willwinder.universalgcodesender.model.Position; import java.util.ArrayList; import java.util.List; +import static com.willwinder.ugs.nbm.visualizer.utils.RenderableUtils.*; + /** * A base class for rendering a vertex buffer object using a shader */ public abstract class VertexObjectRenderable extends Renderable { private double stepSize; private final List vertexList = new ArrayList<>(); + private final List normalList = new ArrayList<>(); private final List colorList = new ArrayList<>(); private Position objectMin = Position.ZERO; private Position objectMax = Position.ZERO; - private PlainShader shader; + private Shader shader; private boolean reloadModel; private int vertexObjectId; private int vertexBufferId; private int colorBufferId; + private int normalBufferId; protected double getStepSize() { return stepSize; } - protected VertexObjectRenderable(int priority, String title, String enabledOptionKey) { + protected VertexObjectRenderable(int priority, String title, String enabledOptionKey, Shader shader) { super(priority, title, enabledOptionKey); + this.shader = shader; reloadPreferences(new VisualizerOptions()); } - private void clear() { + protected void clear() { vertexList.clear(); colorList.clear(); + normalList.clear(); } @Override @@ -55,6 +58,10 @@ public boolean center() { return true; } + protected void addNormal(double x, double y, double z) { + RenderableUtils.addVertex(normalList, x, y, z); + } + protected void addVertex(double x, double y, double z) { RenderableUtils.addVertex(vertexList, x, y, z); } @@ -77,7 +84,6 @@ public void reloadPreferences(VisualizerOptions vo) { @Override public void init(GLAutoDrawable drawable) { - shader = new PlainShader(); shader.init(drawable.getGL().getGL2()); } @@ -117,8 +123,7 @@ public final void draw(GLAutoDrawable drawable, boolean idle, Position machineCo // Bind the VAO containing the vertex data gl.glBindVertexArray(vertexObjectId); - - render(gl); + render(drawable); // Unbind the VAO gl.glBindVertexArray(0); @@ -127,18 +132,19 @@ public final void draw(GLAutoDrawable drawable, boolean idle, Position machineCo private void updateBuffers(GL2 gl) { - gl.glDeleteBuffers(2, new int[]{vertexBufferId, colorBufferId}, 0); + gl.glDeleteBuffers(2, new int[]{vertexBufferId, colorBufferId, normalBufferId}, 0); vertexObjectId = bindVertexObject(gl); vertexBufferId = bindVertexBuffer(gl, vertexList, shader.getShaderVertexIndex()); colorBufferId = bindColorBuffer(gl, colorList, shader.getShaderColorIndex()); + normalBufferId = bindNormalBuffer(gl, normalList, shader.getShaderNormalIndex()); } /** * A method to be used for rendering the vertex buffer. * - * @param gl the current gl context + * @param drawable the current gl context */ - public abstract void render(GL2 gl); + public abstract void render(GLAutoDrawable drawable); /** diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/RenderableUtils.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/RenderableUtils.java index ab86d52a19..1c3f8b0888 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/RenderableUtils.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/RenderableUtils.java @@ -53,6 +53,33 @@ public static int bindColorBuffer(GL2 gl, List colorList, int vertexBuffe return cbo[0]; } + /** + * Generates and binds a color buffer + * + * @param gl the GL context to use + * @param normalList a list of float values containing the vertex normal + * @param normalBufferIndex the parameter index of the shader to bind to + * @return the id of the color buffer to be reference to when rendering + */ + public static int bindNormalBuffer(GL2 gl, List normalList, int normalBufferIndex) { + // Create and upload the Normal Buffer Object (NBO) for colors + int[] nbo = new int[1]; + gl.glGenBuffers(1, nbo, 0); + + gl.glBindBuffer(GL.GL_ARRAY_BUFFER, nbo[0]); + + // Upload vertex colors to the GPU + FloatBuffer normalBuffer = Buffers.newDirectFloatBuffer(ArrayUtils.toPrimitive(normalList.toArray(new Float[0]))); + gl.glBufferData(GL.GL_ARRAY_BUFFER, normalBuffer.capacity() * Float.BYTES, normalBuffer, GL.GL_STATIC_DRAW); + + // Specify vertex attribute layout (index 1 for color) + gl.glEnableVertexAttribArray(normalBufferIndex); + gl.glVertexAttribPointer(normalBufferIndex, 4, GL.GL_FLOAT, false, 4 * Float.BYTES, 0); + + return nbo[0]; + } + + public static int bindVertexBuffer(GL2 gl, List vertexList, int positionIndex) { // Create a Vertex Buffer Object (VBO) int[] vbo = new int[1]; @@ -61,7 +88,7 @@ public static int bindVertexBuffer(GL2 gl, List vertexList, int positionI // Send vertex data to the GPU FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(ArrayUtils.toPrimitive(vertexList.toArray(new Float[0]))); - gl.glBufferData(GL.GL_ARRAY_BUFFER, vertexBuffer.limit() * Float.BYTES, vertexBuffer, GL.GL_STATIC_DRAW); + gl.glBufferData(GL.GL_ARRAY_BUFFER, vertexBuffer.capacity() * Float.BYTES, vertexBuffer, GL.GL_STATIC_DRAW); // Specify the layout of the vertex data gl.glEnableVertexAttribArray(positionIndex); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/ShaderLoader.java b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/ShaderLoader.java index 474066e2b7..c821736bdf 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/ShaderLoader.java +++ b/ugs-platform/ugs-platform-visualizer/src/main/java/com/willwinder/ugs/nbm/visualizer/utils/ShaderLoader.java @@ -108,6 +108,11 @@ public static int loadProgram(final GL2ES2 gl, final String vertexShaderSource, throw new GLException(log); } + // Explicitly bind the parameters locations + gl.glBindAttribLocation(program, 0, "gl_Position"); + gl.glBindAttribLocation(program, 1, "gl_Normal"); + gl.glBindAttribLocation(program, 3, "gl_Color"); + // Clean up the shaders gl.glDeleteShader(vertexShaderId); gl.glDeleteShader(fragmentShaderId); diff --git a/ugs-platform/ugs-platform-visualizer/src/main/resources/shaders/plain.vert.glsl b/ugs-platform/ugs-platform-visualizer/src/main/resources/shaders/plain.vert.glsl index 3cf4459146..06d6aaa851 100644 --- a/ugs-platform/ugs-platform-visualizer/src/main/resources/shaders/plain.vert.glsl +++ b/ugs-platform/ugs-platform-visualizer/src/main/resources/shaders/plain.vert.glsl @@ -1,16 +1,12 @@ #version 110 -// Input vertex position and color -attribute vec3 inPosition; -attribute vec4 inColor; - // Output color to the fragment shader varying vec4 fragColor; void main() { // Set the vertex position - gl_Position = gl_ModelViewProjectionMatrix * vec4(inPosition, 1.0); + gl_Position = ftransform(); // Pass the vertex color to the fragment shader - fragColor = inColor; + fragColor = gl_Color; } \ No newline at end of file