* Ogre3D mesh.xml loader is now more resilient to certain models exported using blender2ogre

* Ogre3D dotScene loader can now load spot lights
 * Added some better debugging to FBO errors
 * Fix weird explosion in TestWalkingChar
 * Added additional "canvas torture methods" in TestCanvas 
 * Several fixes to canvas:
   - Issue when size becomes 0, 0
   - Freeze if no framerate limit is imposed and canvas is closed

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8210 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent 120a3d1e4f
commit 6d728615a3
  1. 9
      engine/src/core/com/jme3/util/xml/SAXUtil.java
  2. 125
      engine/src/lwjgl-ogl/com/jme3/renderer/lwjgl/LwjglRenderer.java
  3. 210
      engine/src/lwjgl-ogl/com/jme3/system/lwjgl/LwjglCanvas.java
  4. 146
      engine/src/ogre/com/jme3/scene/plugins/ogre/SceneLoader.java
  5. 2
      engine/src/test/jme3test/app/TestBareBonesApp.java
  6. 2
      engine/src/test/jme3test/app/state/TestAppStates.java
  7. 155
      engine/src/test/jme3test/awt/TestCanvas.java
  8. 2
      engine/src/test/jme3test/bullet/TestWalkingChar.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){

@ -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");
}
@ -1278,6 +1282,85 @@ public class LwjglRenderer implements Renderer {
}
}
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,15 +1632,16 @@ 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);
logger.log(Level.SEVERE, "=== jMonkeyEngine FBO State ===\n{0}", fb);
printRealFrameBufferInfo(fb);
throw ex;
}
}
}
public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) {
if (fb != null) {
@ -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);
}
}
@ -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<VertexBuffer> 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;
}

@ -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);
logger.log(Level.INFO, "EDT: Telling OGL to destroy display ..");
synchronized (taskLock){
desiredTask = TASK_DESTROY_DISPLAY;
while (desiredTask != TASK_COMPLETE){
try {
actionRequiredBarrier.await();
} catch (InterruptedException ex) {
logger.log(Level.SEVERE, "EDT: Interrupted! ", ex);
} catch (BrokenBarrierException ex){
logger.log(Level.SEVERE, "EDT: Broken barrier! ", ex);
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
@ -171,35 +187,43 @@ public class LwjglCanvas extends LwjglAbstractDisplay implements JmeCanvasContex
@Override
protected void runLoop(){
if (needDestroyCanvas.getAndSet(false)){
// Destroy canvas
logger.log(Level.INFO, "OGL: Received destroy request! Complying..");
try {
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();
} 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);
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)
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;
}
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);
}
} catch (LWJGLException ex) {
logger.log(Level.SEVERE, "Encountered exception when setting parent to null", ex);
//Display.setParent(null);
}
//} 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);
if (USE_SHARED_CONTEXT){
Display.create(acquirePixelFormat(false), pbuffer);
}else{
Display.create(acquirePixelFormat(false));
}
// because the display is a different opengl context
// must reset the context state.
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.

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

@ -78,7 +78,7 @@ public class TestBareBonesApp extends Application {
boxGeom.updateGeometricState();
// render the viewports
renderManager.render(tpf, true);
renderManager.render(tpf, context.isRenderable());
}
@Override

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

@ -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 Container canvasPanel1, canvasPanel2;
private static Container currentPanel;
private static JTabbedPane tabbedPane;
private static final String appClass = "jme3test.post.TestRenderToTexture";
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 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();
}
});
}
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);
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 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<? extends Application> clazz = (Class<? extends Application>) 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);

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

Loading…
Cancel
Save