diff --git a/engine/src/core/com/jme3/util/xml/SAXUtil.java b/engine/src/core/com/jme3/util/xml/SAXUtil.java index 33b6f4288..1ac493657 100644 --- a/engine/src/core/com/jme3/util/xml/SAXUtil.java +++ b/engine/src/core/com/jme3/util/xml/SAXUtil.java @@ -102,12 +102,11 @@ public final class SAXUtil { public static boolean parseBool(String bool, boolean def) throws SAXException{ if (bool == null || bool.equals("")) return def; - else if (bool.equals("false")) - return false; - else if (bool.equals("true")) - return true; else - throw new SAXException("Expected a boolean, got'"+bool+"'"); + return Boolean.valueOf(bool); + //else + //else + // throw new SAXException("Expected a boolean, got'"+bool+"'"); } public static String parseString(String str, String def){ diff --git a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java index 6abb6d527..6e1d65125 100644 --- a/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -90,6 +90,7 @@ import jme3tools.converters.MipMapGenerator; import org.lwjgl.opengl.ARBDrawBuffers; //import org.lwjgl.opengl.ARBDrawInstanced; import org.lwjgl.opengl.ARBDrawInstanced; +import org.lwjgl.opengl.ARBFramebufferObject; import org.lwjgl.opengl.ARBMultisample; import org.lwjgl.opengl.ContextCapabilities; import org.lwjgl.opengl.EXTTextureArray; @@ -427,14 +428,17 @@ public class LwjglRenderer implements Renderer { } public void resetGLObjects() { + logger.log(Level.INFO, "Reseting objects and invalidating state"); objManager.resetObjects(); statistics.clearMemory(); invalidateState(); } public void cleanup() { + logger.log(Level.INFO, "Deleting objects and invalidating state"); objManager.deleteAllObjects(this); statistics.clearMemory(); + invalidateState(); } private void checkCap(Caps cap) { @@ -537,8 +541,8 @@ public class LwjglRenderer implements Renderer { if (state.isPointSprite() && !context.pointSprite) { // Only enable/disable sprite - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { + if (context.boundTextures[0] != null){ + if (context.boundTextureUnit != 0){ glActiveTexture(GL_TEXTURE0); context.boundTextureUnit = 0; } @@ -547,8 +551,8 @@ public class LwjglRenderer implements Renderer { } context.pointSprite = true; } else if (!state.isPointSprite() && context.pointSprite) { - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { + if (context.boundTextures[0] != null){ + if (context.boundTextureUnit != 0){ glActiveTexture(GL_TEXTURE0); context.boundTextureUnit = 0; } @@ -960,7 +964,7 @@ public class LwjglRenderer implements Renderer { } source.setId(id); - } else { + }else{ throw new RendererException("Cannot recompile shader source"); } @@ -1277,7 +1281,86 @@ public class LwjglRenderer implements Renderer { // TODO: support non-blit copies? } } + + private String getTargetBufferName(int buffer){ + switch (buffer){ + case GL_NONE: return "NONE"; + case GL_FRONT: return "GL_FRONT"; + case GL_BACK: return "GL_BACK"; + default: + if ( buffer >= GL_COLOR_ATTACHMENT0_EXT + && buffer <= GL_COLOR_ATTACHMENT15_EXT){ + return "GL_COLOR_ATTACHMENT" + + (buffer - GL_COLOR_ATTACHMENT0_EXT); + }else{ + return "UNKNOWN? " + buffer; + } + } + } + private void printRealRenderBufferInfo(FrameBuffer fb, RenderBuffer rb, String name){ + System.out.println("== Renderbuffer " + name + " =="); + System.out.println("RB ID: " + rb.getId()); + System.out.println("Is proper? " + glIsRenderbufferEXT(rb.getId())); + + int attachment = convertAttachmentSlot(rb.getSlot()); + + int type = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT, + attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT); + + int rbName = glGetFramebufferAttachmentParameterEXT(GL_DRAW_FRAMEBUFFER_EXT, + attachment, + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT); + + switch (type){ + case GL_NONE: + System.out.println("Type: None"); + return; // note: return from method as other queries will be invalid + case GL_TEXTURE: + System.out.println("Type: Texture"); + break; + case GL_RENDERBUFFER_EXT: + System.out.println("Type: Buffer"); + System.out.println("RB ID: " + rbName); + break; + } + + + + } + + private void printRealFrameBufferInfo(FrameBuffer fb) { + boolean doubleBuffer = glGetBoolean(GL_DOUBLEBUFFER); + String drawBuf = getTargetBufferName(glGetInteger(GL_DRAW_BUFFER)); + String readBuf = getTargetBufferName(glGetInteger(GL_READ_BUFFER)); + + int fbId = fb.getId(); + int curDrawBinding = glGetInteger(ARBFramebufferObject.GL_DRAW_FRAMEBUFFER_BINDING); + int curReadBinding = glGetInteger(ARBFramebufferObject.GL_READ_FRAMEBUFFER_BINDING); + + System.out.println("=== OpenGL FBO State ==="); + System.out.println("Context doublebuffered? " + doubleBuffer); + System.out.println("FBO ID: " + fbId); + System.out.println("Is proper? " + glIsFramebufferEXT(fbId)); + System.out.println("Is bound to draw? " + (fbId == curDrawBinding)); + System.out.println("Is bound to read? " + (fbId == curReadBinding)); + System.out.println("Draw buffer: " + drawBuf); + System.out.println("Read buffer: " + readBuf); + + if (context.boundFBO != fbId){ + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbId); + context.boundFBO = fbId; + } + + if (fb.getDepthBuffer() != null){ + printRealRenderBufferInfo(fb, fb.getDepthBuffer(), "Depth"); + } + for (int i = 0; i < fb.getNumColorBuffers(); i++){ + printRealRenderBufferInfo(fb, fb.getColorBuffer(i), "Color" + i); + } + } + private void checkFrameBufferError() { int status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); switch (status) { @@ -1290,7 +1373,7 @@ public class LwjglRenderer implements Renderer { case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: throw new IllegalStateException("Framebuffer has erronous attachment."); case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: - throw new IllegalStateException("Framebuffer is missing required attachment."); + throw new IllegalStateException("Framebuffer doesn't have any renderbuffers attached."); case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: throw new IllegalStateException("Framebuffer attachments must have same dimensions."); case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: @@ -1487,6 +1570,11 @@ public class LwjglRenderer implements Renderer { lastFb = null; } else { + if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null){ + throw new IllegalArgumentException("The framebuffer: " + fb + + "\nDoesn't have any color/depth buffers"); + } + if (fb.isUpdateNeeded()) { updateFrameBuffer(fb); } @@ -1544,13 +1632,14 @@ public class LwjglRenderer implements Renderer { assert fb.getId() >= 0; assert context.boundFBO == fb.getId(); lastFb = fb; - - try { - checkFrameBufferError(); - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "Problem FBO:\n{0}", fb); - throw ex; - } + } + + try { + checkFrameBufferError(); + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb); + printRealFrameBufferInfo(fb); + throw ex; } } @@ -1952,7 +2041,7 @@ public class LwjglRenderer implements Renderer { objManager.registerForCleanup(vb); //statistics.onNewVertexBuffer(); - + created = true; } @@ -1964,7 +2053,7 @@ public class LwjglRenderer implements Renderer { glBindBuffer(target, bufId); context.boundElementArrayVBO = bufId; //statistics.onVertexBufferUse(vb, true); - } else { + }else{ //statistics.onVertexBufferUse(vb, false); } } else { @@ -1973,7 +2062,7 @@ public class LwjglRenderer implements Renderer { glBindBuffer(target, bufId); context.boundArrayVBO = bufId; //statistics.onVertexBufferUse(vb, true); - } else { + }else{ //statistics.onVertexBufferUse(vb, false); } } @@ -2082,7 +2171,7 @@ public class LwjglRenderer implements Renderer { intBuf1.position(0).limit(1); glDeleteBuffers(intBuf1); vb.resetObject(); - + //statistics.onDeleteVertexBuffer(); } } @@ -2142,7 +2231,7 @@ public class LwjglRenderer implements Renderer { glBindBuffer(GL_ARRAY_BUFFER, bufId); context.boundArrayVBO = bufId; //statistics.onVertexBufferUse(vb, true); - } else { + }else{ //statistics.onVertexBufferUse(vb, false); } @@ -2189,7 +2278,7 @@ public class LwjglRenderer implements Renderer { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufId); context.boundElementArrayVBO = bufId; //statistics.onVertexBufferUse(indexBuf, true); - } else { + }else{ //statistics.onVertexBufferUse(indexBuf, true); } @@ -2314,9 +2403,9 @@ public class LwjglRenderer implements Renderer { } private void renderMeshVertexArray(Mesh mesh, int lod, int count) { - if (mesh.getId() == -1) { + if (mesh.getId() == -1){ updateVertexArray(mesh); - } else { + }else{ // TODO: Check if it was updated } @@ -2359,7 +2448,7 @@ public class LwjglRenderer implements Renderer { } //for (Entry entry : buffers) { // VertexBuffer vb = entry.getValue(); - for (int i = 0; i < buffersList.size(); i++) { + for (int i = 0; i < buffersList.size(); i++){ VertexBuffer vb = buffersList.get(i); if (vb.getBufferType() == Type.InterleavedData @@ -2391,10 +2480,10 @@ public class LwjglRenderer implements Renderer { return; } - if (context.pointSprite && mesh.getMode() != Mode.Points) { + if (context.pointSprite && mesh.getMode() != Mode.Points){ // XXX: Hack, disable point sprite mode if mesh not in point mode - if (context.boundTextures[0] != null) { - if (context.boundTextureUnit != 0) { + if (context.boundTextures[0] != null){ + if (context.boundTextureUnit != 0){ glActiveTexture(GL_TEXTURE0); context.boundTextureUnit = 0; } @@ -2417,7 +2506,7 @@ public class LwjglRenderer implements Renderer { // if (GLContext.getCapabilities().GL_ARB_vertex_array_object){ // renderMeshVertexArray(mesh, lod, count); // }else{ - renderMeshDefault(mesh, lod, count); + renderMeshDefault(mesh, lod, count); // } } } diff --git a/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java b/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java index f5ed50363..de32612a6 100644 --- a/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java +++ b/engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java @@ -38,13 +38,11 @@ import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeSystem; import com.jme3.system.JmeSystem.Platform; import java.awt.Canvas; -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import org.lwjgl.LWJGLException; +import org.lwjgl.LWJGLUtil; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; @@ -53,14 +51,23 @@ import org.lwjgl.opengl.PixelFormat; public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContext { + protected static final int TASK_NOTHING = 0, + TASK_DESTROY_DISPLAY = 1, + TASK_CREATE_DISPLAY = 2, + TASK_COMPLETE = 3; + +// protected static final boolean USE_SHARED_CONTEXT = +// Boolean.parseBoolean(System.getProperty("jme3.canvas.sharedctx", "true")); + + protected static final boolean USE_SHARED_CONTEXT = false; + private static final Logger logger = Logger.getLogger(LwjglDisplay.class.getName()); private Canvas canvas; private int width; private int height; - private final AtomicBoolean needRestoreCanvas = new AtomicBoolean(false); - private final AtomicBoolean needDestroyCanvas = new AtomicBoolean(false); - private final CyclicBarrier actionRequiredBarrier = new CyclicBarrier(2); + private final Object taskLock = new Object(); + private int desiredTask = TASK_NOTHING; private Thread renderThread; private boolean runningFirstTime = true; @@ -95,11 +102,19 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex return; } - logger.log(Level.INFO, "EDT: Notifying OGL that canvas is visible.."); - needRestoreCanvas.set(true); - - // NOTE: no need to wait for OGL to initialize the canvas, - // it can happen at any time. + logger.log(Level.INFO, "EDT: Telling OGL to create display .."); + synchronized (taskLock){ + desiredTask = TASK_CREATE_DISPLAY; +// while (desiredTask != TASK_COMPLETE){ +// try { +// taskLock.wait(); +// } catch (InterruptedException ex) { +// return; +// } +// } +// desiredTask = TASK_NOTHING; + } +// logger.log(Level.INFO, "EDT: OGL has created the display"); } @Override @@ -112,18 +127,19 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex // We must tell GL context to shutdown and wait for it to // shutdown, otherwise, issues will occur. - logger.log(Level.INFO, "EDT: Notifying OGL that canvas is about to become invisible.."); - needDestroyCanvas.set(true); - try { - actionRequiredBarrier.await(); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, "EDT: Interrupted! ", ex); - } catch (BrokenBarrierException ex){ - logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex); + logger.log(Level.INFO, "EDT: Telling OGL to destroy display .."); + synchronized (taskLock){ + desiredTask = TASK_DESTROY_DISPLAY; + while (desiredTask != TASK_COMPLETE){ + try { + taskLock.wait(); + } catch (InterruptedException ex){ + super.removeNotify(); + return; + } + } + desiredTask = TASK_NOTHING; } - - // Reset barrier for future use - actionRequiredBarrier.reset(); logger.log(Level.INFO, "EDT: Acknowledged receipt of canvas death"); // GL context is dead at this point @@ -168,37 +184,45 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex public Canvas getCanvas(){ return canvas; } - + @Override protected void runLoop(){ - if (needDestroyCanvas.getAndSet(false)){ - // Destroy canvas - logger.log(Level.INFO, "OGL: Received destroy request! Complying.."); - try { - listener.loseFocus(); - pauseCanvas(); - } finally { - try { - // Required to avoid deadlock if an exception occurs - actionRequiredBarrier.await(); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, "OGL: Interrupted! ", ex); - } catch (BrokenBarrierException ex) { - logger.log(Level.SEVERE, "OGL: Broken barrier! ", ex); + if (desiredTask != TASK_NOTHING){ + synchronized (taskLock){ + switch (desiredTask){ + case TASK_CREATE_DISPLAY: + logger.log(Level.INFO, "OGL: Creating display .."); + restoreCanvas(); + listener.gainFocus(); + desiredTask = TASK_NOTHING; + break; + case TASK_DESTROY_DISPLAY: + logger.log(Level.INFO, "OGL: Destroying display .."); + listener.loseFocus(); + pauseCanvas(); + break; } + desiredTask = TASK_COMPLETE; + taskLock.notifyAll(); } - }else if (needRestoreCanvas.getAndSet(false)){ - // Put canvas back online - logger.log(Level.INFO, "OGL: Canvas is now visible! Re-initializing.."); - restoreCanvas(); - listener.gainFocus(); } - if (width != canvas.getWidth() || height != canvas.getHeight()){ - width = canvas.getWidth(); - height = canvas.getHeight(); - if (listener != null) - listener.reshape(width, height); + if (renderable.get()){ + int newWidth = Math.max(canvas.getWidth(), 1); + int newHeight = Math.max(canvas.getHeight(), 1); + if (width != newWidth || height != newHeight){ + width = newWidth; + height = newHeight; + if (listener != null){ + listener.reshape(width, height); + } + } + }else{ + if (frameRate <= 0){ + // NOTE: MUST be done otherwise + // Windows OS will freeze + Display.sync(30); + } } super.runLoop(); @@ -218,8 +242,6 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex Keyboard.destroy(); } - logger.log(Level.INFO, "OGL: Canvas will become invisible! Destroying .."); - renderable.set(false); destroyContext(); } @@ -237,7 +259,7 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex } } - logger.log(Level.INFO, "OGL: Creating display.."); + logger.log(Level.INFO, "OGL: Creating display context .."); // Set renderable to true, since canvas is now displayable. renderable.set(true); @@ -306,7 +328,27 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex if (pbuffer == null) { pbuffer = new Pbuffer(1, 1, acquirePixelFormat(true), null); + pbuffer.makeCurrent(); logger.log(Level.INFO, "OGL: Pbuffer has been created"); + + // Any created objects are no longer valid + if (!runningFirstTime){ + renderer.resetGLObjects(); + } + } + + pbuffer.makeCurrent(); + if (!pbuffer.isCurrent()){ + throw new LWJGLException("Pbuffer cannot be made current"); + } + } + + protected void destroyPbuffer(){ + if (pbuffer != null){ + if (!pbuffer.isBufferLost()){ + pbuffer.destroy(); + } + pbuffer = null; } } @@ -317,25 +359,9 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex */ protected void destroyContext(){ try { - // The canvas is no longer visible, - // but the context thread is still running. - if (!needClose.get()){ - // MUST make sure there's still a context current here .. - // Display is dead, make pbuffer available to the system - makePbufferAvailable(); - - // pbuffer is now available, make it current - pbuffer.makeCurrent(); - - // invalidate the state so renderer can resume operation - renderer.invalidateState(); - }else{ - // The context thread is no longer running. - // Destroy pbuffer. - if (pbuffer != null && !pbuffer.isBufferLost()){ - pbuffer.destroy(); - pbuffer = null; - } + // invalidate the state so renderer can resume operation + if (!USE_SHARED_CONTEXT){ + renderer.cleanup(); } if (Display.isCreated()){ @@ -353,21 +379,35 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex Keyboard.destroy(); } - try { + //try { // NOTE: On Windows XP, not calling setParent(null) // freezes the application. // On Mac it freezes the application. // On Linux it fixes a crash with X Window System. if (JmeSystem.getPlatform() == Platform.Windows32 || JmeSystem.getPlatform() == Platform.Windows64){ - Display.setParent(null); + //Display.setParent(null); } - } catch (LWJGLException ex) { - logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex); - } + //} catch (LWJGLException ex) { + // logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex); + //} Display.destroy(); } + + // The canvas is no longer visible, + // but the context thread is still running. + if (!needClose.get()){ + // MUST make sure there's still a context current here .. + // Display is dead, make pbuffer available to the system + makePbufferAvailable(); + + renderer.invalidateState(); + }else{ + // The context thread is no longer running. + // Destroy pbuffer. + destroyPbuffer(); + } } catch (LWJGLException ex) { listener.handleError("Failed make pbuffer available", ex); } @@ -385,28 +425,44 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex frameRate = settings.getFrameRate(); try { - // First create the pbuffer, if it is needed. - makePbufferAvailable(); - if (renderable.get()){ + if (!runningFirstTime){ + // because the display is a different opengl context + // must reset the context state. + if (!USE_SHARED_CONTEXT){ + renderer.cleanup(); + } + } + // if the pbuffer is currently active, // make sure to deactivate it - if (pbuffer.isCurrent()){ - pbuffer.releaseContext(); + destroyPbuffer(); + + if (Keyboard.isCreated()){ + Keyboard.destroy(); } - + + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + } + Display.setVSyncEnabled(settings.isVSync()); Display.setParent(canvas); - Display.create(acquirePixelFormat(false), pbuffer); - // because the display is a different opengl context - // must reset the context state. + if (USE_SHARED_CONTEXT){ + Display.create(acquirePixelFormat(false), pbuffer); + }else{ + Display.create(acquirePixelFormat(false)); + } + renderer.invalidateState(); }else{ - pbuffer.makeCurrent(); + // First create the pbuffer, if it is needed. + makePbufferAvailable(); } - // At this point, the OpenGL context is active. + // At this point, the OpenGL context is active. if (runningFirstTime){ // THIS is the part that creates the renderer. // It must always be called, now that we have the pbuffer workaround. diff --git a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java index cdfd92d17..a4d9576f7 100644 --- a/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java +++ b/engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java @@ -41,6 +41,8 @@ import com.jme3.asset.AssetNotFoundException; import com.jme3.light.DirectionalLight; import com.jme3.light.Light; import com.jme3.light.PointLight; +import com.jme3.light.SpotLight; +import com.jme3.math.FastMath; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.scene.Node; @@ -104,16 +106,22 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { light = null; } + private void checkTopNode(String topNode) throws SAXException{ + if (!elementStack.peek().equals(topNode)){ + throw new SAXException("dotScene parse error: Expected parent node to be " + topNode); + } + } + private Quaternion parseQuat(Attributes attribs) throws SAXException{ if (attribs.getValue("x") != null){ // defined as quaternion - // qx, qy, qz, qw defined float x = parseFloat(attribs.getValue("x")); float y = parseFloat(attribs.getValue("y")); float z = parseFloat(attribs.getValue("z")); float w = parseFloat(attribs.getValue("w")); return new Quaternion(x,y,z,w); }else if (attribs.getValue("qx") != null){ + // defined as quaternion with prefix "q" float x = parseFloat(attribs.getValue("qx")); float y = parseFloat(attribs.getValue("qy")); float z = parseFloat(attribs.getValue("qz")); @@ -129,6 +137,7 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { q.fromAngleAxis(angle, new Vector3f(axisX, axisY, axisZ)); return q; }else{ + // defines as 3 angles along XYZ axes float angleX = parseFloat(attribs.getValue("angleX")); float angleY = parseFloat(attribs.getValue("angleY")); float angleZ = parseFloat(attribs.getValue("angleZ")); @@ -139,19 +148,22 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { } private void parseLightNormal(Attributes attribs) throws SAXException { - assert elementStack.peek().equals("light"); + checkTopNode("light"); // SpotLight will be supporting a direction-normal, too. if (light instanceof DirectionalLight) ((DirectionalLight) light).setDirection(parseVector3(attribs)); + else if (light instanceof SpotLight){ + ((SpotLight) light).setDirection(parseVector3(attribs)); + } } private void parseLightAttenuation(Attributes attribs) throws SAXException { - // NOTE: Only radius is supported atm ( for pointlights only, since there are no spotlights, yet). - assert elementStack.peek().equals("light"); + // NOTE: Derives range based on "linear" if it is used solely + // for the attenuation. Otherwise derives it from "range" + checkTopNode("light"); - // SpotLight will be supporting a direction-normal, too. - if (light instanceof PointLight){ + if (light instanceof PointLight || light instanceof SpotLight){ float range = parseFloat(attribs.getValue("range")); float constant = parseFloat(attribs.getValue("constant")); float linear = parseFloat(attribs.getValue("linear")); @@ -165,15 +177,36 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { if (constant == 1 && quadratic == 0 && linear > 0){ range = 1f / linear; } - ((PointLight) light).setRadius(range); + + if (light instanceof PointLight){ + ((PointLight) light).setRadius(range); + }else{ + ((SpotLight)light).setSpotRange(range); + } } - } + private void parseLightSpotLightRange(Attributes attribs) throws SAXException{ + checkTopNode("light"); + + float outer = SAXUtil.parseFloat(attribs.getValue("outer")); + float inner = SAXUtil.parseFloat(attribs.getValue("inner")); + + if (!(light instanceof SpotLight)){ + throw new SAXException("dotScene parse error: spotLightRange " + + "can only appear under 'spot' light elements"); + } + + SpotLight sl = (SpotLight) light; + sl.setSpotInnerAngle(inner * 0.5f); + sl.setSpotOuterAngle(outer * 0.5f); + } + private void parseLight(Attributes attribs) throws SAXException { - assert node != null; - assert node.getParent() != null; - assert elementStack.peek().equals("node"); + if (node == null || node.getParent() == null) + throw new SAXException("dotScene parse error: light can only appear under a node"); + + checkTopNode("node"); String lightType = parseString(attribs.getValue("type"), "point"); if(lightType.equals("point")) { @@ -182,10 +215,8 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { light = new DirectionalLight(); // Assuming "normal" property is not provided ((DirectionalLight)light).setDirection(Vector3f.UNIT_Z); - } else if(lightType.equals("spotLight")) { - // TODO: SpotLight class. - logger.warning("No SpotLight class atm, using Pointlight instead."); - light = new PointLight(); + } else if(lightType.equals("spotLight") || lightType.equals("spot")) { + light = new SpotLight(); } else { logger.log(Level.WARNING, "No matching jME3 LightType found for OGRE LightType: {0}", lightType); } @@ -203,14 +234,19 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { @Override public void startElement(String uri, String localName, String qName, Attributes attribs) throws SAXException{ if (qName.equals("scene")){ - assert elementStack.size() == 0; + if (elementStack.size() != 0){ + throw new SAXException("dotScene parse error: 'scene' element must be the root XML element"); + } + String version = attribs.getValue("formatVersion"); - if (version == null || !version.equals("1.0.0")) + if (version == null && !version.equals("1.0.0") && !version.equals("1.0.1")) logger.log(Level.WARNING, "Unrecognized version number" + " in dotScene file: {0}", version); }else if (qName.equals("nodes")){ - assert root == null; + if (root != null){ + throw new SAXException("dotScene parse error: nodes element was specified twice"); + } if (sceneName == null) root = new Node("OgreDotScene"+(++sceneIdx)); else @@ -218,22 +254,31 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { node = root; }else if (qName.equals("externals")){ - assert elementStack.peek().equals("scene"); - + checkTopNode("scene"); + // Not loaded currently }else if (qName.equals("item")){ - assert elementStack.peek().equals("externals"); + checkTopNode("externals"); }else if (qName.equals("file")){ - assert elementStack.peek().equals("item"); - String matFile = folderName+attribs.getValue("name"); - try { - materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile)); - } catch (AssetNotFoundException ex){ - materialList = null; - logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile); - } + checkTopNode("item"); + + // XXX: Currently material file name is based + // on the scene's filename. THIS IS NOT CORRECT. + // To solve, port SceneLoader to use DOM instead of SAX + + //String matFile = folderName+attribs.getValue("name"); + //try { + // materialList = (MaterialList) assetManager.loadAsset(new OgreMaterialKey(matFile)); + //} catch (AssetNotFoundException ex){ + // materialList = null; + // logger.log(Level.WARNING, "Cannot locate material file: {0}", matFile); + //} }else if (qName.equals("node")){ String curElement = elementStack.peek(); - assert curElement.equals("nodes") || curElement.equals("node"); + if (!curElement.equals("node") && !curElement.equals("nodes")){ + throw new SAXException("dotScene parse error: " + + "node element can only appear under 'node' or 'nodes'"); + } + String name = attribs.getValue("name"); if (name == null) name = "OgreNode-" + (++nodeIdx); @@ -259,7 +304,8 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { } } }else if (qName.equals("entity")){ - assert elementStack.peek().equals("node"); + checkTopNode("node"); + String name = attribs.getValue("name"); if (name == null) name = "OgreEntity-" + (++nodeIdx); @@ -267,32 +313,31 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { name += "-entity"; String meshFile = attribs.getValue("meshFile"); - if (meshFile == null) + if (meshFile == null) { throw new SAXException("Required attribute 'meshFile' missing for 'entity' node"); + } + // TODO: Not currently used String materialName = attribs.getValue("materialName"); - // NOTE: append "xml" since its assumed mesh filse are binary in dotScene - if (folderName != null) + if (folderName != null) { meshFile = folderName + meshFile; + } + // NOTE: append "xml" since its assumed mesh files are binary in dotScene meshFile += ".xml"; entityNode = new Node(name); OgreMeshKey key = new OgreMeshKey(meshFile, materialList); - Spatial ogreMesh = - (Spatial) assetManager.loadAsset(key); - //TODO:workaround for meshxml / mesh.xml - if(ogreMesh==null){ - meshFile = folderName + attribs.getValue("meshFile") + "xml"; - key = new OgreMeshKey(meshFile, materialList); - ogreMesh = (Spatial) assetManager.loadAsset(key); - } + Spatial ogreMesh = assetManager.loadModel(key); + entityNode.attachChild(ogreMesh); node.attachChild(entityNode); node = null; }else if (qName.equals("position")){ - node.setLocalTranslation(SAXUtil.parseVector3(attribs)); + if (elementStack.peek().equals("node")){ + node.setLocalTranslation(SAXUtil.parseVector3(attribs)); + } }else if (qName.equals("quaternion") || qName.equals("rotation")){ node.setLocalRotation(parseQuat(attribs)); }else if (qName.equals("scale")){ @@ -305,19 +350,22 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { light.setColor(parseColor(attribs)); } }else{ - assert elementStack.peek().equals("environment"); + checkTopNode("environment"); } - } else if (qName.equals("normal")) { + } else if (qName.equals("normal") || qName.equals("direction")) { + checkTopNode("light"); parseLightNormal(attribs); } else if (qName.equals("lightAttenuation")) { parseLightAttenuation(attribs); + } else if (qName.equals("spotLightRange") || qName.equals("lightRange")) { + parseLightSpotLightRange(attribs); } elementStack.push(qName); } @Override - public void endElement(String uri, String name, String qName) { + public void endElement(String uri, String name, String qName) throws SAXException { if (qName.equals("node")){ node = node.getParent(); }else if (qName.equals("nodes")){ @@ -339,11 +387,21 @@ public class SceneLoader extends DefaultHandler implements AssetLoader { PointLight pl = (PointLight) light; Vector3f pos = node.getWorldTranslation(); pl.setPosition(pos); + }else if (light instanceof SpotLight){ + SpotLight sl = (SpotLight) light; + + Vector3f pos = node.getWorldTranslation(); + sl.setPosition(pos); + + Quaternion q = node.getWorldRotation(); + Vector3f dir = sl.getDirection(); + q.multLocal(dir); + sl.setDirection(dir); } } light = null; } - assert elementStack.peek().equals(qName); + checkTopNode(qName); elementStack.pop(); } diff --git a/engine/src/test/jme3test/app/TestBareBonesApp.java b/engine/src/test/jme3test/app/TestBareBonesApp.java index e358af354..ee4638876 100644 --- a/engine/src/test/jme3test/app/TestBareBonesApp.java +++ b/engine/src/test/jme3test/app/TestBareBonesApp.java @@ -78,7 +78,7 @@ public class TestBareBonesApp extends Application { boxGeom.updateGeometricState(); // render the viewports - renderManager.render(tpf, true); + renderManager.render(tpf, context.isRenderable()); } @Override diff --git a/engine/src/test/jme3test/app/state/TestAppStates.java b/engine/src/test/jme3test/app/state/TestAppStates.java index d31ec87e9..721bc0499 100644 --- a/engine/src/test/jme3test/app/state/TestAppStates.java +++ b/engine/src/test/jme3test/app/state/TestAppStates.java @@ -88,7 +88,7 @@ public class TestAppStates extends Application { stateManager.render(renderManager); // render the viewports - renderManager.render(tpf, true); + renderManager.render(tpf, context.isRenderable()); } @Override diff --git a/engine/src/test/jme3test/awt/TestCanvas.java b/engine/src/test/jme3test/awt/TestCanvas.java index 811752068..31c2809fd 100644 --- a/engine/src/test/jme3test/awt/TestCanvas.java +++ b/engine/src/test/jme3test/awt/TestCanvas.java @@ -37,7 +37,10 @@ import com.jme3.app.SimpleApplication; import com.jme3.system.AppSettings; import com.jme3.system.JmeCanvasContext; import com.jme3.util.JmeFormatter; +import java.awt.BorderLayout; import java.awt.Canvas; +import java.awt.Container; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; @@ -50,8 +53,11 @@ import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; +import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; +import javax.swing.UIManager; public class TestCanvas { @@ -59,87 +65,149 @@ public class TestCanvas { private static Canvas canvas; private static Application app; private static JFrame frame; - private static final String appClass = "jme3test.post.TestMultiplesFilters"; - - private static void createFrame(){ - frame = new JFrame("Test"); - frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - frame.addWindowListener(new WindowAdapter(){ - @Override - public void windowClosed(WindowEvent e) { - app.stop(); - } - }); + private static Container canvasPanel1, canvasPanel2; + private static Container currentPanel; + private static JTabbedPane tabbedPane; + private static final String appClass = "jme3test.post.TestRenderToTexture"; + private static void createTabs(){ + tabbedPane = new JTabbedPane(); + + canvasPanel1 = new JPanel(); + canvasPanel1.setLayout(new BorderLayout()); + tabbedPane.addTab("jME3 Canvas 1", canvasPanel1); + + canvasPanel2 = new JPanel(); + canvasPanel2.setLayout(new BorderLayout()); + tabbedPane.addTab("jME3 Canvas 2", canvasPanel2); + + frame.getContentPane().add(tabbedPane); + + currentPanel = canvasPanel1; + } + + private static void createMenu(){ JMenuBar menuBar = new JMenuBar(); frame.setJMenuBar(menuBar); - JMenu menuFile = new JMenu("File"); - menuBar.add(menuFile); + JMenu menuTortureMethods = new JMenu("Canvas Torture Methods"); + menuBar.add(menuTortureMethods); final JMenuItem itemRemoveCanvas = new JMenuItem("Remove Canvas"); - menuFile.add(itemRemoveCanvas); + menuTortureMethods.add(itemRemoveCanvas); itemRemoveCanvas.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (itemRemoveCanvas.getText().equals("Remove Canvas")){ - frame.getContentPane().remove(canvas); - - // force OS to repaint over canvas .. - // this is needed since AWT does not handle - // that when a heavy-weight component is removed. - frame.setVisible(false); - frame.setVisible(true); - frame.requestFocus(); + currentPanel.remove(canvas); itemRemoveCanvas.setText("Add Canvas"); }else if (itemRemoveCanvas.getText().equals("Add Canvas")){ - frame.getContentPane().add(canvas); + currentPanel.add(canvas, BorderLayout.CENTER); + itemRemoveCanvas.setText("Remove Canvas"); } } }); - + + final JMenuItem itemHideCanvas = new JMenuItem("Hide Canvas"); + menuTortureMethods.add(itemHideCanvas); + itemHideCanvas.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (itemHideCanvas.getText().equals("Hide Canvas")){ + canvas.setVisible(false); + itemHideCanvas.setText("Show Canvas"); + }else if (itemHideCanvas.getText().equals("Show Canvas")){ + canvas.setVisible(true); + itemHideCanvas.setText("Hide Canvas"); + } + } + }); + + final JMenuItem itemSwitchTab = new JMenuItem("Switch to tab #2"); + menuTortureMethods.add(itemSwitchTab); + itemSwitchTab.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e){ + if (itemSwitchTab.getText().equals("Switch to tab #2")){ + canvasPanel1.remove(canvas); + canvasPanel2.add(canvas, BorderLayout.CENTER); + currentPanel = canvasPanel2; + itemSwitchTab.setText("Switch to tab #1"); + }else if (itemSwitchTab.getText().equals("Switch to tab #1")){ + canvasPanel2.remove(canvas); + canvasPanel1.add(canvas, BorderLayout.CENTER); + currentPanel = canvasPanel1; + itemSwitchTab.setText("Switch to tab #2"); + } + } + }); + + JMenuItem itemSwitchLaf = new JMenuItem("Switch Look and Feel"); + menuTortureMethods.add(itemSwitchLaf); + itemSwitchLaf.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e){ + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Throwable t){ + t.printStackTrace(); + } + SwingUtilities.updateComponentTreeUI(frame); + frame.pack(); + } + }); + + JMenuItem itemSmallSize = new JMenuItem("Set size to (0, 0)"); + menuTortureMethods.add(itemSmallSize); + itemSmallSize.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e){ + Dimension preferred = frame.getPreferredSize(); + frame.setPreferredSize(new Dimension(0, 0)); + frame.pack(); + frame.setPreferredSize(preferred); + } + }); + JMenuItem itemKillCanvas = new JMenuItem("Stop/Start Canvas"); - menuFile.add(itemKillCanvas); + menuTortureMethods.add(itemKillCanvas); itemKillCanvas.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - frame.getContentPane().remove(canvas); + currentPanel.remove(canvas); app.stop(true); createCanvas(appClass); - frame.getContentPane().add(canvas); + currentPanel.add(canvas, BorderLayout.CENTER); frame.pack(); startApp(); } }); JMenuItem itemExit = new JMenuItem("Exit"); - menuFile.add(itemExit); + menuTortureMethods.add(itemExit); itemExit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { frame.dispose(); app.stop(); } }); + } + + private static void createFrame(){ + frame = new JFrame("Test"); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter(){ + @Override + public void windowClosed(WindowEvent e) { + app.stop(); + } + }); - JMenu menuEdit = new JMenu("Edit"); - menuBar.add(menuEdit); - JMenuItem itemDelete = new JMenuItem("Delete"); - menuEdit.add(itemDelete); - - JMenu menuView = new JMenu("View"); - menuBar.add(menuView); - JMenuItem itemSetting = new JMenuItem("Settings"); - menuView.add(itemSetting); - - JMenu menuHelp = new JMenu("Help"); - menuBar.add(menuHelp); + createTabs(); + createMenu(); } public static void createCanvas(String appClass){ AppSettings settings = new AppSettings(true); - settings.setWidth( Math.max(640, frame.getContentPane().getWidth()) ); - settings.setHeight( Math.max(480, frame.getContentPane().getHeight()) ); + settings.setWidth(640); + settings.setHeight(480); try{ Class clazz = (Class) Class.forName(appClass); @@ -155,6 +223,7 @@ public class TestCanvas { app.setPauseOnLostFocus(false); app.setSettings(settings); app.createCanvas(); + app.startCanvas(); context = (JmeCanvasContext) app.getContext(); canvas = context.getCanvas(); @@ -184,14 +253,20 @@ public class TestCanvas { Logger.getLogger("").removeHandler(Logger.getLogger("").getHandlers()[0]); Logger.getLogger("").addHandler(consoleHandler); + createCanvas(appClass); + + try { + Thread.sleep(500); + } catch (InterruptedException ex) { + } + SwingUtilities.invokeLater(new Runnable(){ public void run(){ JPopupMenu.setDefaultLightWeightPopupEnabled(false); createFrame(); - createCanvas(appClass); - frame.getContentPane().add(canvas); + currentPanel.add(canvas, BorderLayout.CENTER); frame.pack(); startApp(); frame.setLocationRelativeTo(null); diff --git a/engine/src/test/jme3test/bullet/TestWalkingChar.java b/engine/src/test/jme3test/bullet/TestWalkingChar.java index 3d1dbb4b4..c5d394da6 100644 --- a/engine/src/test/jme3test/bullet/TestWalkingChar.java +++ b/engine/src/test/jme3test/bullet/TestWalkingChar.java @@ -225,7 +225,7 @@ public class TestWalkingChar extends SimpleApplication implements ActionListener Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md"); mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png")); effect.setMaterial(mat); - effect.setLocalScale(100); +// effect.setLocalScale(100); rootNode.attachChild(effect); }