Export the scene and all the assets used to a single file for sharing on another computer. * This is not the same as saving the scene.
*/ public class ExportScene extends AbstractAction { - private static final Logger logger = LoggerFactory.getLogger(SaveScene.class); - private static final JFileChooser chooser = new JFileChooser(); + private final Logger logger = LoggerFactory.getLogger(ExportScene.class); + public static final FileNameExtensionFilter ZIP_FILTER = new FileNameExtensionFilter("ZIP files", "zip"); + private final JFileChooser chooser = new JFileChooser(); public ExportScene() { super(); putValue(Action.NAME,"Export Scene"); putValue(Action.SMALL_ICON,new ImageIcon(Objects.requireNonNull(getClass().getResource("icons8-export-16.png")))); - putValue(SHORT_DESCRIPTION,"Export the scene and all the assets used to a single file."); + putValue(SHORT_DESCRIPTION,"Export the scene and all the assets used to a ZIP file."); } /** @@ -32,15 +41,29 @@ public ExportScene() { */ @Override public void actionPerformed(ActionEvent e) { - chooser.setFileFilter(RobotOverlord.FILE_FILTER); + chooser.setFileFilter(ZIP_FILTER); Component source = (Component) e.getSource(); JFrame parentFrame = (JFrame)SwingUtilities.getWindowAncestor(source); + // make sure to add the extension if the user doesn't + chooser.addActionListener((e2) -> { + if (JFileChooser.APPROVE_SELECTION.equals(e2.getActionCommand())) { + String[] extensions = ZIP_FILTER.getExtensions(); + File f = chooser.getSelectedFile(); + String fname = f.getName().toLowerCase(); + boolean matches = Arrays.stream(extensions).anyMatch((ext) -> fname.toLowerCase().endsWith("." + ext)); + if (!matches) { + f = new File(f.getPath() + "." + extensions[0]); // append the first extension from ZIP_FILTER + chooser.setSelectedFile(f); + } + } + }); + if (chooser.showSaveDialog(parentFrame) == JFileChooser.APPROVE_OPTION) { // check before overwriting. File selectedFile = chooser.getSelectedFile(); if (selectedFile.exists()) { - int response = JOptionPane.showConfirmDialog(null, + int response = JOptionPane.showConfirmDialog(SwingUtilities.getWindowAncestor(source), "Do you want to replace the existing file?", "Confirm Overwrite", JOptionPane.YES_NO_OPTION, @@ -51,12 +74,87 @@ public void actionPerformed(ActionEvent e) { } String absolutePath = chooser.getSelectedFile().getAbsolutePath(); - exportScene(absolutePath); + commitExport(absolutePath); } } - private void exportScene(String absolutePath) { - logger.info("Export scene to {}", absolutePath); - logger.error("Not implemented yet."); + private void commitExport(String absolutePath) { + logger.info("Exporting to {}", absolutePath); + + JSONObject json = Registry.getScene().toJSON(); + + ListDraw the background. This may be a skybox or a solid color.
*TODO use the OpenGL cube map texture?
*/ -public class DrawBackground implements RenderPass { +public class DrawBackground extends AbstractRenderPass { private static final Logger logger = LoggerFactory.getLogger(DrawBackground.class); - private int activeStatus = ALWAYS; private final ColorRGB eraseColor = new ColorRGB(64,64,128); private ShaderProgram shader; private final Mesh mesh = new Mesh(); private final TextureWithMetadata texture; - private int canvasWidth, canvasHeight; public DrawBackground() { + super("Erase/Background"); + // build a box mesh.setRenderStyle(GL3.GL_QUADS); @@ -80,22 +78,7 @@ public DrawBackground() { mesh.addTexCoord(a,f); mesh.addVertex(-v, -v, -v); texture = Registry.textureFactory.load("/skybox/skybox.png"); - } - - /** - * @return NEVER, SOMETIMES, or ALWAYS - */ - @Override - public int getActiveStatus() { - return activeStatus; - } - - /** - * @param status NEVER, SOMETIMES, or ALWAYS - */ - @Override - public void setActiveStatus(int status) { - activeStatus = status; + texture.setDoNotExport(true); } /** @@ -125,15 +108,6 @@ public void dispose(GLAutoDrawable glAutoDrawable) { shader.delete(gl3); } - @Override - public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) { - canvasWidth = width; - canvasHeight = height; - } - - @Override - public void display(GLAutoDrawable glAutoDrawable) {} - @Override public void draw() { GL3 gl3 = GLContext.getCurrentGL().getGL3(); diff --git a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawBoundingBoxes.java b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawBoundingBoxes.java similarity index 83% rename from src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawBoundingBoxes.java rename to src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawBoundingBoxes.java index 2acb272c2..e56bd9b31 100644 --- a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawBoundingBoxes.java +++ b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawBoundingBoxes.java @@ -1,4 +1,4 @@ -package com.marginallyclever.ro3.render.renderpasses; +package com.marginallyclever.ro3.apps.render.renderpasses; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GLAutoDrawable; @@ -10,12 +10,7 @@ import com.marginallyclever.ro3.Registry; import com.marginallyclever.ro3.node.Node; import com.marginallyclever.ro3.node.nodes.Camera; -import com.marginallyclever.ro3.node.nodes.Material; import com.marginallyclever.ro3.node.nodes.MeshInstance; -import com.marginallyclever.ro3.node.nodes.Pose; -import com.marginallyclever.ro3.render.RenderPass; -import com.marginallyclever.ro3.texture.TextureWithMetadata; -import com.marginallyclever.robotoverlord.components.shapes.Box; import com.marginallyclever.robotoverlord.systems.render.ShaderProgram; import com.marginallyclever.robotoverlord.systems.render.mesh.Mesh; import org.slf4j.Logger; @@ -30,14 +25,14 @@ /** * Draw the bounding box of each {@link MeshInstance} in the scene. */ -public class DrawBoundingBoxes implements RenderPass { +public class DrawBoundingBoxes extends AbstractRenderPass { private static final Logger logger = LoggerFactory.getLogger(DrawBoundingBoxes.class); - private int activeStatus = ALWAYS; private ShaderProgram shader; private final Mesh mesh = new Mesh(); - private int canvasWidth, canvasHeight; public DrawBoundingBoxes() { + super("Bounding Boxes"); + mesh.setRenderStyle(GL3.GL_LINES); // add 8 points of a unit cube centered on the origin mesh.addVertex(-0.5f, 0.5f, 0.5f); @@ -65,30 +60,6 @@ public DrawBoundingBoxes() { mesh.addIndex(3); mesh.addIndex(7); } - /** - * @return NEVER, SOMETIMES, or ALWAYS - */ - @Override - public int getActiveStatus() { - return activeStatus; - } - - /** - * @param status NEVER, SOMETIMES, or ALWAYS - */ - @Override - public void setActiveStatus(int status) { - activeStatus = status; - } - - /** - * @return the localized name of this overlay - */ - @Override - public String getName() { - return "Bounding Boxes"; - } - @Override public void init(GLAutoDrawable glAutoDrawable) { GL3 gl3 = glAutoDrawable.getGL().getGL3(); @@ -108,15 +79,6 @@ public void dispose(GLAutoDrawable glAutoDrawable) { shader.delete(gl3); } - @Override - public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) { - canvasWidth = width; - canvasHeight = height; - } - - @Override - public void display(GLAutoDrawable glAutoDrawable) {} - @Override public void draw() { Camera camera = Registry.getActiveCamera(); diff --git a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawCameras.java b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawCameras.java similarity index 83% rename from src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawCameras.java rename to src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawCameras.java index dbcc73c02..6963b7c3f 100644 --- a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawCameras.java +++ b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawCameras.java @@ -1,4 +1,4 @@ -package com.marginallyclever.ro3.render.renderpasses; +package com.marginallyclever.ro3.apps.render.renderpasses; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GLAutoDrawable; @@ -7,7 +7,6 @@ import com.marginallyclever.convenience.helpers.ResourceHelper; import com.marginallyclever.ro3.Registry; import com.marginallyclever.ro3.node.nodes.Camera; -import com.marginallyclever.ro3.render.RenderPass; import com.marginallyclever.robotoverlord.systems.render.ShaderProgram; import com.marginallyclever.robotoverlord.systems.render.mesh.Mesh; import org.slf4j.Logger; @@ -19,14 +18,14 @@ /** * Draws each {@link Camera} as a pyramid approximating the perspective view frustum. */ -public class DrawCameras implements RenderPass { +public class DrawCameras extends AbstractRenderPass { private static final Logger logger = LoggerFactory.getLogger(DrawCameras.class); - private int activeStatus = ALWAYS; private final Mesh mesh = new Mesh(); private ShaderProgram shader; - private int canvasWidth,canvasHeight; public DrawCameras() { + super("Cameras"); + // add mesh to a list that can be unloaded and reloaded as needed. mesh.setRenderStyle(GL3.GL_LINES); Vector3d a = new Vector3d(-1,-1,-1); @@ -47,30 +46,6 @@ public DrawCameras() { mesh.addColor(0,0,0,1); mesh.addVertex((float)a.x, (float)a.y, (float)a.z); } - /** - * @return NEVER, SOMETIMES, or ALWAYS - */ - @Override - public int getActiveStatus() { - return activeStatus; - } - - /** - * @param status NEVER, SOMETIMES, or ALWAYS - */ - @Override - public void setActiveStatus(int status) { - activeStatus = status; - } - - /** - * @return the localized name of this overlay - */ - @Override - public String getName() { - return "Cameras"; - } - @Override public void init(GLAutoDrawable glAutoDrawable) { GL3 gl3 = glAutoDrawable.getGL().getGL3(); @@ -90,15 +65,6 @@ public void dispose(GLAutoDrawable glAutoDrawable) { shader.delete(gl3); } - @Override - public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) { - canvasWidth = width; - canvasHeight = height; - } - - @Override - public void display(GLAutoDrawable glAutoDrawable) {} - @Override public void draw() { Camera camera = Registry.getActiveCamera(); diff --git a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawDHParameters.java b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawDHParameters.java similarity index 83% rename from src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawDHParameters.java rename to src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawDHParameters.java index 36e2bee9b..04b9e3ee4 100644 --- a/src/main/java/com/marginallyclever/ro3/render/renderpasses/DrawDHParameters.java +++ b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawDHParameters.java @@ -1,4 +1,4 @@ -package com.marginallyclever.ro3.render.renderpasses; +package com.marginallyclever.ro3.apps.render.renderpasses; import com.jogamp.opengl.GL3; import com.jogamp.opengl.GLAutoDrawable; @@ -11,7 +11,6 @@ import com.marginallyclever.ro3.node.nodes.Camera; import com.marginallyclever.ro3.node.nodes.DHParameter; import com.marginallyclever.ro3.node.nodes.Pose; -import com.marginallyclever.ro3.render.RenderPass; import com.marginallyclever.robotoverlord.systems.render.ShaderProgram; import com.marginallyclever.robotoverlord.systems.render.mesh.Mesh; import org.slf4j.Logger; @@ -26,14 +25,14 @@ /** * Draws each {@link DHParameter} as two lines from the previous joint to the current joint. */ -public class DrawDHParameters implements RenderPass { +public class DrawDHParameters extends AbstractRenderPass { private static final Logger logger = LoggerFactory.getLogger(DrawDHParameters.class); - private int activeStatus = ALWAYS; private final Mesh mesh = new Mesh(); private ShaderProgram shader; - private int canvasWidth,canvasHeight; public DrawDHParameters() { + super("DH Parameters"); + // add mesh to a list that can be unloaded and reloaded as needed. mesh.setRenderStyle(GL3.GL_LINES); // line d @@ -44,30 +43,6 @@ public DrawDHParameters() { mesh.addColor(1,0,0,1); mesh.addVertex(0,0,0); } - /** - * @return NEVER, SOMETIMES, or ALWAYS - */ - @Override - public int getActiveStatus() { - return activeStatus; - } - - /** - * @param status NEVER, SOMETIMES, or ALWAYS - */ - @Override - public void setActiveStatus(int status) { - activeStatus = status; - } - - /** - * @return the localized name of this overlay - */ - @Override - public String getName() { - return "DH Parameters"; - } - @Override public void init(GLAutoDrawable glAutoDrawable) { GL3 gl3 = glAutoDrawable.getGL().getGL3(); @@ -87,15 +62,6 @@ public void dispose(GLAutoDrawable glAutoDrawable) { shader.delete(gl3); } - @Override - public void reshape(GLAutoDrawable glAutoDrawable, int x, int y, int width, int height) { - canvasWidth = width; - canvasHeight = height; - } - - @Override - public void display(GLAutoDrawable glAutoDrawable) {} - @Override public void draw() { Camera camera = Registry.getActiveCamera(); diff --git a/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawGroundPlane.java b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawGroundPlane.java new file mode 100644 index 000000000..662d5ce38 --- /dev/null +++ b/src/main/java/com/marginallyclever/ro3/apps/render/renderpasses/DrawGroundPlane.java @@ -0,0 +1,87 @@ +package com.marginallyclever.ro3.apps.render.renderpasses; + +import com.jogamp.opengl.GL3; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.GLContext; +import com.marginallyclever.convenience.helpers.MatrixHelper; +import com.marginallyclever.convenience.helpers.ResourceHelper; +import com.marginallyclever.ro3.Registry; +import com.marginallyclever.ro3.node.nodes.Camera; +import com.marginallyclever.robotoverlord.systems.render.ShaderProgram; +import com.marginallyclever.robotoverlord.systems.render.mesh.Mesh; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.vecmath.Matrix4d; +import javax.vecmath.Vector3d; + +/** + * Draw the ground plane. + */ +public class DrawGroundPlane extends AbstractRenderPass { + private static final Logger logger = LoggerFactory.getLogger(DrawGroundPlane.class); + private final Mesh mesh = MatrixHelper.createMesh(1.0); + private ShaderProgram shader; + + public DrawGroundPlane() { + super("Ground plane"); + + mesh.setRenderStyle(GL3.GL_LINES); + int v = 1000; + int stepSize=100; + for(int s=-v;sA {@link Camera} is a {@link Pose} that can be used by a {@link com.marginallyclever.ro3.render.Viewport} to + *
A {@link Camera} is a {@link Pose} that can be used by a {@link Viewport} to * render the scene.
*For perspective rendering it has a vertical field of view and a near and far clipping plane.
*/ @@ -25,6 +25,7 @@ public class Camera extends Pose { private double fovY = 60; private double nearZ = 1; private double farZ = 1000; + private double orbitRadius = 50; public Camera() { super("Camera"); diff --git a/src/main/java/com/marginallyclever/ro3/texture/TextureFactory.java b/src/main/java/com/marginallyclever/ro3/texture/TextureFactory.java index 4b8068d9b..684bb200a 100644 --- a/src/main/java/com/marginallyclever/ro3/texture/TextureFactory.java +++ b/src/main/java/com/marginallyclever/ro3/texture/TextureFactory.java @@ -7,7 +7,6 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -56,4 +55,16 @@ public void unloadAll() { t.unload(); } } + + /** + * @return a list of all the sources used to load textures. + */ + public List