view org/skin.org @ 71:a1e421d9c485

improved test suite
author Robert McIntyre <rlm@mit.edu>
date Sun, 11 Dec 2011 22:32:28 -0700
parents 39e4e1542e4a
children e8df6e76c3e5
line wrap: on
line source
1 #+title: Simulated Sense of Touch
2 #+author: Robert McIntyre
3 #+email: rlm@mit.edu
4 #+description: Simulated touch for AI research using JMonkeyEngine and clojure.
5 #+keywords: simulation, tactile sense, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org
10 * Touch
12 My creatures need to be able to feel their environments. The idea here
13 is to create thousands of small /touch receptors/ along the geometries
14 which make up the creature's body. The number of touch receptors in a
15 given area is determined by how complicated that area is, as
16 determined by the total number of triangles in that region. This way,
17 complicated regions like the hands/face, etc. get more touch receptors
18 than simpler areas of the body.
20 #+name: skin-main
21 #+begin_src clojure
22 (ns cortex.touch
23 "Simulate the sense of touch in jMonkeyEngine3. Enables any Geometry
24 to be outfitted with touch sensors with density proportional to the
25 density of triangles along the surface of the Geometry. Enables a
26 Geometry to know what parts of itself are touching nearby objects."
27 {:author "Robert McIntyre"}
28 (:use (cortex world util))
29 (:import com.jme3.scene.Geometry)
30 (:import com.jme3.collision.CollisionResults)
31 (:import (com.jme3.math Triangle Vector3f Ray)))
33 (defn triangles
34 "Return a sequence of all the Triangles which compose a given
35 Geometry."
36 [#^Geometry geom]
37 (let
38 [mesh (.getMesh geom)
39 triangles (transient [])]
40 (dorun
41 (for [n (range (.getTriangleCount mesh))]
42 (let [tri (Triangle.)]
43 (.getTriangle mesh n tri)
44 ;; (.calculateNormal tri)
45 ;; (.calculateCenter tri)
46 (conj! triangles tri))))
47 (persistent! triangles)))
49 (defn get-ray-origin
50 "Return the origin which a Ray would have to have to be in the exact
51 center of a particular Triangle in the Geometry in World
52 Coordinates."
53 [geom tri]
54 (let [new (Vector3f.)]
55 (.calculateCenter tri)
56 (.localToWorld geom (.getCenter tri) new) new))
58 (defn get-ray-direction
59 "Return the direction which a Ray would have to have to be in the
60 exact center of a particular Triangle in the Geometry, pointing
61 normal to the Triangle, in coordinates relative to the center of the
62 Triangle."
63 [geom tri]
64 (let [n+c (Vector3f.)]
65 (.calculateNormal tri)
66 (.calculateCenter tri)
67 (.localToWorld
68 geom
69 (.add (.getCenter tri) (.getNormal tri)) n+c)
70 (.subtract n+c (get-ray-origin geom tri))))
72 (defn normal-rays
73 "For each Triangle which comprises the Geometry, returns a Ray which
74 is centered on that Triangle, points outward in a normal direction,
75 and extends for =limit= distance."
76 [limit #^Geometry geom]
77 (vec
78 (map
79 (fn [tri]
80 (doto
81 (Ray. (get-ray-origin geom tri)
82 (get-ray-direction geom tri))
83 (.setLimit limit)))
84 (triangles geom))))
86 (defn touch-percieve
87 "Augment a Geometry with the sense of touch. Returns a sequence of
88 non-negative integers, one for each triangle, with the value of the
89 integer describing how many objects a ray of length =limit=, normal
90 to the triangle and originating from its center, encountered. The
91 Geometry itself is not counted among the results."
92 [limit geom node]
93 (let [normals (normal-rays limit geom)]
94 (doall
95 (for [ray normals]
96 (do
97 (let [results (CollisionResults.)]
98 (.collideWith node ray results)
99 (let [touch-objects
100 (set (filter #(not (= geom %))
101 (map #(.getGeometry %) results)))]
102 (count touch-objects))))))))
103 #+end_src
106 * Example
108 #+name: touch-test
109 #+begin_src clojure
110 (ns cortex.test.touch
111 (:use (cortex world util touch))
112 (:import
113 com.jme3.scene.shape.Sphere
114 com.jme3.math.ColorRGBA
115 com.jme3.math.Vector3f
116 com.jme3.material.RenderState$BlendMode
117 com.jme3.renderer.queue.RenderQueue$Bucket
118 com.jme3.scene.shape.Box
119 com.jme3.scene.Node))
121 (defn ray-origin-debug
122 [ray color]
123 (make-shape
124 (assoc base-shape
125 :shape (Sphere. 5 5 0.05)
126 :name "arrow"
127 :color color
128 :texture false
129 :physical? false
130 :position
131 (.getOrigin ray))))
133 (defn ray-debug [ray color]
134 (make-shape
135 (assoc
136 base-shape
137 :name "debug-ray"
138 :physical? false
139 :shape (com.jme3.scene.shape.Line.
140 (.getOrigin ray)
141 (.add
142 (.getOrigin ray)
143 (.mult (.getDirection ray)
144 (float (.getLimit ray))))))))
147 (defn contact-color [contacts]
148 (case contacts
149 0 ColorRGBA/Gray
150 1 ColorRGBA/Red
151 2 ColorRGBA/Green
152 3 ColorRGBA/Yellow
153 4 ColorRGBA/Orange
154 5 ColorRGBA/Red
155 6 ColorRGBA/Magenta
156 7 ColorRGBA/Pink
157 8 ColorRGBA/White))
159 (defn update-ray-debug [node ray contacts]
160 (let [origin (.getChild node 0)]
161 (.setLocalTranslation origin (.getOrigin ray))
162 (.setColor (.getMaterial origin) "Color" (contact-color contacts))))
164 (defn init-node
165 [debug-node rays]
166 (.detachAllChildren debug-node)
167 (dorun
168 (for [ray rays]
169 (do
170 (.attachChild
171 debug-node
172 (doto (Node.)
173 (.attachChild (ray-origin-debug ray ColorRGBA/Gray))
174 (.attachChild (ray-debug ray ColorRGBA/Gray))
175 ))))))
177 (defn manage-ray-debug-node [debug-node geom touch-data limit]
178 (let [rays (normal-rays limit geom)]
179 (if (not= (count (.getChildren debug-node)) (count touch-data))
180 (init-node debug-node rays))
181 (dorun
182 (for [n (range (count touch-data))]
183 (update-ray-debug
184 (.getChild debug-node n) (nth rays n) (nth touch-data n))))))
186 (defn transparent-sphere []
187 (doto
188 (make-shape
189 (merge base-shape
190 {:position (Vector3f. 0 2 0)
191 :name "the blob."
192 :material "Common/MatDefs/Misc/Unshaded.j3md"
193 :texture "Textures/purpleWisp.png"
194 :physical? true
195 :mass 70
196 :color ColorRGBA/Blue
197 :shape (Sphere. 10 10 1)}))
198 (-> (.getMaterial)
199 (.getAdditionalRenderState)
200 (.setBlendMode RenderState$BlendMode/Alpha))
201 (.setQueueBucket RenderQueue$Bucket/Transparent)))
203 (defn transparent-box []
204 (doto
205 (make-shape
206 (merge base-shape
207 {:position (Vector3f. 0 2 0)
208 :name "box"
209 :material "Common/MatDefs/Misc/Unshaded.j3md"
210 :texture "Textures/purpleWisp.png"
211 :physical? true
212 :mass 70
213 :color ColorRGBA/Blue
214 :shape (Box. 1 1 1)}))
215 (-> (.getMaterial)
216 (.getAdditionalRenderState)
217 (.setBlendMode RenderState$BlendMode/Alpha))
218 (.setQueueBucket RenderQueue$Bucket/Transparent)))
220 (defn transparent-floor []
221 (doto
222 (box 5 0.2 5 :mass 0 :position (Vector3f. 0 -2 0)
223 :material "Common/MatDefs/Misc/Unshaded.j3md"
224 :texture "Textures/redWisp.png"
225 :name "floor")
226 (-> (.getMaterial)
227 (.getAdditionalRenderState)
228 (.setBlendMode RenderState$BlendMode/Alpha))
229 (.setQueueBucket RenderQueue$Bucket/Transparent)))
231 (defn test-skin
232 "Testing touch:
233 you should see a ball which responds to the table
234 and whatever balls hit it."
235 []
236 (let [b
237 ;;(transparent-box)
238 (transparent-sphere)
239 ;;(sphere)
240 f (transparent-floor)
241 debug-node (Node.)
242 node (doto (Node.) (.attachChild b) (.attachChild f))
243 root-node (doto (Node.) (.attachChild node)
244 (.attachChild debug-node))
245 ]
247 (world
248 root-node
249 {"key-return" (fire-cannon-ball node)}
250 (fn [world]
251 ;; (Capture/SimpleCaptureVideo
252 ;; world
253 ;; (file-str "/home/r/proj/cortex/tmp/blob.avi"))
254 ;; (no-logging)
255 ;;(enable-debug world)
256 ;; (set-accuracy world (/ 1 60))
257 )
259 (fn [& _]
260 (let [sensitivity 0.2
261 touch-data (touch-percieve sensitivity b node)]
262 (manage-ray-debug-node debug-node b touch-data sensitivity))
263 ))))
266 #+end_src
272 * COMMENT code generation
273 #+begin_src clojure :tangle ../src/cortex/touch.clj
274 <<skin-main>>
275 #+end_src
277 #+begin_src clojure :tangle ../src/cortex/test/touch.clj
278 <<touch-test>>
279 #+end_src