$ mvn -C clean install
<dependency> <groupId>com.io7m.jcamera</groupId> <artifactId>com.io7m.jcamera-core</artifactId> <version>0.5.0</version> </dependency>
Copyright © 2016 <code@io7m.com> http://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 © 2016 <code@io7m.com> http://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( final JCameraReadableSnapshotType s, final 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 © 2016 <code@io7m.com> http://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 © 2016 <code@io7m.com> http://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 © 2016 <code@io7m.com> http://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.jnull.Nullable; 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 @Nullable 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 @Nullable 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 © 2016 <code@io7m.com> http://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.io7m.jnull.Nullable; 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 @Nullable 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( (double) e.getX(), (double) e.getY())); this.input.addRotationAroundHorizontal(this.rotations.horizontal()); this.input.addRotationAroundVertical(this.rotations.vertical()); } } }
/* * Copyright © 2016 <code@io7m.com> http://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.io7m.jnull.Nullable; 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 @Nullable 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 @Nullable GLAutoDrawable drawable) { // Nothing. } @Override public void display( final @Nullable 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 -= (double) 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, (double) 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 @Nullable 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, (double) width, (double) 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 @Nullable 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)