/**
* Copyright (c) 2008-2009 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/
package automenta.spacenet.run;
import automenta.spacenet.space.video.PropertiesDialog;
import automenta.spacenet.space.video.PropertiesGameSettings;
import automenta.spacenet.space.video.JoglModule;
import automenta.spacenet.space.video.Exit;
import automenta.spacenet.space.*;
import automenta.spacenet.space.control.camera.ArdorCamera;
import automenta.spacenet.space.control.pointer.DefaultKeyboard;
import automenta.spacenet.space.control.pointer.DefaultPointer;
import automenta.spacenet.var.physical.Color;
import automenta.spacenet.var.scalar.DoubleVar;
import automenta.spacenet.var.vector.V3;
import com.ardor3d.intersection.PickResults;
import java.awt.EventQueue;
import java.awt.event.ComponentEvent;
import java.net.URL;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.ardor3d.annotation.MainThread;
import com.ardor3d.framework.ArdorModule;
import com.ardor3d.framework.Canvas;
import com.ardor3d.framework.DisplaySettings;
import com.ardor3d.framework.FrameHandler;
import com.ardor3d.framework.NativeCanvas;
import com.ardor3d.framework.Scene;
import com.ardor3d.framework.Updater;
import com.ardor3d.framework.jogl.JoglCanvas;
import com.ardor3d.image.TextureStoreFormat;
import com.ardor3d.image.util.AWTImageLoader;
import com.ardor3d.image.util.ScreenShotImageExporter;
import com.ardor3d.input.ControllerWrapper;
import com.ardor3d.input.FocusWrapper;
import com.ardor3d.input.Key;
import com.ardor3d.input.KeyboardWrapper;
import com.ardor3d.input.MouseManager;
import com.ardor3d.input.MouseWrapper;
import com.ardor3d.input.PhysicalLayer;
import com.ardor3d.input.control.FirstPersonControl;
import com.ardor3d.input.logical.InputTrigger;
import com.ardor3d.input.logical.KeyPressedCondition;
import com.ardor3d.input.logical.LogicalLayer;
import com.ardor3d.input.logical.TriggerAction;
import com.ardor3d.input.logical.TwoInputStates;
import com.ardor3d.light.PointLight;
import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.Ray3;
import com.ardor3d.math.Vector3;
import com.ardor3d.renderer.Renderer;
import com.ardor3d.renderer.TextureRendererFactory;
import com.ardor3d.renderer.jogl.JoglTextureRendererProvider;
import com.ardor3d.renderer.queue.RenderBucketType;
import com.ardor3d.renderer.state.LightState;
import com.ardor3d.renderer.state.WireframeState;
import com.ardor3d.renderer.state.ZBufferState;
import com.ardor3d.scenegraph.Spatial;
import com.ardor3d.scenegraph.event.DirtyType;
import com.ardor3d.util.Constants;
import com.ardor3d.util.ContextGarbageCollector;
import com.ardor3d.util.GameTaskQueue;
import com.ardor3d.util.GameTaskQueueManager;
import com.ardor3d.util.ReadOnlyTimer;
import com.ardor3d.util.geom.Debugger;
import com.ardor3d.util.screen.ScreenExporter;
import com.ardor3d.util.stat.StatCollector;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.Stage;
import java.awt.event.ComponentAdapter;
import javax.media.opengl.GLException;
import javolution.context.ConcurrentContext;
@Deprecated abstract public class ArdorSpacetime extends Space implements Runnable, Updater, Scene, Exit, Spacetime {
private static final Logger logger = Logger.getLogger(ArdorSpacetime.class.getName());
protected Color backgroundColor = new Color(Color.GRAY);
protected final LogicalLayer logicalLayer;
protected PhysicalLayer _physicalLayer;
protected final Space volume;
protected final FrameHandler _frameHandler;
protected LightState defaultLightState;
protected WireframeState _wireframeState;
protected volatile boolean _exit = false;
protected static boolean _stereo = false;
protected boolean _showBounds = false;
protected boolean _showNormals = false;
protected boolean _showDepth = false;
protected boolean _doShot = false;
protected NativeCanvas canvas;
protected ScreenShotImageExporter _screenShotExp = new ScreenShotImageExporter();
protected MouseManager _mouseManager;
protected FirstPersonControl _controlHandle;
protected Vector3 _worldUp = new Vector3(0, 1, 0);
protected static int _minDepthBits = -1;
protected static int _minAlphaBits = -1;
protected static int _minStencilBits = -1;
private DefaultPointer pointer;
private ArdorCamera camera;
private DoubleVar frameDelay = new DoubleVar(0.01);
final private Space face, sky;
private PointLight defaultLight;
@Inject
public ArdorSpacetime(final LogicalLayer logicalLayer, final FrameHandler frameHandler) {
super();
this.logicalLayer = logicalLayer;
_frameHandler = frameHandler;
volume = this;
//do not add face and sky nodes to root (volume) - they will be rendered in separate passes
face = new Space();
sky = new Space();
}
public MouseManager getMouseManager() {
return _mouseManager;
}
public static synchronized void delay(double s) {
try {
Thread.sleep((long) (s * 1000.0));
} catch (InterruptedException ex) {
}
}
public void run() {
try {
_frameHandler.init();
long lastFrame = System.nanoTime(), nowFrame;
while (!_exit) {
nowFrame = System.nanoTime();
double framePeriod = (nowFrame - lastFrame) * 1e-9;
//System.out.println("fps=" + 1.0 / framePeriod);
_frameHandler.updateFrame();
lastFrame = nowFrame;
delay(getFrameDelay().d());
Thread.yield();
}
// grab the graphics context so cleanup will work out.
canvas.getCanvasRenderer().setCurrentContext();
quit(canvas.getCanvasRenderer().getRenderer());
} catch (final Throwable t) {
System.err.println("Throwable caught in MainThread - exiting");
t.printStackTrace(System.err);
}
}
@Deprecated public void exit() {
_exit = true;
}
public void stop() {
exit();
}
@Override public LogicalLayer getInputLogic() {
return logicalLayer;
}
@Override public NativeCanvas getVideo() {
return canvas;
}
private void updateBackgroundColor() {
if (getVideo() != null) {
if (getVideo().getCanvasRenderer() != null) {
if (getVideo().getCanvasRenderer().getRenderer() != null) {
try {
getVideo().getCanvasRenderer().getRenderer().setBackgroundColor(backgroundColor);
} catch (GLException e) {
e.printStackTrace();
}
}
}
}
}
@MainThread
public void init() {
int numProcs = Runtime.getRuntime().availableProcessors();
System.out.println("Num processors: " + numProcs);
ConcurrentContext.setConcurrency(numProcs);
initInput();
AWTImageLoader.registerLoader();
// try {
// SimpleResourceLocator srl = new SimpleResourceLocator(ArdorSpaceTime.class.getClassLoader().getResource(
// "com/ardor3d/example/media/"));
// ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE, srl);
// srl = new SimpleResourceLocator(ArdorSpaceTime.class.getClassLoader().getResource(
// "com/ardor3d/example/media/models/"));
// ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_MODEL, srl);
// } catch (final URISyntaxException ex) {
// ex.printStackTrace();
// }
// getBackgroundColor().add(new IfColorChanges() {
// @Override public void onColorChanged(Color c) {
// //updateBackgroundColor();
// }
// });
updateBackgroundColor();
/**
* Create a ZBuffer to display pixels closest to the camera above farther ones.
*/
final ZBufferState buf = new ZBufferState();
buf.setEnabled(true);
buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
volume.setRenderState(buf);
// ---- LIGHTS
/** Set up a basic, default light. */
defaultLight = new PointLight();
defaultLight.setDiffuse(new ColorRGBA(0.75f, 0.75f, 0.75f, 0.75f));
defaultLight.setAmbient(new ColorRGBA(0.7f, 0.7f, 0.7f, 0.75f));
defaultLight.setLocation(new Vector3(0, 0, 50));
defaultLight.setAttenuate(true);
defaultLight.setLinear(0.003f);
defaultLight.setQuadratic(0.001f);
defaultLight.setEnabled(true);
/** Attach the light to a lightState and the lightState to rootNode. */
defaultLightState = new LightState();
defaultLightState.setEnabled(true);
defaultLightState.attach(defaultLight);
volume.setRenderState(defaultLightState);
_wireframeState = new WireframeState();
_wireframeState.setEnabled(false);
volume.setRenderState(_wireframeState);
volume.getSceneHints().setRenderBucketType(RenderBucketType.Opaque);
if (canvas instanceof JoglCanvas) {
((JoglCanvas) canvas).addComponentListener(new ComponentAdapter() {
@Override public void componentResized(ComponentEvent e) {
java.awt.Dimension s = e.getComponent().getSize();
canvas.getCanvasRenderer().getCamera().resize(s.width, s.height);
}
});
}
this.camera = new ArdorCamera(this, new V3(0, 0, 4), new V3(0, 0, 0), new V3(0, 1, 0));
add(camera);
this.pointer = new DefaultPointer(this);
add(pointer); //must be added w/ add(Repeat r)
add(new DefaultKeyboard(this, pointer));
initWindow();
}
abstract protected void initWindow();
@MainThread
public void update(final ReadOnlyTimer timer) {
if (getVideo().isClosing()) {
//needs to be first in this method
exit();
}
/** update stats, if enabled. */
if (Constants.stats) {
StatCollector.update();
}
updateLogicalLayer(timer);
// Execute updateQueue item
GameTaskQueueManager.getManager(getVideo().getCanvasRenderer().getRenderContext()).getQueue(GameTaskQueue.UPDATE).execute();
/** Call simpleUpdate in any derived classes of ExampleBase. */
updateWindow(timer);
updateLights();
/** Update controllers/render states/transforms/bounds for rootNode. */
ConcurrentContext.enter();
try {
getSky().updateGeometricState(timer.getTimePerFrame(), true);
getRoot().updateGeometricState(timer.getTimePerFrame(), true);
getFace().updateGeometricState(timer.getTimePerFrame(), true);
} finally {
ConcurrentContext.exit();
}
}
protected void updateLights() {
defaultLight.setLocation(getCamera().getCurrentPosition());
}
// public static void updateLater(final Runnable r) {
//
// GameTaskQueueManager.getManager(_canvas.getCanvasRenderer().getRenderContext()).getQueue(GameTaskQueue.UPDATE).enqueue(new Callable() {
// @Override public Object call() throws Exception {
// r.run();
// }
// });
// }
protected void updateLogicalLayer(final ReadOnlyTimer timer) {
// check and execute any input triggers, if we are concerned with input
if (logicalLayer != null) {
logicalLayer.checkTriggers(timer.getTimePerFrame());
}
}
protected void updateWindow(final ReadOnlyTimer timer) {
// does nothing
}
@MainThread
public boolean renderUnto(final Renderer renderer) {
// Execute renderQueue item
GameTaskQueueManager.getManager(canvas.getCanvasRenderer().getRenderContext()).getQueue(GameTaskQueue.RENDER).execute(renderer);
// Clean up card garbage such as textures, vbos, etc.
ContextGarbageCollector.doRuntimeCleanup(renderer);
/** Draw the rootNode and all its children. */
if (!canvas.isClosing()) {
renderer.draw(sky);
renderer.renderBuckets();
/** Call renderExample in any derived classes. */
renderRoot(renderer);
renderDebug(renderer);
renderer.renderBuckets();
renderer.draw(face);
if (_doShot) {
// force any waiting scene elements to be renderer.
renderer.renderBuckets();
ScreenExporter.exportCurrentScreen(canvas.getCanvasRenderer().getRenderer(), _screenShotExp);
_doShot = false;
}
return true;
} else {
return false;
}
}
protected void renderRoot(final Renderer renderer) {
renderer.draw(volume);
}
protected void renderDebug(final Renderer renderer) {
if (_showBounds) {
Debugger.drawBounds(volume, renderer, true);
}
if (_showNormals) {
Debugger.drawNormals(volume, renderer);
Debugger.drawTangents(volume, renderer);
}
if (_showDepth) {
renderer.renderBuckets();
Debugger.drawBuffer(TextureStoreFormat.Depth16, Debugger.NORTHEAST, renderer);
}
}
protected void quit(final Renderer renderer) {
ContextGarbageCollector.doFinalCleanup(renderer);
canvas.close();
}
public ArdorCamera getCamera() {
return camera;
}
private DoubleVar getFrameDelay() {
return frameDelay;
}
public static class ArdorSpaceTimeProcess extends ArdorSpacetime {
@Inject public ArdorSpaceTimeProcess(final LogicalLayer logicalLayer, final FrameHandler frameWork) {
super(logicalLayer, frameWork);
}
@Override protected void initWindow() {
}
}
// public static void newWindow(Class<? extends Spatial> spaceClass) {
// try {
// newWindow(spaceClass.newInstance());
// } catch (InstantiationException ex) {
// Logger.getLogger(ArdorSpaceTime.class.getName()).log(Level.SEVERE, null, ex);
// } catch (IllegalAccessException ex) {
// Logger.getLogger(ArdorSpaceTime.class.getName()).log(Level.SEVERE, null, ex);
// }
// }
public static void newWindow(final Spatial s) {
ArdorSpacetime a = newWindow(ArdorSpaceTimeProcess.class);
a.getRoot().add(s);
}
public static ArdorSpacetime newWindow(final Class<? extends ArdorSpacetime> exampleClazz) {
// Ask for properties
final PropertiesGameSettings prefs = getAttributes(new PropertiesGameSettings("ardorSettings.properties", null));
// Convert to DisplayProperties (XXX: maybe merge these classes?)
final DisplaySettings settings = new DisplaySettings(prefs.getWidth(), prefs.getHeight(), prefs.getDepth(),
prefs.getFrequency(),
// alpha
_minAlphaBits != -1 ? _minAlphaBits : prefs.getAlphaBits(),
// depth
_minDepthBits != -1 ? _minDepthBits : prefs.getDepthBits(),
// stencil
_minStencilBits != -1 ? _minStencilBits : prefs.getStencilBits(),
// samples
prefs.getSamples(),
// other
prefs.isFullscreen(), _stereo);
// get our framework
final ArdorModule ardorModule = new ArdorModule();
Module systemModule = null;
// if ("LWJGL".equalsIgnoreCase(prefs.getRenderer())) {
// systemModule = new LwjglModule();
// TextureRendererFactory.INSTANCE.setProvider(new LwjglTextureRendererProvider());
// } else if ("JOGL".equalsIgnoreCase(prefs.getRenderer())) {
systemModule = new JoglModule();
TextureRendererFactory.INSTANCE.setProvider(new JoglTextureRendererProvider());
// }
final Module exampleModule = new AbstractModule() {
@Override
protected void configure() {
bind(ArdorSpacetime.class).to(exampleClazz).in(Scopes.SINGLETON);
bind(Scene.class).to(ArdorSpacetime.class);
bind(Updater.class).to(ArdorSpacetime.class);
bind(Exit.class).to(ArdorSpacetime.class);
}
};
final Provider<DisplaySettings> settingsProvider = new Provider<DisplaySettings>() {
public DisplaySettings get() {
return settings;
}
};
// Setup our injector.
final Injector injector = Guice.createInjector(Stage.PRODUCTION, ardorModule, systemModule, exampleModule,
new AbstractModule() {
@Override
protected void configure() {
bind(DisplaySettings.class).toProvider(settingsProvider);
}
});
final LogicalLayer ll = injector.getInstance(LogicalLayer.class);
final FrameHandler frameWork = injector.getInstance(FrameHandler.class);
final ArdorSpacetime ardorSpacetime = injector.getInstance(ArdorSpacetime.class);
final NativeCanvas canvas = injector.getInstance(NativeCanvas.class);
final Updater updater = injector.getInstance(Updater.class);
final PhysicalLayer physicalLayer = new PhysicalLayer(injector.getInstance(KeyboardWrapper.class), injector.getInstance(MouseWrapper.class), injector.getInstance(ControllerWrapper.class), injector.getInstance(FocusWrapper.class));
// set the mouse manager member. It's a bit of a hack to do that this way.
ardorSpacetime._mouseManager = injector.getInstance(MouseManager.class);
ll.registerInput(canvas, physicalLayer);
// Register our example as an updater.
frameWork.addUpdater(updater);
// Make a native canvas and register it.
frameWork.addCanvas(canvas);
ardorSpacetime.canvas = canvas;
ardorSpacetime._physicalLayer = physicalLayer;
new Thread(ardorSpacetime).start();
return ardorSpacetime;
}
protected static PropertiesGameSettings getAttributes(final PropertiesGameSettings settings) {
// Always show the dialog in these examples.
URL dialogImage = null;
final String dflt = settings.getDefaultSettingsWidgetImage();
if (dflt != null) {
try {
dialogImage = ArdorSpacetime.class.getResource(dflt);
} catch (final Exception e) {
logger.log(Level.SEVERE, "Resource lookup of '" + dflt + "' failed. Proceeding.");
}
}
if (dialogImage == null) {
logger.fine("No dialog image loaded");
} else {
logger.fine("Using dialog image '" + dialogImage + "'");
}
final URL dialogImageRef = dialogImage;
final AtomicReference<PropertiesDialog> dialogRef = new AtomicReference<PropertiesDialog>();
final Stack<Runnable> mainThreadTasks = new Stack<Runnable>();
try {
if (EventQueue.isDispatchThread()) {
dialogRef.set(new PropertiesDialog(settings, dialogImageRef, mainThreadTasks));
} else {
EventQueue.invokeLater(new Runnable() {
public void run() {
dialogRef.set(new PropertiesDialog(settings, dialogImageRef, mainThreadTasks));
}
});
}
} catch (final Exception e) {
logger.logp(Level.SEVERE, ArdorSpacetime.class.getClass().toString(), "ExampleBase.getAttributes(settings)",
"Exception", e);
return null;
}
PropertiesDialog dialogCheck = dialogRef.get();
while (dialogCheck == null || dialogCheck.isVisible()) {
try {
// check worker queue for work
while (!mainThreadTasks.isEmpty()) {
mainThreadTasks.pop().run();
}
// go back to sleep for a while
Thread.sleep(50);
} catch (final InterruptedException e) {
logger.warning("Error waiting for dialog system, using defaults.");
}
dialogCheck = dialogRef.get();
}
if (dialogCheck.isCancelled()) {
System.exit(0);
}
return settings;
}
public ArdorSpacetime getRoot() {
return this;
}
protected void initInput() {
// check if this example worries about input at all
if (logicalLayer == null) {
return;
}
_controlHandle = FirstPersonControl.setupTriggers(logicalLayer, _worldUp, true);
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.L), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
defaultLightState.setEnabled(!defaultLightState.isEnabled());
// Either an update or a markDirty is needed here since we did not touch the affected spatial directly.
volume.markDirty(DirtyType.RenderState);
}
}));
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.F4), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
_showDepth = !_showDepth;
}
}));
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.T), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
_wireframeState.setEnabled(!_wireframeState.isEnabled());
// Either an update or a markDirty is needed here since we did not touch the affected spatial directly.
volume.markDirty(DirtyType.RenderState);
}
}));
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.B), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
_showBounds = !_showBounds;
}
}));
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.N), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
_showNormals = !_showNormals;
}
}));
//TODO move this to a separate class that opens a save file window
logicalLayer.registerTrigger(new InputTrigger(new KeyPressedCondition(Key.F1), new TriggerAction() {
public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
_doShot = true;
}
}));
// _logicalLayer.registerTrigger(new InputTrigger(new AnyKeyCondition(), new TriggerAction() {
//
// public void perform(final Canvas source, final TwoInputStates inputState, final double tpf) {
// System.out.println("Key character pressed: " + inputState.getCurrent().getKeyboardState().getKeyEvent().getKeyChar());
// }
// }));
}
@Override
public PickResults doPick(Ray3 arg0) {
return null;
}
@Override public Color getBackgroundColor() {
return backgroundColor;
}
@Override
public void addCondition(InputTrigger t) {
getInputLogic().registerTrigger(t);
}
@Override
public void removeCondition(InputTrigger t) {
getInputLogic().deregisterTrigger(t);
}
@Override
public Space getFace() {
return face;
}
@Override
public Space getSky() {
return sky;
}
@Override public PhysicalLayer getInputPhy() {
return _physicalLayer;
}
@Override public DefaultPointer getPointer() {
return pointer;
}
}