diff --git a/example/game/asteroids.clj b/example/game/asteroids.clj index 6130c33..4fe6557 100644 --- a/example/game/asteroids.clj +++ b/example/game/asteroids.clj @@ -9,8 +9,6 @@ (ns example.game.asteroids (:use [penumbra opengl geometry]) (:require [penumbra.app :as app]) - (:require [penumbra.input :as input]) - (:require [penumbra.window :as window]) (:use [clojure.contrib.seq-utils :only (separate)])) ;;; @@ -154,7 +152,7 @@ 15 0.25 [0 0 1] 2))))) (defn emit-flame [state] - (if (input/key-pressed? :up) + (if (app/key-pressed? :up) (let [ship (:spaceship state) theta (+ 180 (:theta ship) (- (rand 20) 10)) particles (:particles state) @@ -168,11 +166,11 @@ (let [p (:position ship) v (:velocity ship) theta (:theta ship) - theta (condp (fn [x _] (input/key-pressed? x)) nil + theta (condp (fn [x _] (app/key-pressed? x)) nil :left (rem (+ theta (* 360 dt)) 360) :right (rem (- theta (* 360 dt)) 360) theta) - a (if (input/key-pressed? :up) + a (if (app/key-pressed? :up) (map (partial * 3) (cartesian theta)) [0 0]) v (map + v (map * a (repeat dt))) @@ -284,7 +282,7 @@ (defn init [state] (app/set-title "Asteroids") - (window/vsync true) + (app/vsync true) (init-asteroids) (init-particles) (init-spaceship) diff --git a/example/game/tetris.clj b/example/game/tetris.clj index ab5a83c..c471f47 100644 --- a/example/game/tetris.clj +++ b/example/game/tetris.clj @@ -9,7 +9,6 @@ (ns example.game.tetris (:use [penumbra opengl]) (:require [penumbra.app :as app]) - (:require [penumbra.input :as input]) (:use [clojure.contrib.seq-utils :only (indexed)]) (:use [clojure.contrib.def :only (defn-memo)]) (:use [clojure.contrib.pprint])) @@ -182,11 +181,11 @@ (app/start-update-loop 2 (fn [state] - (if (input/key-pressed? :down) + (if (app/key-pressed? :down) (app/frequency! 10) (app/frequency! 2)) (descend state))) - (input/key-repeat true) + (app/key-repeat true) state) (defn reshape [[x y w h] state] diff --git a/example/opengl/gears.clj b/example/opengl/gears.clj index ecf48d5..f58e730 100644 --- a/example/opengl/gears.clj +++ b/example/opengl/gears.clj @@ -9,7 +9,7 @@ (ns example.opengl.gears (:use [penumbra opengl geometry]) (:require [penumbra.app :as app]) - (:require [penumbra.window :as window])) + (:require [penumbra.text :as text])) ;;;;;;;;;;;;;;;;;;;;;;;;; ;;Gear building functions @@ -85,6 +85,7 @@ (defn init [state] (app/set-title "Gears") + (disable :multisample) (enable :depth-test) (enable :lighting) (enable :light0) @@ -105,7 +106,7 @@ :rot-y (+ (:rot-y state) dx))) (defn display [[delta time] state] - (write-to-screen (format "%d fps" (int (/ 1 delta))) 0 0) + (text/write-to-screen (format "%d fps" (int (/ 1 delta))) 0 0) (rotate (:rot-x state) 1 0 0) (rotate (:rot-y state) 0 1 0) (rotate (* 20. (rem time 360)) 0 0 1) diff --git a/example/opengl/marble.clj b/example/opengl/marble.clj index f228f66..6194981 100644 --- a/example/opengl/marble.clj +++ b/example/opengl/marble.clj @@ -9,7 +9,6 @@ (ns example.opengl.marble (:use [penumbra opengl geometry]) (:require [penumbra.app :as app]) - (:require [penumbra.window :as window]) (:use [penumbra.glsl.effects])) ;;;;;;;;;;;;;;;;; @@ -68,7 +67,6 @@ (enable :depth-test) (enable :lighting) (enable :light0) - (window/vsync true) (assoc state :sphere (sphere-geometry 100) :program (create-program declarations vertex-shader fragment-shader))) diff --git a/example/opengl/render_to_texture.clj b/example/opengl/render_to_texture.clj index 9487bc4..ee9316e 100644 --- a/example/opengl/render_to_texture.clj +++ b/example/opengl/render_to_texture.clj @@ -8,8 +8,7 @@ (ns example.opengl.render-to-texture (:use [penumbra opengl]) - (:require [penumbra.app :as app]) - (:require [penumbra.window :as window])) + (:require [penumbra.app :as app])) (defn textured-quad [] (push-matrix @@ -65,7 +64,7 @@ state) (defn mouse-drag [[dx dy] [x y] button state] - (let [[w h] (window/dimensions)] + (let [[w h] (app/dimensions)] (if (< x (int (/ w 2))) (let [[lx ly] (:left state)] (assoc state :left [(+ lx dy) (+ ly dx)])) @@ -77,7 +76,7 @@ [rx ry] (:right state) checkers (:checkers state) view (:view state) - [w h] (window/dimensions)] + [w h] (app/dimensions)] (light 0 :position [-1 -1 1 0]) diff --git a/example/opengl/sierpinski.clj b/example/opengl/sierpinski.clj index 74d325a..781481a 100644 --- a/example/opengl/sierpinski.clj +++ b/example/opengl/sierpinski.clj @@ -48,7 +48,6 @@ (app/set-title "Sierpinski Pyramid") (enable :normalize) (enable :depth-test) - (enable :multisample) (enable :cull-face) (enable :lighting) (enable :light0) diff --git a/src/penumbra/app.clj b/src/penumbra/app.clj index d43160d..15f7e62 100644 --- a/src/penumbra/app.clj +++ b/src/penumbra/app.clj @@ -8,16 +8,22 @@ ;; software. (ns penumbra.app + (:use [clojure.contrib.core :only (-?>)]) (:use [clojure.contrib.def :only [defmacro- defvar-]]) (:use [penumbra.opengl]) + (:use [penumbra.app.core]) (:use [penumbra.opengl.core :only [*texture-pool*]]) (:require [penumbra.opengl.texture :as texture]) (:require [penumbra.slate :as slate]) - (:require [penumbra.window :as window]) - (:require [penumbra.input :as input]) + (:require [penumbra.app.window :as window]) + (:require [penumbra.app.input :as input]) + (:require [penumbra.app.loop :as loop]) + (:import [org.lwjgl.input Keyboard]) (:import [org.lwjgl.opengl Display]) (:import [java.util.concurrent CountDownLatch])) +;;; + (defstruct app-struct :window :input @@ -28,10 +34,7 @@ :latch :invalidated?) -(defvar- *app* nil - "Current application.") - -(defn- create [callbacks state] +(defn create [callbacks state] (with-meta (struct-map app-struct :window (window/create) @@ -57,114 +60,99 @@ (Display/getTitle) (/ size 1e6) (* 100 active-percent)))))) -(defn cleanup - "Cleans up any active apps." - [] - (Display/destroy)) +;;Input -(defn repaint - "Forces a new frame to be redrawn" - [] - (when *app* - (reset! (:invalidated? *app*) true))) +(defn key-pressed? [key] + ((-> @(:keys *input*) vals set) key)) + +(defn key-repeat [enabled] + (Keyboard/enableRepeatEvents enabled)) + +;;Window (defn set-title [title] (Display/setTitle title)) -(defn- try-callback [callback & args] - (when-let [f (callback (:callbacks *app*))] - (when (not (identical? - @(:state *app*) - (swap! - (:state *app*) - (if (empty? args) - f - (apply partial (list* f args)))))) - (repaint)))) +(defn display-modes + "Returns a list of available display modes." + [] + (map window/transform-display-mode (Display/getAvailableDisplayModes))) + +(defn current-display-mode + "Returns the current display mode." + [] + (window/transform-display-mode (Display/getDisplayMode))) + +(defn set-display-mode + "Sets the current display mode." + ([width height] + (->> + (display-modes) + (filter #(= [width height] (:resolution %))) + (sort-by :bpp) + last + set-display-mode)) + ([mode] + (Display/setDisplayMode (:mode mode)) + (apply viewport (concat [0 0] (:resolution mode))))) + +(defn dimensions + "Returns dimensions of window as [width height]." + ([] + (dimensions *window*)) + ([w] + (window/dimensions w))) + +(defn vsync [enabled] + (Display/setVSyncEnabled enabled) + (reset! (:vsync? *window*) enabled)) + +(defn vsync? [] + @(:vsync? *window*)) + +(defn resizable + ([enabled] + (resizable *app* enabled)) + ([app enabled] + (if enabled + (window/enable-resizable app) + (window/disable-resizable app)))) -(defmacro- with-app [app & body] - `(binding [*app* ~app - input/*callback-handler* try-callback - window/*callback-handler* try-callback] - (input/with-input (:input ~app) - (window/with-window (:window ~app) - ~@body)))) +(defn fullscreen [enabled] + (Display/setFullscreen enabled)) -;;Loops -(defn clock [] (/ (System/nanoTime) 1e9)) +;;Update loops -(defvar- *hz* nil - "Refresh rate of periodic function") +(defn clock [] + (/ (System/nanoTime) 1e9)) (defn frequency! "Update frequency of update-loop. Can only be called from inside update-loop." [hz] (reset! *hz* hz)) -(defn- periodic-fn - [hz] - (let [frequency (atom hz)] - (fn [f] - (binding [*hz* frequency] - (let [start (clock)] - (f) - (let [delta (- (clock) start) - sleep (max 0 (- (/ 1 @*hz*) delta))] - (when-not (zero? sleep) - (Thread/sleep (long (* sleep 1e3)) (long (rem (* sleep 1e6) 1e6)))))))))) - -(defn- timed-fn [f] - (let [last-time (atom (clock))] - (fn [& args] - (let [current-time (clock)] - (try - (if f - (apply f (list* [(- current-time @last-time) current-time] args)) - @(:state *app*)) - (finally - (reset! last-time current-time))))))) - -(defn- update-loop - [latch-fn complete-fn inner-loop] - (loop [] - (when-let [latch (latch-fn)] - (.await #^CountDownLatch latch)) - (inner-loop) - (when-not (complete-fn) - (recur)))) - -(defn- secondary-loop [app hz f] - (let [wrapper (periodic-fn hz)] - (-> - (Thread. - (fn [] - (with-app app - (let [s (slate/create (:window app))] - (try - (Thread/sleep (/ 1000 hz)) - (update-loop - #(deref (:latch app)) - #(deref (:stopped? app)) - #(wrapper f)) - (finally - (slate/destroy s))))))) - .start))) - -(defn- primary-loop [app f] - (let [wrapper (periodic-fn 200)] - (with-app app - (update-loop - (constantly nil) - #(or @(:paused? app) @(:stopped? app)) - #(wrapper f))))) +;;App state -;;; +(defn cleanup + "Cleans up any active apps." + [] + (Display/destroy)) + +(defn repaint + "Forces a new frame to be redrawn" + [] + (when *app* + (reset! (:invalidated? *app*) true))) (defn stop - "Stops the application, and returns the app struct." + "Stops the application or update-loop, and returns nil." ([] - (stop *app*)) + (if *async-running?* + (do + (reset! *async-running?* false) + nil) + (stop *app*))) ([app] (reset! (:stopped? app) true) nil)) @@ -178,11 +166,20 @@ (reset! (:latch app) (CountDownLatch. 1)) nil)) +(defn pause + "Halts main loop, and yields control back to the REPL." + ([] + (pause *app*)) + ([app] + (reset! (:paused? app) true) + (reset! (:latch app) (CountDownLatch. 1)) + nil)) + (defn- destroy ([] (destroy *app*)) ([app] - (try-callback :close) + (loop/try-callback :close) (input/destroy) (window/destroy))) @@ -190,11 +187,11 @@ ([] (init *app*)) ([app] - (with-app app + (loop/with-app app (when @(:stopped? *app*) (window/init) - (try-callback :init) - (try-callback :reshape (concat [0 0] @(:size window/*window*))) + (loop/try-callback :init) + (loop/try-callback :reshape (concat [0 0] (dimensions))) (input/init) (reset! (:stopped? app) false))))) @@ -203,18 +200,18 @@ ([] (update-once *app*)) ([app] - (with-app app + (loop/with-app app (input/handle-keyboard) (input/handle-mouse) (window/check-for-resize) - (try-callback :update) + (loop/try-callback :update) (if (or (Display/isDirty) @(:invalidated? app)) (do (reset! (:invalidated? app) false) (clear 0 0 0) (push-matrix ((-> app :callbacks :display) @(:state app))) - (when @(:vsync? window/*window*) + (when (vsync?) (Display/sync 60))) (Thread/sleep 15)) (if (Display/isCloseRequested) @@ -232,8 +229,8 @@ (fn [callbacks] (-> callbacks - (update-in [:update] #(timed-fn %)) - (update-in [:display] #(timed-fn %))))) + (update-in [:update] #(loop/timed-fn %)) + (update-in [:display] #(loop/timed-fn %))))) (with-meta (meta app))))) (defn start @@ -242,13 +239,13 @@ (start (create callbacks state))) ([app] (let [app (alter-callbacks app)] - (with-app app + (loop/with-app app (try (init) (reset! (:paused? app) false) (when-let [latch @(:latch app)] (.countDown #^CountDownLatch latch)) - (primary-loop app update-once) + (loop/primary-loop app update-once) (catch Exception e (reset! (:stopped? app) true) (throw e)) @@ -261,7 +258,7 @@ ([hz f] (start-update-loop *app* hz f)) ([app hz f] - (secondary-loop + (loop/secondary-loop app hz #(do diff --git a/src/penumbra/app/core.clj b/src/penumbra/app/core.clj new file mode 100644 index 0000000..dc5dbf6 --- /dev/null +++ b/src/penumbra/app/core.clj @@ -0,0 +1,29 @@ +;; 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.core + (:use [clojure.contrib.def :only [defvar]])) + +(defvar *app* nil + "Current application.") + +(defvar *window* nil + "Current window") + +(defvar *input* nil + "Holds current input state.") + +(defvar *callback-handler* nil + "Function which routes to callbacks.") + +(defvar *hz* nil + "Refresh rate of update-loop") + +(defvar *async-running?* nil + "Should update-loop continue running") + diff --git a/src/penumbra/input.clj b/src/penumbra/app/input.clj similarity index 90% rename from src/penumbra/input.clj rename to src/penumbra/app/input.clj index 8693b69..deba2d7 100644 --- a/src/penumbra/input.clj +++ b/src/penumbra/app/input.clj @@ -6,21 +6,16 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns penumbra.input +(ns penumbra.app.input (:use [clojure.contrib.seq-utils :only [indexed]]) (:use [clojure.contrib.def :only [defvar]]) - (:use [penumbra.window :only [dimensions]]) + (:use [penumbra.app.window :only [dimensions]]) + (:use [penumbra.app.core]) (:import [org.lwjgl.input Keyboard Mouse])) (defstruct input-struct :keys) -(defvar *input* nil - "Holds current input state.") - -(defvar *callback-handler* nil - "Function that receives callbacks") - (defn create [] (struct-map input-struct :keys (atom {}))) @@ -45,12 +40,6 @@ ;;Keyboard -(defn key-pressed? [key] - ((-> @(:keys *input*) vals set) key)) - -(defn key-repeat [enabled] - (Keyboard/enableRepeatEvents enabled)) - (defn- current-key [] (let [char (Keyboard/getEventCharacter) key (Keyboard/getEventKey) @@ -88,7 +77,7 @@ (keyword (str "button" (inc button-idx))))) (defn handle-mouse [] - (let [[w h] (dimensions)] + (let [[w h] (dimensions *window*)] (loop [buttons (vec (map #(Mouse/isButtonDown %) (range (Mouse/getButtonCount))))] (Mouse/poll) (when (Mouse/next) diff --git a/src/penumbra/app/loop.clj b/src/penumbra/app/loop.clj new file mode 100644 index 0000000..98a09e9 --- /dev/null +++ b/src/penumbra/app/loop.clj @@ -0,0 +1,105 @@ +;; 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.loop + (:use [penumbra.opengl]) + (:use [penumbra.app.core]) + (:require [penumbra.opengl.texture :as texture]) + (:require [penumbra.slate :as slate]) + (:import [org.lwjgl.opengl Display]) + (:import [java.util.concurrent CountDownLatch]) + (:require [penumbra.app.window :as window]) + (:require [penumbra.app.input :as input])) + +;;; + +(defn- repaint [] + (reset! (:invalidated? *app*) true)) + +(defn try-callback [callback & args] + (when-let [f (callback (:callbacks *app*))] + (let [state @(:state *app*) + new-state (swap! + (:state *app*) + (fn [s] + (or (if (empty? args) + (f s) + (apply f (concat args [s]))) + state)))] + (when-not (identical? state new-state) + (repaint))))) + +(defmacro with-app [app & body] + `(binding [*app* ~app + *callback-handler* try-callback] + (input/with-input (:input ~app) + (window/with-window (:window ~app) + ~@body)))) + +;;Loops + +(defn- clock [] + (/ (System/nanoTime) 1e9)) + +(defn periodic-fn + [hz] + (let [frequency (atom hz)] + (fn [f] + (binding [*hz* frequency] + (let [start (clock)] + (f) + (let [delta (- (clock) start) + sleep (max 0 (- (/ 1 @*hz*) delta))] + (when-not (zero? sleep) + (Thread/sleep (long (* sleep 1e3)) (long (rem (* sleep 1e6) 1e6)))))))))) + +(defn timed-fn [f] + (let [last-time (atom (clock))] + (fn [& args] + (let [current-time (clock)] + (try + (if f + (apply f (list* [(- current-time @last-time) current-time] args)) + @(:state *app*)) + (finally + (reset! last-time current-time))))))) + +(defn update-loop + [latch-fn complete-fn inner-loop] + (loop [] + (when-let [latch (latch-fn)] + (.await #^CountDownLatch latch)) + (inner-loop) + (when-not (complete-fn) + (recur)))) + +(defn secondary-loop [app hz f] + (let [wrapper (periodic-fn hz)] + (-> + (Thread. + (fn [] + (with-app app + (binding [*async-running?* (atom true)] + (let [s (slate/create (:window app))] + (try + (Thread/sleep (/ 1000 hz)) + (update-loop + #(deref (:latch app)) + #(or (not @*async-running?*) @(:stopped? app)) + #(wrapper f)) + (finally + (slate/destroy s)))))))) + .start))) + +(defn primary-loop [app f] + (let [wrapper (periodic-fn 200)] + (with-app app + (update-loop + (constantly nil) + #(or @(:paused? app) @(:stopped? app)) + #(wrapper f))))) \ No newline at end of file diff --git a/src/penumbra/window.clj b/src/penumbra/app/window.clj similarity index 61% rename from src/penumbra/window.clj rename to src/penumbra/app/window.clj index d53f967..25ed0a0 100644 --- a/src/penumbra/window.clj +++ b/src/penumbra/app/window.clj @@ -6,12 +6,12 @@ ;; the terms of this license. ;; You must not remove this notice, or any other, from this software. -(ns penumbra.window - (:use [clojure.contrib.def :only (defvar)]) - (:use [clojure.contrib.core :only (-?>)]) +(ns penumbra.app.window + (:use [penumbra.app.core]) (:use [penumbra.opengl]) (:use [penumbra.opengl.texture :only (create-texture-pool)]) (:use [penumbra.opengl.core :only (*texture-pool*)]) + (:use [clojure.contrib.core :only (-?>)]) (:require [penumbra.slate :as slate]) (:require [penumbra.opengl.texture :as texture]) (:require [penumbra.text :as text]) @@ -27,65 +27,63 @@ :frame :size) -(defvar *window* nil - "Current window.") - -(defvar *callback-handler* nil) - ;;Display Mode -(defn- transform-display-mode [m] +(defn transform-display-mode [m] {:resolution [(.getWidth m) (.getHeight m)] :bpp (.getBitsPerPixel m) :fullscreen (.isFullscreenCapable m) :mode m}) -(defn display-modes - "Returns a list of available display modes." - [] - (map transform-display-mode (Display/getAvailableDisplayModes))) - -(defn current-display-mode - "Returns the current display mode." - [] - (transform-display-mode (Display/getDisplayMode))) - -(defn set-display-mode - "Sets the current display mode." - ([width height] - (->> - (display-modes) - (filter #(= [width height] (:resolution %))) - (sort-by :bpp) - last - set-display-mode)) - ([mode] - (Display/setDisplayMode (:mode mode)) - (apply viewport (concat [0 0] (:resolution mode))))) - -(defn dimensions - "Returns dimensions of window as [width height]." - ([] - (dimensions *window*)) - ([window] - (if-let [canvas (-?> window :frame deref (.getComponent 1))] - [(.getWidth canvas) (.getHeight canvas)] - (:resolution (current-display-mode))))) - -(defn vsync [enabled] - (Display/setVSyncEnabled enabled) - (reset! (:vsync? *window*) enabled)) +(defn dimensions [w] + (if-let [canvas (-?> w :frame deref (.getComponent 0))] + [(.getWidth canvas) (.getHeight canvas)] + (:resolution (transform-display-mode (Display/getDisplayMode))))) (defn check-for-resize ([] (check-for-resize *window*)) ([window] - (let [dim (dimensions window)] + (let [[w h :as dim] (dimensions window)] (when (not= @(:size window) dim) (reset! (:size window) dim) + (viewport 0 0 w h) (*callback-handler* :reshape (concat [0 0] dim)))))) -;; +;;Frame + +(defn enable-resizable [app] + (when (nil? @(-> app :window :frame)) + (let [window (:window app) + frame (Frame.) + canvas (Canvas.) + [w h] @(:size window)] + (doto canvas + (.setFocusable true) + (.setIgnoreRepaint true) + (.setSize w h)) + (doto frame + (.addWindowListener + (proxy [WindowAdapter] [] + (windowOpened [event] (.requestFocus canvas)) + (windowClosing [event] (reset! (:stopped? app) true)))) + (.setTitle (Display/getTitle)) + (.setIgnoreRepaint true) + (.setResizable true) + (.setVisible true) + (.add canvas) + (.pack)) + (Display/setParent canvas) + (reset! (:frame window) frame)))) + +(defn disable-resizable [app] + (let [window (:window app)] + (when-let [frame @(:frame window)] + (.dispose frame) + (Display/setParent nil) + (reset! (:frame window) nil)))) + +;;; (defn create [] (struct-map window-struct @@ -93,17 +91,17 @@ :texture-pool (atom (create-texture-pool)) :font-cache (atom {}) :frame (atom nil) - :size (atom [1024 768]) + :size (atom (dimensions nil)) :vsync? (atom false))) (defn init ([] (init *window*)) ([window] - (Display/create (-> (PixelFormat.) (.withSamples 4))) - (blend-func :src-alpha :one-minus-src-alpha) (Display/setParent nil) - (apply set-display-mode @(:size window)) + (Display/create (-> (PixelFormat.) (.withSamples 1))) + (blend-func :src-alpha :one-minus-src-alpha) + (apply viewport @(:size window)) (*callback-handler* :reshape (concat [0 0] @(:size window))))) (defn destroy @@ -122,42 +120,3 @@ text/*font-cache* (:font-cache ~window)] ~@body)) -;;Frame - -(defn- enable-resizable [window] - (when (nil? @(:frame window)) - (let [frame (Frame.) - canvas (Canvas.) - [w h] @(:size window)] - (doto canvas - (.setFocusable true) - (.setIgnoreRepaint true) - (.setSize w h)) - (doto frame - (.addWindowListener - (proxy [WindowAdapter] [] - (windowOpened [event] (.requestFocus canvas)) - (windowClosing [event] (reset! (:disposed window) true)))) - (.setTitle (Display/getTitle)) - (.setIgnoreRepaint true) - (.setResizable true) - (.setVisible true) - (.add canvas) - (.pack)) - (Display/setParent canvas) - (reset! (:frame window) frame)))) - -(defn- disable-resizable [window] - (when-let [frame @(:frame window)] - (.dispose frame) - (Display/setParent nil) - (reset! (:frame window) nil))) - -(defn resizable - ([enabled] - (resizable *window* enabled)) - ([window enabled] - (if enabled - (enable-resizable window) - (disable-resizable window)))) - diff --git a/src/penumbra/opengl.clj b/src/penumbra/opengl.clj index b9c105b..708f250 100644 --- a/src/penumbra/opengl.clj +++ b/src/penumbra/opengl.clj @@ -9,7 +9,6 @@ (ns penumbra.opengl (:use [clojure.contrib.def :only (defn-memo)]) (:use [clojure.contrib.seq-utils :only (indexed)]) - (:use [penumbra.text]) (:use [penumbra.opengl core geometry shader texture]) (:use [penumbra.glsl core]) (:use [penumbra.geometry]) @@ -17,7 +16,7 @@ (:import (java.awt Font)) (:import (java.nio ByteBuffer IntBuffer FloatBuffer)) (:import (java.io File)) - (:import (org.newdawn.slick.opengl InternalTextureLoader Texture TextureImpl))) + (:import (org.newdawn.slick.opengl InternalTextureLoader Texture))) ;;; @@ -670,20 +669,4 @@ (attach-textures [] [~tex]) (clear) (push-matrix - ~@body))))) - -;;;;;;;;;;;;;;;;;;;;;; - - -(defn write-to-screen - "writes string at pixel coordinates (x, y)" - [string x y] - (with-font (font "Tahoma" :size 20) - (with-disabled :lighting - (with-enabled [:texture-2d :blend] - (let [[x y w h] @*view*] - (with-projection (ortho-view x (+ x w) (+ y h) y -1 1) - (push-matrix - (load-identity) - (TextureImpl/bindNone) - (.drawString *font* x y string)))))))) \ No newline at end of file + ~@body))))) \ No newline at end of file diff --git a/src/penumbra/text.clj b/src/penumbra/text.clj index 3177c50..ff69444 100644 --- a/src/penumbra/text.clj +++ b/src/penumbra/text.clj @@ -1,8 +1,11 @@ (ns penumbra.text + (:use [penumbra.opengl]) + (:use [penumbra.opengl.core :only [*view*]]) (:use [clojure.contrib.def :only (defvar defn-memo)]) (:import [java.awt Font]) (:import [java.awt.font TextAttribute]) - (:import [org.newdawn.slick TrueTypeFont])) + (:import [org.newdawn.slick TrueTypeFont]) + (:import [org.newdawn.slick.opengl TextureImpl])) (defvar *font-cache* nil "Where all the fonts are kept") @@ -29,5 +32,18 @@ `(binding [*font* ~f] ~@body)) +(defn write-to-screen + "writes string at pixel coordinates (x, y)" + [string x y] + (with-font (font "Tahoma" :size 20) + (with-disabled :lighting + (with-enabled [:texture-2d :blend] + (let [[x y w h] @*view*] + (with-projection (ortho-view x (+ x w) (+ y h) y -1 1) + (push-matrix + (load-identity) + (TextureImpl/bindNone) + (.drawString *font* x y string)))))))) +