comparison org/test-creature.org @ 151:aaacf087504c

refactored vision code
author Robert McIntyre <rlm@mit.edu>
date Fri, 03 Feb 2012 05:52:18 -0700
parents 1e6beed24cec
children c95179907951
comparison
equal deleted inserted replaced
150:e1232043656a 151:aaacf087504c
145 {:author "Robert McIntyre"}) 145 {:author "Robert McIntyre"})
146 146
147 ;; TODO remove this! 147 ;; TODO remove this!
148 (require 'cortex.import) 148 (require 'cortex.import)
149 (cortex.import/mega-import-jme3) 149 (cortex.import/mega-import-jme3)
150 (use '(cortex world util body hearing touch vision)) 150 (use '(cortex world util body hearing touch vision sense))
151 151
152 (rlm.rlm-commands/help) 152 (rlm.rlm-commands/help)
153 (import java.awt.image.BufferedImage) 153 (import java.awt.image.BufferedImage)
154 (import javax.swing.JPanel) 154 (import javax.swing.JPanel)
155 (import javax.swing.SwingUtilities) 155 (import javax.swing.SwingUtilities)
158 (import java.awt.Dimension) 158 (import java.awt.Dimension)
159 (import com.aurellem.capture.RatchetTimer) 159 (import com.aurellem.capture.RatchetTimer)
160 (declare joint-create) 160 (declare joint-create)
161 (use 'clojure.contrib.def) 161 (use 'clojure.contrib.def)
162 162
163 (defn points->image
164 "Take a sparse collection of points and visuliaze it as a
165 BufferedImage."
166
167 ;; TODO maybe parallelize this since it's easy
168
169 [points]
170 (if (empty? points)
171 (BufferedImage. 1 1 BufferedImage/TYPE_BYTE_BINARY)
172 (let [xs (vec (map first points))
173 ys (vec (map second points))
174 x0 (apply min xs)
175 y0 (apply min ys)
176 width (- (apply max xs) x0)
177 height (- (apply max ys) y0)
178 image (BufferedImage. (inc width) (inc height)
179 BufferedImage/TYPE_INT_RGB)]
180 (dorun
181 (for [x (range (.getWidth image))
182 y (range (.getHeight image))]
183 (.setRGB image x y 0xFF0000)))
184 (dorun
185 (for [index (range (count points))]
186 (.setRGB image (- (xs index) x0) (- (ys index) y0) -1)))
187
188 image)))
189
190 (defn average [coll]
191 (/ (reduce + coll) (count coll)))
192
193 (defn collapse-1d
194 "One dimensional analogue of collapse"
195 [center line]
196 (let [length (count line)
197 num-above (count (filter (partial < center) line))
198 num-below (- length num-above)]
199 (range (- center num-below)
200 (+ center num-above))))
201
202 (defn collapse
203 "Take a set of pairs of integers and collapse them into a
204 contigous bitmap."
205 [points]
206 (if (empty? points) []
207 (let
208 [num-points (count points)
209 center (vector
210 (int (average (map first points)))
211 (int (average (map first points))))
212 flattened
213 (reduce
214 concat
215 (map
216 (fn [column]
217 (map vector
218 (map first column)
219 (collapse-1d (second center)
220 (map second column))))
221 (partition-by first (sort-by first points))))
222 squeezed
223 (reduce
224 concat
225 (map
226 (fn [row]
227 (map vector
228 (collapse-1d (first center)
229 (map first row))
230 (map second row)))
231 (partition-by second (sort-by second flattened))))
232 relocate
233 (let [min-x (apply min (map first squeezed))
234 min-y (apply min (map second squeezed))]
235 (map (fn [[x y]]
236 [(- x min-x)
237 (- y min-y)])
238 squeezed))]
239 relocate)))
240 163
241 (defn load-bullet [] 164 (defn load-bullet []
242 (let [sim (world (Node.) {} no-op no-op)] 165 (let [sim (world (Node.) {} no-op no-op)]
243 (doto sim 166 (doto sim
244 (.enqueue 167 (.enqueue
251 [^String model] 174 [^String model]
252 (.loadModel 175 (.loadModel
253 (doto (asset-manager) 176 (doto (asset-manager)
254 (.registerLoader BlenderModelLoader (into-array String ["blend"]))) 177 (.registerLoader BlenderModelLoader (into-array String ["blend"])))
255 model)) 178 model))
256
257 (defn meta-data [blender-node key]
258 (if-let [data (.getUserData blender-node "properties")]
259 (.findValue data key)
260 nil))
261 179
262 (defn blender-to-jme 180 (defn blender-to-jme
263 "Convert from Blender coordinates to JME coordinates" 181 "Convert from Blender coordinates to JME coordinates"
264 [#^Vector3f in] 182 [#^Vector3f in]
265 (Vector3f. (.getX in) 183 (Vector3f. (.getX in)
472 (.loadTexture 390 (.loadTexture
473 (asset-manager) 391 (asset-manager)
474 image-path)) 392 image-path))
475 false false 0))) 393 false false 0)))
476 394
477 (import ij.process.ImageProcessor) 395
478 (import java.awt.image.BufferedImage)
479
480 (def white -1)
481
482 (defn filter-pixels
483 "List the coordinates of all pixels matching pred, within the bounds
484 provided. Bounds -> [x0 y0 width height]"
485 {:author "Dylan Holmes"}
486 ([pred #^BufferedImage image]
487 (filter-pixels pred image [0 0 (.getWidth image) (.getHeight image)]))
488 ([pred #^BufferedImage image [x0 y0 width height]]
489 ((fn accumulate [x y matches]
490 (cond
491 (>= y (+ height y0)) matches
492 (>= x (+ width x0)) (recur 0 (inc y) matches)
493 (pred (.getRGB image x y))
494 (recur (inc x) y (conj matches [x y]))
495 :else (recur (inc x) y matches)))
496 x0 y0 [])))
497
498 (defn white-coordinates
499 "Coordinates of all the white pixels in a subset of the image."
500 ([#^BufferedImage image bounds]
501 (filter-pixels #(= % white) image bounds))
502 ([#^BufferedImage image]
503 (filter-pixels #(= % white) image)))
504 396
505 (defn triangle 397 (defn triangle
506 "Get the triangle specified by triangle-index from the mesh within 398 "Get the triangle specified by triangle-index from the mesh within
507 bounds." 399 bounds."
508 [#^Mesh mesh triangle-index] 400 [#^Mesh mesh triangle-index]
716 (map enable-touch 608 (map enable-touch
717 (filter #(isa? (class %) Geometry) 609 (filter #(isa? (class %) Geometry)
718 (node-seq pieces))))) 610 (node-seq pieces)))))
719 611
720 612
721 ;; human eye transmits 62kb/s to brain Bandwidth is 8.75 Mb/s
722 ;; http://en.wikipedia.org/wiki/Retina
723
724 (defn test-eye [] 613 (defn test-eye []
725 (.getChild 614 (.getChild
726 (.getChild (worm-model) "eyes") 615 (.getChild (worm-model) "eyes")
727 "eye")) 616 "eye"))
728 617
729 618
730 (defn retina-sensor-image
731 "Return a map of pixel selection functions to BufferedImages
732 describing the distribution of light-sensitive components on this
733 geometry's surface. Each function creates an integer from the rgb
734 values found in the pixel. :red, :green, :blue, :gray are already
735 defined as extracting the red green blue and average components
736 respectively."
737 [#^Spatial eye]
738 (if-let [eye-map (meta-data eye "eye")]
739 (map-vals
740 #(ImageToAwt/convert
741 (.getImage (.loadTexture (asset-manager) %))
742 false false 0)
743 (eval (read-string eye-map)))))
744
745 (defn eye-dimensions
746 "returns the width and height specified in the metadata of the eye"
747 [#^Spatial eye]
748 (let [dimensions
749 (map #(vector (.getWidth %) (.getHeight %))
750 (vals (retina-sensor-image eye)))]
751 [(apply max (map first dimensions))
752 (apply max (map second dimensions))]))
753
754 (defn creature-eyes
755 ;;dylan
756 "Return the children of the creature's \"eyes\" node."
757 ;;"The eye nodes which are children of the \"eyes\" node in the
758 ;;creature."
759 [#^Node creature]
760 (if-let [eye-node (.getChild creature "eyes")]
761 (seq (.getChildren eye-node))
762 (do (println-repl "could not find eyes node") [])))
763
764 ;; Here's how vision will work.
765
766 ;; Make the continuation in scene-processor take FrameBuffer,
767 ;; byte-buffer, BufferedImage already sized to the correct
768 ;; dimensions. the continuation will decide wether to "mix" them
769 ;; into the BufferedImage, lazily ignore them, or mix them halfway
770 ;; and call c/graphics card routines.
771
772 ;; (vision creature) will take an optional :skip argument which will
773 ;; inform the continuations in scene processor to skip the given
774 ;; number of cycles; 0 means that no cycles will be skipped.
775
776 ;; (vision creature) will return [init-functions sensor-functions].
777 ;; The init-functions are each single-arg functions that take the
778 ;; world and register the cameras and must each be called before the
779 ;; corresponding sensor-functions. Each init-function returns the
780 ;; viewport for that eye which can be manipulated, saved, etc. Each
781 ;; sensor-function is a thunk and will return data in the same
782 ;; format as the tactile-sensor functions; the structure is
783 ;; [topology, sensor-data]. Internally, these sensor-functions
784 ;; maintain a reference to sensor-data which is periodically updated
785 ;; by the continuation function established by its init-function.
786 ;; They can be queried every cycle, but their information may not
787 ;; necessairly be different every cycle.
788
789 ;; Each eye in the creature in blender will work the same way as
790 ;; joints -- a zero dimensional object with no geometry whose local
791 ;; coordinate system determines the orientation of the resulting
792 ;; eye. All eyes will have a parent named "eyes" just as all joints
793 ;; have a parent named "joints". The resulting camera will be a
794 ;; ChaseCamera or a CameraNode bound to the geo that is closest to
795 ;; the eye marker. The eye marker will contain the metadata for the
796 ;; eye, and will be moved by it's bound geometry. The dimensions of
797 ;; the eye's camera are equal to the dimensions of the eye's "UV"
798 ;; map.
799
800
801 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
802 619
803 ;; Ears work the same way as vision. 620 ;; Ears work the same way as vision.
804 621
805 ;; (hearing creature) will return [init-functions 622 ;; (hearing creature) will return [init-functions
806 ;; sensor-functions]. The init functions each take the world and 623 ;; sensor-functions]. The init functions each take the world and
815 [#^Node creature] 632 [#^Node creature]
816 (if-let [ear-node (.getChild creature "ears")] 633 (if-let [ear-node (.getChild creature "ears")]
817 (seq (.getChildren ear-node)) 634 (seq (.getChildren ear-node))
818 (do (println-repl "could not find ears node") []))) 635 (do (println-repl "could not find ears node") [])))
819 636
820 (defn closest-node
821 "Return the object in creature which is closest to the given node."
822 ;;dylan"The closest object in creature to the given node."
823 [#^Node creature #^Node eye]
824 (loop [radius (float 0.01)]
825 (let [results (CollisionResults.)]
826 (.collideWith
827 creature
828 (BoundingBox. (.getWorldTranslation eye)
829 radius radius radius)
830 results)
831 (if-let [target (first results)]
832 (.getGeometry target)
833 (recur (float (* 2 radius)))))))
834 637
835 ;;dylan (defn follow-sense, adjoin-sense, attach-stimuli, 638 ;;dylan (defn follow-sense, adjoin-sense, attach-stimuli,
836 ;;anchor-qualia, augment-organ, with-organ 639 ;;anchor-qualia, augment-organ, with-organ
837 (defn bind-sense
838 "Bind the sense to the Spatial such that it will maintain its
839 current position relative to the Spatial no matter how the spatial
840 moves. 'sense can be either a Camera or Listener object."
841 [#^Spatial obj sense]
842 (let [sense-offset (.subtract (.getLocation sense)
843 (.getWorldTranslation obj))
844 initial-sense-rotation (Quaternion. (.getRotation sense))
845 base-anti-rotation (.inverse (.getWorldRotation obj))]
846 (.addControl
847 obj
848 (proxy [AbstractControl] []
849 (controlUpdate [tpf]
850 (let [total-rotation
851 (.mult base-anti-rotation (.getWorldRotation obj))]
852 (.setLocation sense
853 (.add
854 (.mult total-rotation sense-offset)
855 (.getWorldTranslation obj)))
856 (.setRotation sense
857 (.mult total-rotation initial-sense-rotation))))
858 (controlRender [_ _])))))
859 640
860 641
861 (defn update-listener-velocity 642 (defn update-listener-velocity
862 "Update the listener's velocity every update loop." 643 "Update the listener's velocity every update loop."
863 [#^Spatial obj #^Listener lis] 644 [#^Spatial obj #^Listener lis]
917 (into senses-a senses-b)]) 698 (into senses-a senses-b)])
918 [[][]] 699 [[][]]
919 (for [ear (creature-ears creature)] 700 (for [ear (creature-ears creature)]
920 (enable-hearing creature ear)))) 701 (enable-hearing creature ear))))
921 702
922 (defn attach-eye
923 "Attach a Camera to the appropiate area and return the Camera."
924 [#^Node creature #^Spatial eye]
925 (let [target (closest-node creature eye)
926 [cam-width cam-height] (eye-dimensions eye)
927 cam (Camera. cam-width cam-height)]
928 (.setLocation cam (.getWorldTranslation eye))
929 (.setRotation cam (.getWorldRotation eye))
930 (.setFrustumPerspective
931 cam 45 (/ (.getWidth cam) (.getHeight cam))
932 1 1000)
933 (bind-sense target cam)
934 cam))
935
936 (def presets
937 {:all 0xFFFFFF
938 :red 0xFF0000
939 :blue 0x0000FF
940 :green 0x00FF00})
941
942 (defn enable-vision
943 "return [init-function sensor-functions] for a particular eye"
944 [#^Node creature #^Spatial eye & {skip :skip :or {skip 0}}]
945 (let [retinal-map (retina-sensor-image eye)
946 camera (attach-eye creature eye)
947 vision-image
948 (atom
949 (BufferedImage. (.getWidth camera)
950 (.getHeight camera)
951 BufferedImage/TYPE_BYTE_BINARY))]
952 [(fn [world]
953 (add-eye
954 world camera
955 (let [counter (atom 0)]
956 (fn [r fb bb bi]
957 (if (zero? (rem (swap! counter inc) (inc skip)))
958 (reset! vision-image (BufferedImage! r fb bb bi)))))))
959 (vec
960 (map
961 (fn [[key image]]
962 (let [whites (white-coordinates image)
963 topology (vec (collapse whites))
964 mask (presets key)]
965 (fn []
966 (vector
967 topology
968 (vec
969 (for [[x y] whites]
970 (bit-and
971 mask (.getRGB @vision-image x y))))))))
972 retinal-map))]))
973
974 (defn vision
975 [#^Node creature & {skip :skip :or {skip 0}}]
976 (reduce
977 (fn [[init-a senses-a]
978 [init-b senses-b]]
979 [(conj init-a init-b)
980 (into senses-a senses-b)])
981 [[][]]
982 (for [eye (creature-eyes creature)]
983 (enable-vision creature eye))))
984 703
985 704
986 705
987 706
988 707