diff --git a/.gitignore b/.gitignore index 7e6b989..8f54246 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ penumbra.ipr /native/* /autodoc/* penumbra.jar -pom.xml \ No newline at end of file +pom.xml +META-INF \ No newline at end of file diff --git a/project.clj b/project.clj index 6ce9e11..e0f71bf 100644 --- a/project.clj +++ b/project.clj @@ -6,5 +6,6 @@ [org.clojure/clojure-contrib "1.2.0-SNAPSHOT"]] :native-dependencies [[penumbra/lwjgl "2.4.2"]] :dev-dependencies [[native-deps "1.0.0"] + [autodoc "0.7.1"] [lein-clojars "0.5.0-SNAPSHOT"] [leiningen/lein-swank "1.1.0"]]) \ No newline at end of file diff --git a/src/penumbra/app.clj b/src/penumbra/app.clj index 6cc890f..c3403cd 100644 --- a/src/penumbra/app.clj +++ b/src/penumbra/app.clj @@ -44,7 +44,9 @@ (next args))))) arglists))) -(defmacro- auto-extend [type protocol template & explicit] +(defmacro- auto-extend + "Lets the application, which contains an implementation of a protocol, automatically extend that protocol." + [type protocol template & explicit] (let [sigs (eval `(vals (:sigs ~protocol)))] (list `extend @@ -62,7 +64,9 @@ ;;; -(defn- update- [app state f args] +(defn- update- + "Updates the state of the application." + [app state f args] (swap! state (fn [state] (let [state* (if (empty? args) @@ -72,7 +76,9 @@ (controller/invalidated! app true)) (or state* state))))) -(defn- alter-callbacks [clock callbacks] +(defn- alter-callbacks + "Updates the update and display callbacks using timed-fn" + [clock callbacks] (let [wrap (partial loop/timed-fn clock) callbacks (if (:update callbacks) (update-in callbacks [:update] wrap) @@ -122,7 +128,9 @@ (defmethod print-method penumbra.app.App [app writer] (.write writer "App")) -(defn create [callbacks state] +(defn create + "Creates an application." + [callbacks state] (let [window (atom nil) input (atom nil) queue (atom nil) @@ -142,12 +150,15 @@ ;;; -(defn app [] +(defn app + "Returns the current application." + [] app/*app*) -(defn- transform-import-arglists [protocol name arglists] +(defn- transform-import-arglists [protocol name doc arglists] (list* `defn name + doc (map (fn [args] (list @@ -158,13 +169,15 @@ (next args)))) arglists))) -(defmacro- auto-import [protocol & imports] +(defmacro- auto-import + "Creates an function which automatically fills in app with *app*" + [protocol & imports] (let [sigs (eval `(vals (:sigs ~protocol)))] (list* 'do (map - (fn [{name :name arglists :arglists}] - (transform-import-arglists protocol name arglists)) + (fn [{name :name arglists :arglists doc :doc}] + (transform-import-arglists protocol name doc arglists)) (let [imports (set imports)] (filter #(imports (:name %)) sigs)))))) @@ -180,45 +193,68 @@ ;; (defn clock + "Returns the application clock." ([] (clock app/*app*)) ([app] (:clock app))) (defn now + "Returns the elapsed clock time, in seconds, since the application began." ([] (now app/*app*)) ([app] @(clock app))) (defn speed! + "Sets the application clock speed." ([speed] (speed! app/*app* speed)) ([app speed] (time/speed! (clock app) speed))) (defn periodic-update! + "Starts a recurring update, which is called 'hz' times a second. + Time is governed by 'clock', which defaults to the application clock. + + OpenGL calls cannot be made within this callback." ([hz f] (periodic-update! (clock) hz f)) ([clock hz f] (periodic-update! app/*app* clock hz f)) ([app clock hz f] (queue/periodic-enqueue! app clock hz #(update- app (:state app) f nil)))) (defn delay! + "Enqueues an update to be executed in 'delay' milliseconds. + Time is goverend by 'clock', which defaults to the application clock. + + OpenGL calls cannot be made within this callback." ([delay f] (delay! (clock) delay f)) ([clock delay f] (delay! app/*app* clock delay f)) ([app clock delay f] (queue/enqueue! app clock delay #(update- app (:state app) f nil)))) (defn update! + "Enqueues an update to happen immediately. + + OpenGL calls cannot be made within the callback." ([f] (update! app/*app* f)) ([app f] (delay! (clock app) 0 f))) (defn enqueue! + "Enqueues an update to happen before the next frame is rendered. + + OpenGL calls in this callback are okay." ([f] (enqueue! app/*app* f)) ([app f] (event/subscribe-once! app :enqueue #(update- app (:state app) f nil)))) (defn repaint! + "Forces the application to repaint." ([] (repaint! app/*app*)) ([app] (controller/invalidated! app true))) (defn frequency! [hz] + "Updates the update frequency of a periodic update. + + This can only be called from within the periodic callback. A frequency of 0 or less will halt the update." (reset! app/*hz* hz)) ;; -(defmacro with-gl [& body] +(defmacro with-gl + "Creates a valid OpenGL context within the scope." + [& body] `(slate/with-slate (context/with-context nil ~@body))) @@ -245,7 +281,8 @@ (if (window/close? app) (controller/stop! app :requested-by-user)))) -(defn start-single-thread [app loop-fn] +(defn start-single-thread + [app loop-fn] (context/with-context nil (loop-fn app diff --git a/src/penumbra/app/awt.clj b/src/penumbra/app/awt.clj deleted file mode 100644 index f9f1f1b..0000000 --- a/src/penumbra/app/awt.clj +++ /dev/null @@ -1,24 +0,0 @@ -;; Copyright (c) Zachary Tellman. All rights reserved. -;; The use and distribution terms for this software are covered by the -;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this distribution. -;; By using this software in any fashion, you are agreeing to be bound by -;; the terms of this license. -;; You must not remove this notice, or any other, from this -;; software. - -(ns penumbra.app.awt - (:require [penumbra.app - [event :as event] - [controller :as controller]]) - (:use [penumbra.app.core]) - (:import [org.lwjgl.opengl AWTGLCanvas])) - -(defn create [app] - (let [canvas-ref (atom nil)] - (reset! - canvas-ref - (proxy [AWTGLCanvas] [] - (update [_]) - (paintGL [] - ))))) \ No newline at end of file diff --git a/src/penumbra/app/controller.clj b/src/penumbra/app/controller.clj index 3992375..57584d6 100644 --- a/src/penumbra/app/controller.clj +++ b/src/penumbra/app/controller.clj @@ -12,14 +12,14 @@ ;;; (defprotocol Controller - (paused? [c]) - (pause! [c]) - (stopped? [c]) - (stop! [c] [c flag]) - (resume! [c]) - (invalidated? [c]) - (invalidated! [c flag]) - (wait! [c])) + (paused? [c] "Returns true if the application is paused.") + (pause! [c] "Pauses the application.") + (stopped? [c] "Returns true if the application is stopped.") + (stop! [c] [c flag] "Stops the application.") + (resume! [c] "Resumes the application. If the application is currently running, this is a no-op.") + (invalidated? [c] "Returns true if the application needs to be repainted.") + (invalidated! [c flag] "Sets whether the application needs to be repainted.") + (wait! [c] "Halts execution of the thread if application is paused or stops. The thread will resume once the application does.")) ;;; diff --git a/src/penumbra/app/event.clj b/src/penumbra/app/event.clj index 03818e7..7347556 100644 --- a/src/penumbra/app/event.clj +++ b/src/penumbra/app/event.clj @@ -9,10 +9,10 @@ (ns penumbra.app.event) (defprotocol EventHandler - (subscribe! [event hook f] "Subscribe to an event.") - (unsubscribe! [event hook f]) - (subscribe-once! [event hook f]) - (publish- [event hook args])) + (subscribe! [event hook f] "Subscribe to event 'hook' with callback 'f'.") + (unsubscribe! [event hook f] "Unsubscribes callback 'f' from event 'hook'") + (subscribe-once! [event hook f] "Subscribes callback 'f'. Once the callback is triggered, it is unsubscribed.") + (publish- [event hook args] "Publishes an event. Any callbacks will be invoked with 'args'")) (defn create [] (let [event (ref {})] diff --git a/src/penumbra/app/input.clj b/src/penumbra/app/input.clj index 736df1b..c36d119 100644 --- a/src/penumbra/app/input.clj +++ b/src/penumbra/app/input.clj @@ -16,14 +16,14 @@ ;;; (defprotocol InputHandler - (init! [i]) - (destroy! [i]) - (key-repeat! [i flag]) - (key-pressed? [i key]) - (button-pressed? [i button]) - (mouse-location [i]) - (handle-mouse! [i]) - (handle-keyboard! [i])) + (init! [i] "Initialize the input handler.") + (destroy! [i] "Clean up the input handlers.") + (key-repeat! [i flag] "Sets whether a constantly pressed key triggers multiple key-press events.") + (key-pressed? [i key] "Checks whether a key is currently pressed.") + (button-pressed? [i button] "Checks whether a mouse button is currently presseed.") + (mouse-location [i] "Returns the current location of the mouse. [0 0] is the upper-left corner.") + (handle-mouse! [i] "Handle mouse input.") + (handle-keyboard! [i] "Mouse keyboard input.")) ;;Keyboard diff --git a/src/penumbra/app/loop.clj b/src/penumbra/app/loop.clj index 478ea16..3e8732a 100644 --- a/src/penumbra/app/loop.clj +++ b/src/penumbra/app/loop.clj @@ -17,7 +17,9 @@ ;;; -(defn timed-fn [clock f] +(defn timed-fn + "Creates a wrapper function which prepends any arguments with [dt t] in seconds." + [clock f] (when f (let [previous (atom @clock)] (fn [& args] @@ -27,11 +29,12 @@ (finally (reset! previous now)))))))) -(defn create-thread [app outer-fn inner-fn] +(defn create-thread + "Creates a thread. 'outer-fn' is passed 'inner-fn' as its only argument." + [app outer-fn inner-fn] (Thread. #(with-app app - (outer-fn - (inner-fn))))) + (outer-fn inner-fn)))) (defn pauseable-loop [app outer-fn inner-fn] diff --git a/src/penumbra/app/queue.clj b/src/penumbra/app/queue.clj index cf3d2b5..810dbfe 100644 --- a/src/penumbra/app/queue.clj +++ b/src/penumbra/app/queue.clj @@ -14,10 +14,10 @@ ;;; (defprotocol Queue - (init- [q]) - (start-consumer-thread [q]) - (enqueue- [q delay f]) - (periodic-enqueue- [q hz f])) + (init- [q] "Initializes action queue.") + (start-consumer-thread [q] "Starts a consumer thread for queue.") + (enqueue- [q delay f] "Enqueues an action to be executed in 'delay' milliseconds") + (periodic-enqueue- [q hz f] "Creates a recurring action to be executed 'hz' times a second")) (defn- create-queue [app clock] (let [heap (ref (sorted-set-by #(- (compare (first %2) (first %1))))) diff --git a/src/penumbra/app/window.clj b/src/penumbra/app/window.clj index f2a56b0..1f57331 100644 --- a/src/penumbra/app/window.clj +++ b/src/penumbra/app/window.clj @@ -24,21 +24,21 @@ ;;; (defprotocol Window - (display-modes [w]) - (display-mode [w]) - (display-mode! [window w h] [w mode]) - (title! [w title]) - (size [w]) - (resized? [w]) - (invalidated? [w]) - (close? [w]) - (process! [w]) - (update! [w]) - (handle-resize! [w]) - (init! [w]) - (destroy! [w]) - (vsync! [w flag]) - (fullscreen! [w flag])) + (display-modes [w] "Returns all display modes supported by the display device.") + (display-mode [w] "Returns the current display mode.") + (display-mode! [window w h] [w mode] "Sets the display mode.") + (title! [w title] "Sets the title of the application.") + (size [w] "Returns the current size of the application.") + (resized? [w] "Returns true if application was resized since handle-resize! was last called.") + (invalidated? [w] "Returns true if the window is invalidated by the operating system.") + (close? [w] "Returns true if the user has requested it be closed.") + (process! [w] "Processes all messages from the operating system.") + (update! [w] "Swaps the buffers.") + (handle-resize! [w] "Handles any resize events. If there wasn't a resizing, this is a no-op.") + (init! [w] "Initializes the window.") + (destroy! [w] "Destroys the window.") + (vsync! [w flag] "Toggles vertical sync.") + (fullscreen! [w flag] "Toggles fullscreen mode.")) ;;; diff --git a/src/penumbra/node.clj b/src/penumbra/node.clj index f9092ce..fbb43bf 100644 --- a/src/penumbra/node.clj +++ b/src/penumbra/node.clj @@ -27,14 +27,3 @@ (defn update [node event & args] (update- node event args)) - -(defprotocol TestP - (f [x y z])) - -(deftype TestT [a]) - -(def t (TestT (reify TestP (f [x y z] (+ y z))))) - - - -(auto-extend ::TestT TestP (:a this)) \ No newline at end of file diff --git a/src/penumbra/node/core.clj b/src/penumbra/node/core.clj deleted file mode 100644 index 088e6af..0000000 --- a/src/penumbra/node/core.clj +++ /dev/null @@ -1,12 +0,0 @@ -;; Copyright (c) Zachary Tellman. All rights reserved. -;; The use and distribution terms for this software are covered by the -;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this distribution. -;; By using this software in any fashion, you are agreeing to be bound by -;; the terms of this license. -;; You must not remove this notice, or any other, from this software. - -(ns penumbra.node.core) - -(defn create [callbacks state] - ) diff --git a/src/penumbra/opencl.clj b/src/penumbra/opencl.clj deleted file mode 100644 index a883349..0000000 --- a/src/penumbra/opencl.clj +++ /dev/null @@ -1,81 +0,0 @@ -;; Copyright (c) Zachary Tellman. All rights reserved. -;; The use and distribution terms for this software are covered by the -;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -;; which can be found in the file epl-v10.html at the root of this distribution. -;; By using this software in any fashion, you are agreeing to be bound by -;; the terms of this license. -;; You must not remove this notice, or any other, from this software. - -(ns penumbra.opencl - (:import (com.nativelibs4java.opencl CLContext CLDevice CLQueue OpenCL4Java CLEvent CLDevice$QueueProperties)) - (:import (com.nativelibs4java.opencl.library OpenCLLibrary)) - (:use [penumbra.opencl core])) - -;;; - -(defn get-platforms [] - (seq (OpenCL4Java/listPlatforms))) - -(defn get-devices [platform] - (seq (.listAllDevices platform true))) - -(defn get-cpu-devices [platform] - (seq (.listCPUDevices platform true))) - -(defn get-gpu-devices [platform] - (seq (.listGPUDevices platform true))) - -(defn get-device [platform] - (or (first (get-gpu-devices platform)) (first (get-cpu-devices platform)))) - -(defn enable-profiling [queue enabled] - (.setProperty queue CLDevice$QueueProperties/ProfilingEnable enabled)) - -(defn enable-out-of-order [queue enabled] - (.setProperty queue CLDevice$QueueProperties/OutOfOrderExecModeEnable enabled)) - -(defn create-context - ([] - (create-context [(get-device (first (get-platforms)))])) - ([devices] - (OpenCL4Java/createContext (into-array* devices)))) - -(defmacro with-device [device & body] - `(binding [*device* ~device] - ~@body)) - -(defmacro enqueue [& body] - `(binding [*queue* (.createQueue *device* *context*)] - (enable-profiling *queue* *profiling*) - ~@body - (dosync (alter *events* (fn [x#] (conj x# (.enqueueMarker *queue*))))))) - -(defmacro execute [& body] - `(binding [*queue* (.createQueue *device* *context*) - *events* (ref [])] - (enable-profiling *queue* *profiling*) - ~@body - (if (not (empty @*events*)) - (CLEvent/waitFor (into-array* @*events*))) - (.finish *queue*))) - -(defmacro profile [& body] - `(do - (binding [*profiling* true] - (enable-profiling *queue* true) - ~@body - (enable-profiling *queue* false)))) - -(defmacro with-context [context & body] - `(binding [*context* ~context - *device* (first (.getDevices ~context))] - (execute - ~@body))) - -(defmacro with-empty-context [& body] - `(with-context (create-context) - ~@body)) - -;;; - - diff --git a/src/penumbra/opencl/core.clj b/src/penumbra/opencl/core.clj deleted file mode 100644 index 9f2aadd..0000000 --- a/src/penumbra/opencl/core.clj +++ /dev/null @@ -1,44 +0,0 @@ -; Copyright (c) Zachary Tellman. All rights reserved. -; The use and distribution terms for this software are covered by the -; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) -; which can be found in the file epl-v10.html at the root of this distribution. -; By using this software in any fashion, you are agreeing to be bound by -; the terms of this license. -; You must not remove this notice, or any other, from this software. - -(ns penumbra.opencl.core - (:import (com.nativelibs4java.opencl CLContext CLDevice CLQueue OpenCL4Java)) - (:import (com.nativelibs4java.opencl.library OpenCLLibrary)) - (:import (java.lang.reflect Field)) - (:import (com.sun.jna.ptr IntByReference)) - (:use [clojure.contrib.def :only (defn-memo)])) - -;;; - -(def *context* nil) -(def *device* nil) -(def *queue* nil) -(def *profiling* false) -(def *events* nil) -(def *cl* (OpenCLLibrary/INSTANCE)) - -;;; - -(defn into-array* [coll] - (let [ary (make-array (.getClass (first coll)) (count coll))] - (dotimes [i (count coll)] - (aset ary i (nth coll i))) - ary)) - -;;; - -(defn-memo enum-name - [enum-value] - (let [fields (seq (.. *cl* (getClass) (getFields)))] - (.getName #^Field (some #(if (= enum-value (.get #^Field % *cl*)) % nil) fields)))) - -(defn-memo enum [k] - (let [cl (str "CL_" (.. (name k) (replace \- \_) (toUpperCase)))] - (eval `(. OpenCLLibrary ~(symbol cl))))) - - diff --git a/src/penumbra/opengl.clj b/src/penumbra/opengl.clj index efbe9dd..e2eaec3 100644 --- a/src/penumbra/opengl.clj +++ b/src/penumbra/opengl.clj @@ -8,7 +8,7 @@ (ns penumbra.opengl (:use [penumbra.opengl core] - [clojure.contrib.def :only (defn-memo)]) + [clojure.contrib.def :only (defn-memo defmacro- defvar)]) (:require [penumbra.opengl.texture :as tex] [penumbra.data :as data] [penumbra.opengl.frame-buffer :as fb] @@ -24,6 +24,16 @@ ;;; +(defmacro- import-fn [sym] + (let [m (meta (eval sym)) + m (meta (intern (:ns m) (:name m))) + n (:name m) + arglists (:arglists m) + doc (:doc m)] + (list `def (with-meta n {:doc doc :arglists (list 'quote arglists)}) (eval sym)))) + +;;; + (gl-import+ glEnable enable) (gl-import+ glDisable disable) (gl-import+ glIsEnabled enabled?) @@ -142,16 +152,16 @@ ;;Geometry -(def vertex geometry/vertex) -(def normal geometry/normal) -(def texture geometry/texture) -(def attribute geometry/attribute) -(def rotate geometry/rotate) -(def scale geometry/scale) -(def color geometry/color) -(def translate geometry/translate) -(def load-identity geometry/load-identity) -(def declare-attributes geometry/declare-attributes) +(import-fn geometry/vertex) +(import-fn geometry/normal) +(import-fn geometry/texture) +(import-fn geometry/attribute) +(import-fn geometry/rotate) +(import-fn geometry/scale) +(import-fn geometry/color) +(import-fn geometry/translate) +(import-fn geometry/load-identity) +(import-fn geometry/declare-attributes) (defmacro push-matrix [& body] `(geometry/with-transform- *renderer* (fn [] ~@body))) @@ -216,11 +226,12 @@ (gl-import+ glBlendFunc blend-func) (gl-import+ glShadeModel shade-model) -(def render-mode fx/render-mode) -(def color fx/color) -(def material fx/material) -(def fog fx/fog) -(def light fx/light) +(import-fn fx/render-mode) +(import-fn fx/render-mode) +(import-fn fx/color) +(import-fn fx/material) +(import-fn fx/fog) +(import-fn fx/light) (defmacro with-render-mode [mode & body] `(fx/with-render-mode ~mode (fn [] ~@body))) @@ -281,10 +292,10 @@ (gl-import+ glTexParameteri tex-parameter) (gl-import+ glIsTexture valid-texture-id?) -(def bind-texture tex/bind-texture) -(def texture tex/texture) -(def create-texture tex/create-texture) -(def build-mip-map tex/build-mip-map) +(import-fn tex/bind-texture) +(import-fn tex/texture) +(import-fn tex/create-texture) +(import-fn tex/build-mip-map) (defmacro with-texture [tex & body] `(tex/with-texture ~tex (fn [] ~@body))) diff --git a/src/penumbra/opengl/geometry.clj b/src/penumbra/opengl/geometry.clj index 3788fbf..00715ab 100644 --- a/src/penumbra/opengl/geometry.clj +++ b/src/penumbra/opengl/geometry.clj @@ -6,7 +6,8 @@ ; the terms of this license. ; You must not remove this notice, or any other, from this software. -(ns penumbra.opengl.geometry +(ns ^{:skip-wiki true} + penumbra.opengl.geometry (:use [clojure.contrib.def :only (defmacro- defvar)] [cantor] [penumbra.opengl.core]) @@ -29,6 +30,7 @@ (with-transform- [r f])) (defn vertex + "Defines the coordinates for a new vertex." ([v] (cond (vec2? v) (vertex (v 0) (v 1)) @@ -39,6 +41,7 @@ ([x y z w] (vertex x y z))) (defn texture + "Defines the texture coordinate for subsequent vertices." ([v] (cond (vec2? v) (texture (v 0) (v 1)) (vec3? v) (texture (v 0) (v 1) (v 2)) @@ -48,13 +51,15 @@ ([u v w] (texture- *renderer* u v w))) (defn normal + "Defines the normal vector for subsequent vertices." ([v] (cond - (vec2? v) (normal (v 0) (v 1)) + (vec2? v) (normal (v 0) (v 1) 0) (vec3? v) (normal (v 0) (v 1) (v 2)) :else (apply normal v))) ([x y z] (normal- *renderer* x y z))) (defn translate + "Translates the position of subsequent vertices." ([v] (cond (vec2? v) (translate (v 0) (v 1)) (vec3? v) (translate (v 0) (v 1) (v 2)) @@ -63,6 +68,7 @@ ([x y z] (translate- *renderer* x y z))) (defn scale + "Scales the position of subsequent vertices." ([v] (cond (vec2? v) (scale (v 0) (v 1)) (vec3? v) (scale (v 0) (v 1) (v 2)) @@ -71,6 +77,7 @@ ([x y z] (scale- *renderer* x y z))) (defn color + "Defines the color of subsequent vertices." ([c] (cond (vec2? c) (color (c 0) (c 1)) (vec3? c) (color (c 0) (c 1) (c 2)) @@ -78,13 +85,19 @@ ([r g b] (color r g b 1)) ([r g b a] (color- *renderer* r g b a))) -(defn attribute [attrib & values] +(defn attribute + "Defines attribute 'attrib' for subsequent vertices." + [attrib & values] (attribute- *renderer* attrib values)) -(defn rotate [angle x y z] +(defn rotate + "Rotates the position of subsequent vertices." + [angle x y z] (rotate- *renderer* angle x y z)) -(defn load-identity [] +(defn load-identity + "Resets the transformation of subsequent vertices." + [] (load-identity- *renderer*)) ;;; diff --git a/test/example/gpgpu/fluid.clj b/test/example/gpgpu/fluid.clj index 55ada80..97c0df7 100644 --- a/test/example/gpgpu/fluid.clj +++ b/test/example/gpgpu/fluid.clj @@ -15,7 +15,7 @@ [penumbra.opengl.texture :as tex] [penumbra.data :as data])) -(def dim [800 600]) +(def dim [400 300]) (defn textured-quad [] (draw-quads