$ mvn -C clean install
<dependency> <groupId>com.io7m.jcamera</groupId> <artifactId>com.io7m.jcamera-core</artifactId> <version>0.6.0</version> </dependency>
Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
import com.io7m.jcamera.JCameraReadableSnapshotType;
import com.io7m.jtensors.core.unparameterized.vectors.Vector3D;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.GL3;
import java.io.IOException;
import java.util.Optional;
/**
* The interface exposed by the renderer to JOGL.
*/
public interface ExampleRendererType extends ExampleRendererControllerType
{
/**
* Initialize the scene, using the given window and OpenGL interface.
*
* @param in_window The window
* @param in_gl The OpenGL interface
*
* @throws IOException On I/O errors
*/
void init(
GLWindow in_window,
GL3 in_gl)
throws IOException;
/**
* Draw the scene.
*
* @param s A camera snapshot
* @param target An optional target to be drawn
*/
void draw(
JCameraReadableSnapshotType s,
Optional<Vector3D> target);
/**
* Indicate that the screen has been resized.
*
* @param width The new width
* @param height The new height
*/
void reshape(
int width,
int height);
}
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
import com.io7m.jcamera.JCameraFPSStyleInputType;
import com.io7m.jcamera.JCameraFPSStyleIntegratorType;
import com.io7m.jcamera.JCameraFPSStyleSnapshot;
import com.io7m.jcamera.JCameraFPSStyleType;
/**
* The interface to simulations (with fps-style cameras) exposed to JOGL.
*/
public interface ExampleFPSStyleSimulationType
{
/**
* @return {@code true} if the camera is enabled.
*/
boolean cameraIsEnabled();
/**
* Enable/disable the camera.
*
* @param b {@code true} if the camera should be enabled.
*/
void cameraSetEnabled(
boolean b);
/**
* @return The camera used for the simulation.
*/
JCameraFPSStyleType getCamera();
/**
* @return The simulation delta time
*/
float getDeltaTime();
/**
* @return The camera input
*/
JCameraFPSStyleInputType getInput();
/**
* @return The integrator used for the camera.
*/
JCameraFPSStyleIntegratorType getIntegrator();
/**
* @return A new camera snapshot
*/
JCameraFPSStyleSnapshot integrate();
}
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
/**
* The interface that the simulation uses to talk to the renderer.
*/
public interface ExampleRendererControllerType
{
/**
* Tell the renderer/windowing system that it should warp the pointer to the
* center of the screen.
*/
void sendWantWarpPointer();
}
/**
* $example: Construct a new simulation.
*
* @param in_renderer The interface to the renderer
*/
public ExampleFPSStyleSimulation(
final ExampleRendererControllerType in_renderer)
{
this.renderer = in_renderer;
this.input = JCameraFPSStyleInput.newInput();
this.camera = JCameraFPSStyle.newCamera();
final JCameraFPSStyleType camera_fixed = JCameraFPSStyle.newCamera();
this.fixed_snapshot = JCameraFPSStyleSnapshots.of(camera_fixed);
this.camera_enabled = new AtomicBoolean(false);
/*
* $example: Construct an integrator using the default implementations.
*/
this.integrator =
JCameraFPSStyleIntegrator.newIntegrator(this.camera, this.input);
/*
* Work out what fraction of a second the given simulation rate is going
* to require.
*/
final float rate = 60.0f;
this.integrator_time_seconds = 1.0f / rate;
/*
* $example: Configure the integrator. Use a high drag factor to give
* quite abrupt stops, and use high rotational acceleration.
*/
this.integrator.integratorAngularSetDragHorizontal(0.000000001);
this.integrator.integratorAngularSetDragVertical(0.000000001);
this.integrator.integratorAngularSetAccelerationHorizontal(
Math.PI / 12.0 / (double) this.integrator_time_seconds);
this.integrator.integratorAngularSetAccelerationVertical(
Math.PI / 12.0 / (double) this.integrator_time_seconds);
this.integrator.integratorLinearSetAcceleration(
3.0 / (double) this.integrator_time_seconds);
this.integrator.integratorLinearSetMaximumSpeed(3.0);
this.integrator.integratorLinearSetDrag(0.000000001);
}
/**
* $example: Integrate the camera.
*
* @return A new camera snapshot.
*/
@Override
public JCameraFPSStyleSnapshot integrate()
{
/*
* If the camera is actually enabled, integrate and produce a snapshot,
* and then tell the renderer/window system that it should warp the
* pointer back to the center of the screen.
*/
if (this.cameraIsEnabled()) {
this.integrator.integrate(this.integrator_time_seconds);
final JCameraFPSStyleSnapshot snap =
JCameraFPSStyleSnapshots.of(this.camera);
this.renderer.sendWantWarpPointer();
return snap;
}
return this.fixed_snapshot;
}
@Override
public boolean cameraIsEnabled()
{
return this.camera_enabled.get();
}
@Override
public void cameraSetEnabled(
final boolean b)
{
this.camera_enabled.set(b);
}
@Override
public float getDeltaTime()
{
return this.integrator_time_seconds;
}
@Override
public JCameraFPSStyleInputType getInput()
{
return this.input;
}
@Override
public JCameraFPSStyleIntegratorType getIntegrator()
{
return this.integrator;
}
@Override
public JCameraFPSStyleType getCamera()
{
return this.camera;
}
}
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
import com.io7m.jcamera.JCameraFPSStyleInputType;
import com.jogamp.newt.event.InputEvent;
import com.jogamp.newt.event.KeyEvent;
import com.jogamp.newt.event.KeyListener;
import com.jogamp.newt.opengl.GLWindow;
import java.util.concurrent.ExecutorService;
/**
* The key listener used to handle keyboard events.
*/
// CHECKSTYLE_JAVADOC:OFF
@SuppressWarnings("synthetic-access")
public final class ExampleFPSStyleKeyListener implements
KeyListener
{
private final ExampleFPSStyleSimulationType sim;
private final ExecutorService background_workers;
private final ExampleRendererType renderer;
private final JCameraFPSStyleInputType input;
private final GLWindow window;
public ExampleFPSStyleKeyListener(
final ExampleFPSStyleSimulationType in_sim,
final ExecutorService in_background_workers,
final ExampleRendererType in_renderer,
final GLWindow in_window)
{
this.sim = in_sim;
this.background_workers = in_background_workers;
this.renderer = in_renderer;
this.input = in_sim.getInput();
this.window = in_window;
}
@Override
public void keyPressed(
final KeyEvent e)
{
assert e != null;
/*
* Ignore events that are the result of keyboard auto-repeat. This means
* there's one single event when a key is pressed, and another when it is
* released (as opposed to an endless stream of both when the key is held
* down).
*/
if ((e.getModifiers() & InputEvent.AUTOREPEAT_MASK) == InputEvent.AUTOREPEAT_MASK) {
return;
}
switch (e.getKeyCode()) {
/*
* Standard WASD camera controls, with E and Q moving up and down,
* respectively.
*/
case KeyEvent.VK_A: {
this.input.setMovingLeft(true);
break;
}
case KeyEvent.VK_W: {
this.input.setMovingForward(true);
break;
}
case KeyEvent.VK_S: {
this.input.setMovingBackward(true);
break;
}
case KeyEvent.VK_D: {
this.input.setMovingRight(true);
break;
}
case KeyEvent.VK_E: {
this.input.setMovingUp(true);
break;
}
case KeyEvent.VK_Q: {
this.input.setMovingDown(true);
break;
}
}
}
@Override
public void keyReleased(
final KeyEvent e)
{
assert e != null;
/*
* Ignore events that are the result of keyboard auto-repeat. This means
* there's one single event when a key is pressed, and another when it is
* released (as opposed to an endless stream of both when the key is held
* down).
*/
if ((e.getModifiers() & InputEvent.AUTOREPEAT_MASK) == InputEvent.AUTOREPEAT_MASK) {
return;
}
switch (e.getKeyCode()) {
/*
* Pressing 'M' enables/disables the camera.
*/
case KeyEvent.VK_M: {
this.toggleCameraEnabled();
break;
}
/*
* Pressing 'P' makes the mouse cursor visible/invisible.
*/
case KeyEvent.VK_P: {
System.out.printf(
"Making pointer %s\n",
this.window.isPointerVisible() ? "invisible" : "visible");
this.window.setPointerVisible(!this.window.isPointerVisible());
break;
}
/*
* Pressing enter switches between windowed and fullscreen mode. JOGL
* requires that this be executed on a background thread.
*/
case KeyEvent.VK_ENTER: {
this.background_workers.execute(new Runnable()
{
@Override
public void run()
{
final boolean mode =
!ExampleFPSStyleKeyListener.this.window.isFullscreen();
ExampleFPSStyleKeyListener.this.window.setFullscreen(mode);
}
});
break;
}
/*
* Standard WASD camera controls, with E and Q moving up and down,
* respectively.
*/
case KeyEvent.VK_A: {
this.input.setMovingLeft(false);
break;
}
case KeyEvent.VK_W: {
this.input.setMovingForward(false);
break;
}
case KeyEvent.VK_S: {
this.input.setMovingBackward(false);
break;
}
case KeyEvent.VK_D: {
this.input.setMovingRight(false);
break;
}
case KeyEvent.VK_E: {
this.input.setMovingUp(false);
break;
}
case KeyEvent.VK_Q: {
this.input.setMovingDown(false);
break;
}
}
}
public void toggleCameraEnabled()
{
final boolean enabled = this.sim.cameraIsEnabled();
if (enabled) {
System.out.println("Disabling camera");
this.window.confinePointer(false);
} else {
System.out.println("Enabling camera");
this.window.confinePointer(true);
this.renderer.sendWantWarpPointer();
this.input.setRotationHorizontal(0.0);
this.input.setRotationVertical(0.0);
}
this.sim.cameraSetEnabled(!enabled);
}
}
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
import com.io7m.jcamera.JCameraFPSStyleInputType;
import com.io7m.jcamera.JCameraFPSStyleMouseRegion;
import com.io7m.jcamera.JCameraRotationCoefficientsMutable;
import com.jogamp.newt.event.MouseAdapter;
import com.jogamp.newt.event.MouseEvent;
import java.util.concurrent.atomic.AtomicReference;
/**
* The mouse adapter used to handle mouse events.
*/
// CHECKSTYLE_JAVADOC:OFF
public final class ExampleFPSStyleMouseAdapter extends MouseAdapter
{
private final AtomicReference<JCameraFPSStyleMouseRegion> mouse_region;
private final JCameraFPSStyleInputType input;
private final ExampleFPSStyleSimulationType sim;
private final JCameraRotationCoefficientsMutable rotations;
public ExampleFPSStyleMouseAdapter(
final AtomicReference<JCameraFPSStyleMouseRegion> in_mouse_region,
final ExampleFPSStyleSimulationType in_sim,
final JCameraRotationCoefficientsMutable in_rotations)
{
this.mouse_region = in_mouse_region;
this.input = in_sim.getInput();
this.sim = in_sim;
this.rotations = in_rotations;
}
@Override
public void mouseMoved(
final MouseEvent e)
{
assert e != null;
/*
* If the camera is enabled, get the rotation coefficients for the mouse
* movement.
*/
if (this.sim.cameraIsEnabled()) {
this.rotations.from(
this.mouse_region.get().coefficients(
e.getX(),
e.getY()));
this.input.addRotationAroundHorizontal(this.rotations.horizontal());
this.input.addRotationAroundVertical(this.rotations.vertical());
}
}
}
/*
* Copyright © 2021 Mark Raynsford <code@io7m.com> https://www.io7m.com
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package com.io7m.jcamera.examples.jogl;
import com.io7m.jcamera.JCameraFPSStyleMouseRegion;
import com.io7m.jcamera.JCameraFPSStyleSnapshot;
import com.io7m.jcamera.JCameraFPSStyleSnapshots;
import com.io7m.jcamera.JCameraScreenOrigin;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.DebugGL3;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
/**
* The GL event listener used to handle rendering and driving of the
* simulation.
*/
// CHECKSTYLE_JAVADOC:OFF
public final class ExampleFPSStyleGLListener implements GLEventListener
{
private final GLWindow window;
private final ExampleFPSStyleSimulationType sim;
private final AtomicReference<JCameraFPSStyleMouseRegion> mouse_region;
private final ExampleRendererType renderer;
private long time_then;
private double time_accum;
private JCameraFPSStyleSnapshot snap_curr;
private JCameraFPSStyleSnapshot snap_prev;
public ExampleFPSStyleGLListener(
final GLWindow in_window,
final JCameraFPSStyleSnapshot in_snap,
final ExampleFPSStyleSimulationType in_sim,
final AtomicReference<JCameraFPSStyleMouseRegion> in_mouse_region,
final ExampleRendererType in_renderer)
{
this.window = in_window;
this.sim = in_sim;
this.mouse_region = in_mouse_region;
this.renderer = in_renderer;
this.snap_curr = in_snap;
this.snap_prev = in_snap;
}
/**
* Initialize the simulation.
*
* @param drawable The OpenGL drawable
*/
@Override
public void init(
final GLAutoDrawable drawable)
{
try {
assert drawable != null;
final GL3 g = new DebugGL3(drawable.getGL().getGL3());
assert g != null;
this.time_then = System.nanoTime();
this.renderer.init(this.window, g);
this.renderer.reshape(this.window.getWidth(), this.window.getHeight());
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void dispose(
final GLAutoDrawable drawable)
{
// Nothing.
}
@Override
public void display(
final GLAutoDrawable drawable)
{
assert drawable != null;
/*
* Integrate the camera as many times as necessary for each rendering
* frame interval.
*/
final long time_now = System.nanoTime();
final long time_diff = time_now - this.time_then;
final double time_diff_s = (double) time_diff / 1000000000.0;
this.time_accum = this.time_accum + time_diff_s;
this.time_then = time_now;
final float sim_delta = this.sim.getDeltaTime();
while (this.time_accum >= (double) sim_delta) {
this.snap_prev = this.snap_curr;
this.snap_curr = this.sim.integrate();
this.time_accum -= sim_delta;
}
/*
* Determine how far the current time is between the current camera state
* and the next, and use that value to interpolate between the two saved
* states.
*/
final float alpha = (float) (this.time_accum / (double) sim_delta);
final JCameraFPSStyleSnapshot snap_interpolated =
JCameraFPSStyleSnapshots.interpolate(
this.snap_prev,
this.snap_curr,
alpha);
final GL3 g = new DebugGL3(drawable.getGL().getGL3());
assert g != null;
g.glClear(GL.GL_COLOR_BUFFER_BIT);
/*
* Draw the scene!
*/
this.renderer.draw(snap_interpolated, Optional.empty());
}
@Override
public void reshape(
final GLAutoDrawable drawable,
final int x,
final int y,
final int width,
final int height)
{
this.mouse_region.set(JCameraFPSStyleMouseRegion.of(
JCameraScreenOrigin.SCREEN_ORIGIN_TOP_LEFT,
width,
height));
this.renderer.reshape(width, height);
}
}
/*
* $example: Construct a new renderer.
*/
final ExampleRendererType renderer = new ExampleRenderer();
/*
* $example: Construct a new simulation and produce an initial snapshot of
* the camera for later use.
*/
final ExampleFPSStyleSimulationType sim =
new ExampleFPSStyleSimulation(renderer);
final JCameraFPSStyleSnapshot snap = sim.integrate();
/*
* $example: Declare a structure to hold mouse rotation coefficients, and
* a mouse region configured with an origin that matches that of JOGL's
* windowing system.
*/
final JCameraRotationCoefficientsMutable rotations =
JCameraRotationCoefficientsMutable.create();
final AtomicReference<JCameraFPSStyleMouseRegion> mouse_region =
new AtomicReference<>(
JCameraFPSStyleMouseRegion.of(
JCameraScreenOrigin.SCREEN_ORIGIN_TOP_LEFT,
640.0,
480.0));
/*
* $example: Initialize JOGL and open a window, construct an animator to
* regularly refresh the screen, and assign GL event listener, mouse
* listener, and keyboard listener.
*/
final GLProfile profile = GLProfile.get(GLProfile.GL3);
final GLCapabilities caps = new GLCapabilities(profile);
final GLWindow window = GLWindow.create(caps);
window.setSize(640, 480);
window.setTitle(ExampleFPSStyleMain.class.getCanonicalName());
final Animator anim = new Animator();
anim.add(window);
window.addGLEventListener(new ExampleFPSStyleGLListener(
window,
snap,
sim,
mouse_region,
renderer));
window.addMouseListener(new ExampleFPSStyleMouseAdapter(
mouse_region,
sim,
rotations));
window.addKeyListener(new ExampleFPSStyleKeyListener(
sim,
background_workers,
renderer,
window));
/*
* Close the program when the window closes.
*/
window.addWindowListener(new WindowAdapter()
{
@Override
public void windowDestroyed(
final WindowEvent e)
{
System.out.println("Stopping animator");
anim.stop();
System.out.println("Exiting");
System.exit(0);
}
});
window.setDefaultCloseOperation(
WindowClosingProtocol.WindowClosingMode.DISPOSE_ON_CLOSE);
window.setVisible(true);
/*
* Start everything running.
*/
anim.start();
}
}
module Forward where
import qualified Vector3f
move_forward :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_forward p forward d = Vector3f.add3 p (Vector3f.scale forward d)
module ExampleDefaultVectors where
import qualified Vector3f
h :: Float
h = 0
v :: Float
v = pi / 2.0
p :: Vector3f.T
p = Vector3f.V3 0.0 0.0 0.0
forward_x :: Float
forward_x = cos (v) * cos (h)
-- = cos (π / 2) * cos (0)
-- = 0 * 1
-- = 0
forward_y :: Float
forward_y = sin (h)
-- = sin (0)
-- = 0
forward_z :: Float
forward_z = -(cos (h) * sin (v))
-- = -(cos (0) * sin (π / 2))
-- = -(1 * 1)
-- = -1
forward :: Vector3f.T
forward = Vector3f.V3 forward_x forward_y forward_z
-- = (0, 0, -1)
right_x :: Float
right_x = cos (v - (pi / 2.0)) * cos (h)
-- = cos (0) * cos (0)
-- = 1 * 1
-- = 1
right_y :: Float
right_y = sin (h)
-- = sin (0)
-- = 0
right_z :: Float
right_z = -(cos (h) * sin (v - (pi / 2)))
-- = -(cos (0) * sin (0))
-- = -(1 * 0)
-- = 0
right :: Vector3f.T
right = Vector3f.V3 right_x right_y right_z
-- = (1, 0, 0)
up :: Vector3f.T
up = Vector3f.cross right forward
-- = ((right_y * forward_z) - (right_z * forward_y),
-- (right_z * forward_x) - (right_x * forward_z),
-- (right_x * forward_y) - (right_y * forward_x))
-- = ((0 * -1) - (0 * 0),
-- (0 * 0) - (1 * -1),
-- (1 * 0) - (0 * 0))
-- = (0, 1, 0)
module Forward where
import qualified Vector3f
move_forward :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_forward p forward d = Vector3f.add3 p (Vector3f.scale forward d)
module Backward where
import qualified Vector3f
import qualified Forward
move_backward :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_backward p forward d = Forward.move_forward p forward (-d)
module Right where
import qualified Vector3f
move_right :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_right p right d = Vector3f.add3 p (Vector3f.scale right d)
module Left where
import qualified Vector3f
import qualified Right
move_left :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_left p right d = Right.move_right p right (-d)
module Up where
import qualified Vector3f
move_up :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_up p up d = Vector3f.add3 p (Vector3f.scale up d)
module Down where
import qualified Vector3f
import qualified Up
move_down :: Vector3f.T -> Vector3f.T -> Float -> Vector3f.T
move_down p up d = Up.move_up p up (-d)
module ViewRotation where
import qualified Matrix4f
import qualified Vector3f
import qualified Vector4f
import Vector3f (x, y, z)
rotation :: (Vector3f.T, Vector3f.T, Vector3f.T) -> Matrix4f.T
rotation (right, up, forward) =
Matrix4f.T {
Matrix4f.column_3 = Vector4f.V4 0.0 0.0 0.0 1.0,
Matrix4f.column_2 = Vector4f.V4 (x forward) (y forward) (z forward) 0.0,
Matrix4f.column_1 = Vector4f.V4 (x up) (y up) (z up) 0.0,
Matrix4f.column_0 = Vector4f.V4 (x right) (y right) (z right) 0.0
}
module ViewTranslation where
import qualified Matrix4f
import qualified Vector3f
import qualified Vector4f
import Vector3f (x, y, z)
translation :: Vector3f.T -> Matrix4f.T
translation p =
let np_x = -(x p)
np_y = -(y p)
np_z = -(z p)
in
Matrix4f.T {
Matrix4f.column_3 = Vector4f.V4 np_x np_y np_z 1.0,
Matrix4f.column_2 = Vector4f.V4 0.0 0.0 1.0 0.0,
Matrix4f.column_1 = Vector4f.V4 0.0 1.0 0.0 0.0,
Matrix4f.column_0 = Vector4f.V4 1.0 0.0 0.0 0.0
}
module View where
import ViewTranslation (translation)
import ViewRotation (rotation)
import qualified Matrix4f
import qualified Vector3f
view_matrix :: Vector3f.T -> (Vector3f.T, Vector3f.T, Vector3f.T) -> Matrix4f.T
view_matrix p (right, up, forward) =
Matrix4f.mult (rotation (right, up, forward)) (translation p)
module Input (T (..)) where
data T = T {
is_moving_backward :: Bool,
is_moving_forward :: Bool,
is_moving_left :: Bool,
is_moving_right :: Bool,
is_moving_up :: Bool,
is_moving_down :: Bool,
rotation_horizontal :: Float,
rotation_vertical :: Float
} deriving (Eq, Show)
module MouseRegion (T, newRegion, coefficients) where
data Origin =
TopLeft
| BottomLeft
deriving (Eq, Show)
data T = T {
origin :: Origin,
width :: Float,
height :: Float,
center_x :: Float,
center_y :: Float
} deriving (Eq, Show)
newRegion :: Origin -> Float -> Float -> T
newRegion o w h = T {
origin = o,
width = w,
height = h,
center_x = w / 2.0,
center_y = h / 2.0
}
coefficients :: T -> (Integer, Integer) -> (Float, Float)
coefficients r (sx, sy) =
let fx = fromIntegral sx
fy = fromIntegral sy
ox = ((fx - center_x r) / width r) * 2.0
oy = ((fy - center_y r) / height r) * 2.0
in
case (origin r) of
TopLeft -> (-ox, -oy)
BottomLeft -> (-ox, oy)
module SecondLaw where
f :: Float -> Float -> Float
f m a = m * a
module SecondLawRewrite where
a :: Float -> Float -> Float
a f m = (1 / m) * f
module IntegratorForward where
import qualified Clamp
import qualified Vector3f
import qualified Input
forward_speed :: Input.T -> Float -> Float -> Float -> Float
forward_speed i sf a delta =
if (Input.is_moving_forward i)
then sf + (a * delta)
else sf
backward_speed :: Input.T -> Float -> Float -> Float -> Float
backward_speed i sf a delta =
if (Input.is_moving_backward i)
then sf - (a * delta)
else sf
forward :: (Vector3f.T, Vector3f.T, Float, Input.T, Float, Float, Float) -> Float -> (Vector3f.T, Float)
forward (p, v_forward, sf, i, a, d, ms) delta =
let
sf0 = backward_speed i (forward_speed i sf a delta) a delta
sf1 = Clamp.clamp sf0 (-ms, ms)
pr = Vector3f.add3 p (Vector3f.scale v_forward (sf1 * delta))
sfr = sf1 * (d ** delta)
in
(pr, sfr)
module IntegratorRight where
import qualified Clamp
import qualified Vector3f
import qualified Input
right_speed :: Input.T -> Float -> Float -> Float -> Float
right_speed i sf a delta =
if (Input.is_moving_right i)
then sf + (a * delta)
else sf
left_speed :: Input.T -> Float -> Float -> Float -> Float
left_speed i sf a delta =
if (Input.is_moving_left i)
then sf - (a * delta)
else sf
right :: (Vector3f.T, Vector3f.T, Float, Input.T, Float, Float, Float) -> Float -> (Vector3f.T, Float)
right (p, v_right, sf, i, a, d, ms) delta =
let
sf0 = left_speed i (right_speed i sf a delta) a delta
sf1 = Clamp.clamp sf0 (-ms, ms)
pr = Vector3f.add3 p (Vector3f.scale v_right (sf1 * delta))
sfr = sf1 * (d ** delta)
in
(pr, sfr)
module IntegratorUp where
import qualified Clamp
import qualified Vector3f
import qualified Input
up_speed :: Input.T -> Float -> Float -> Float -> Float
up_speed i sf a delta =
if (Input.is_moving_up i)
then sf + (a * delta)
else sf
down_speed :: Input.T -> Float -> Float -> Float -> Float
down_speed i sf a delta =
if (Input.is_moving_down i)
then sf - (a * delta)
else sf
up :: (Vector3f.T, Vector3f.T, Float, Input.T, Float, Float, Float) -> Float -> (Vector3f.T, Float)
up (p, v_up, sf, i, a, d, ms) delta =
let
sf0 = down_speed i (up_speed i sf a delta) a delta
sf1 = Clamp.clamp sf0 (-ms, ms)
pr = Vector3f.add3 p (Vector3f.scale v_up (sf1 * delta))
sfr = sf1 * (d ** delta)
in
(pr, sfr)
module IntegratorAngularVertical where
import qualified Clamp
import qualified Input
vertical :: (Float, Float, Input.T, Float, Float, Float) -> Float -> (Float, Float)
vertical (v, sv, i, a, d, ms) delta =
let
sf0 = sv + ((Input.rotation_vertical i) * a * delta)
sf1 = Clamp.clamp sf0 (-ms, ms)
vr = v + (sf1 * delta)
sfr = sf1 * (d ** delta)
in
(vr, sfr)
q = Vector3f.scale d r
p = Vector3f.add3 q t
forward = Vector3f.normalize (Vector3f.scale d -1)
right = Vector3f.cross forward up
project :: Vector3f.T -> Vector3f.T
project v =
let vx = Vector3f.x v
vz = Vector3f.z v
in Vector3f.normalize (Vector3f.V3 vx 0.0 vz)
forward_on_xz :: Vector3f.T
forward_on_xz = project forward
module ExampleSphericalDefaultVectors where
import qualified Vector3f
heading :: Float
heading = -pi / 2.0
incline :: Float
incline = 0
radius :: Float
radius = 8
target :: Vector3f.T
target = Vector3f.V3 0 1 4
direction :: Float -> Float -> Vector3f.T
direction heading incline =
let
x = (cos heading) * (cos incline)
y = sin incline
z = -((cos incline) * (sin heading))
in
Vector3f.V3 x y z
d :: Vector3f.T
d = direction heading incline
q :: Vector3f.T
q = Vector3f.scale d radius
p :: Vector3f.T
p = Vector3f.add3 q target
forward :: Vector3f.T
forward = Vector3f.normalize (Vector3f.scale d (-1.0))
up :: Vector3f.T
up =
let d = direction heading (incline - (pi / 2.0)) in
Vector3f.normalize (Vector3f.scale d (-1.0))
right :: Vector3f.T
right = Vector3f.cross forward up
project :: Vector3f.T -> Vector3f.T
project v =
let vx = Vector3f.x v
vz = Vector3f.z v
in Vector3f.normalize (Vector3f.V3 vx 0.0 vz)
forward_on_xz :: Vector3f.T
forward_on_xz = project forward
module NormalizedDevice where
import qualified Vector3f
width :: Float
width = 640.0
height :: Float
height = 480.0
to_ndc_from_top_left :: (Integer, Integer) -> (Float, Float)
to_ndc_from_top_left (sx, sy) =
let
center_x = width / 2.0
center_y = height / 2.0
rx = ((fromIntegral sx - center_x) / width) * 2.0
ry = ((fromIntegral sy - center_y) / height) * 2.0
in
(rx, ry)
to_ndc_from_bottom_left :: (Integer, Integer) -> (Float, Float)
to_ndc_from_bottom_left (sx, sy) =
let
center_x = width / 2.0
center_y = height / 2.0
rx = ((fromIntegral sx - center_x) / width) * 2.0
ry = ((fromIntegral sy - center_y) / height) * 2.0
in
(rx, -ry)
module InputSpherical (T (..)) where
data T = T {
is_moving_backward_key :: Bool,
is_moving_backward_cursor :: Bool,
is_moving_forward_key :: Bool,
is_moving_forward_cursor :: Bool,
is_moving_left_key :: Bool,
is_moving_left_cursor :: Bool,
is_moving_right_key :: Bool,
is_moving_right_cursor :: Bool,
is_moving_up :: Bool,
is_moving_down :: Bool,
moving_forward_continuous :: Float,
moving_right_continuous :: Float,
is_orbiting_heading_positive :: Bool,
is_orbiting_heading_negative :: Bool,
is_orbiting_incline_positive :: Bool,
is_orbiting_incline_negative :: Bool,
is_zooming_in :: Bool,
is_zooming_out :: Bool
} deriving (Eq, Show)
module IntegratorSphericalForward where
import qualified Clamp
import qualified Vector3f
import qualified InputSpherical
forward_speed :: InputSpherical.T -> Float -> Float -> Float -> Float
forward_speed i sf a delta =
if (InputSpherical.is_moving_forward_key i || InputSpherical.is_moving_forward_cursor i)
then sf + (a * delta)
else sf
backward_speed :: InputSpherical.T -> Float -> Float -> Float -> Float
backward_speed i sf a delta =
if (InputSpherical.is_moving_backward_key i || InputSpherical.is_moving_backward_cursor i)
then sf - (a * delta)
else sf
drag_forward_speed :: InputSpherical.T -> Float -> Float -> Float
drag_forward_speed i a delta =
(InputSpherical.moving_forward_continuous i) * a * delta
forward :: (Vector3f.T, Vector3f.T, Float, InputSpherical.T, Float, Float, Float) -> Float -> (Vector3f.T, Float)
forward (p, v_forward_on_xz, sf, i, a, d, ms) delta =
let
sf0 = backward_speed i (forward_speed i sf a delta) a delta
sf1 = Clamp.clamp sf0 (-ms, ms)
sd = drag_forward_speed i a delta
sf2 = sf1 + sd
pr = Vector3f.add3 p (Vector3f.scale v_forward_on_xz (sf2 * delta))
sfr = sf1 * (d ** delta)
in
(pr, sfr)
module IntegratorSphericalForwardZoomScaled where
import qualified Clamp
import qualified Vector3f
import qualified InputSpherical
type ScaleFunction = Float -> Float
forward_speed :: InputSpherical.T -> Float -> Float -> Float -> Float -> ScaleFunction -> Float
forward_speed i sf a zoom scale delta =
let accel = (scale zoom) * (a * delta) in
if (InputSpherical.is_moving_forward_key i || InputSpherical.is_moving_forward_cursor i)
then sf + accel
else sf
backward_speed :: InputSpherical.T -> Float -> Float -> Float -> Float -> ScaleFunction -> Float
backward_speed i sf a zoom scale delta =
let accel = (scale zoom) * (a * delta) in
if (InputSpherical.is_moving_backward_key i || InputSpherical.is_moving_backward_cursor i)
then sf - accel
else sf
drag_forward_speed :: InputSpherical.T -> Float -> Float -> ScaleFunction -> Float
drag_forward_speed i a zoom scale delta =
(InputSpherical.moving_forward_continuous i) * a * (scale zoom) * delta
forward :: (Vector3f.T, Vector3f.T, Float, InputSpherical.T, Float, Float, Float, Float, (ScaleFunction, ScaleFunction)) -> Float -> (Vector3f.T, Float)
forward (p, v_forward_on_xz, sf, i, a, d, ms, zoom, (scale_linear, scale_dragging)) delta =
let
sf0 = backward_speed i (forward_speed i sf a delta) a delta
sms = (scale_linear zoom) * ms
sf1 = Clamp.clamp sf0 (-sms, sms)
sd = drag_forward_speed i a zoom scale_dragging delta
sf2 = sf1 + sd
pr = Vector3f.add3 p (Vector3f.scale v_forward_on_xz (sf2 * delta))
sfr = sf1 * (d ** delta)
in
(pr, sfr)