Mercurial > cortex
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 |