comparison org/touch.org @ 177:5af4ebe72b97

renamed skin.org to touch.org
author Robert McIntyre <rlm@mit.edu>
date Sat, 04 Feb 2012 06:54:14 -0700
parents org/skin.org@e8df6e76c3e5
children 6fba17a74a57
comparison
equal deleted inserted replaced
176:026f69582022 177:5af4ebe72b97
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
8
9
10 * Touch
11
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.
19
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 sense))
29 (:import com.jme3.scene.Geometry)
30 (:import com.jme3.collision.CollisionResults)
31 (:import jme3tools.converters.ImageToAwt)
32 (:import (com.jme3.math Triangle Vector3f Ray)))
33
34 (use 'clojure.contrib.def)
35 (cortex.import/mega-import-jme3)
36
37 (defn triangles
38 "Return a sequence of all the Triangles which compose a given
39 Geometry."
40 [#^Geometry geom]
41 (let
42 [mesh (.getMesh geom)
43 triangles (transient [])]
44 (dorun
45 (for [n (range (.getTriangleCount mesh))]
46 (let [tri (Triangle.)]
47 (.getTriangle mesh n tri)
48 ;; (.calculateNormal tri)
49 ;; (.calculateCenter tri)
50 (conj! triangles tri))))
51 (persistent! triangles)))
52
53 (defn get-ray-origin
54 "Return the origin which a Ray would have to have to be in the exact
55 center of a particular Triangle in the Geometry in World
56 Coordinates."
57 [geom tri]
58 (let [new (Vector3f.)]
59 (.calculateCenter tri)
60 (.localToWorld geom (.getCenter tri) new) new))
61
62 (defn get-ray-direction
63 "Return the direction which a Ray would have to have to be to point
64 normal to the Triangle, in coordinates relative to the center of the
65 Triangle."
66 [geom tri]
67 (let [n+c (Vector3f.)]
68 (.calculateNormal tri)
69 (.calculateCenter tri)
70 (.localToWorld
71 geom
72 (.add (.getCenter tri) (.getNormal tri)) n+c)
73 (.subtract n+c (get-ray-origin geom tri))))
74
75 ;; Every Mesh has many triangles, each with its own index.
76 ;; Every vertex has its own index as well.
77
78 (defn tactile-sensor-image
79 "Return the touch-sensor distribution image in BufferedImage format,
80 or nil if it does not exist."
81 [#^Geometry obj]
82 (if-let [image-path (meta-data obj "touch")]
83 (ImageToAwt/convert
84 (.getImage
85 (.loadTexture
86 (asset-manager)
87 image-path))
88 false false 0)))
89
90
91
92 (defn triangle
93 "Get the triangle specified by triangle-index from the mesh within
94 bounds."
95 [#^Mesh mesh triangle-index]
96 (let [scratch (Triangle.)]
97 (.getTriangle mesh triangle-index scratch)
98 scratch))
99
100 (defn triangle-vertex-indices
101 "Get the triangle vertex indices of a given triangle from a given
102 mesh."
103 [#^Mesh mesh triangle-index]
104 (let [indices (int-array 3)]
105 (.getTriangle mesh triangle-index indices)
106 (vec indices)))
107
108 (defn vertex-UV-coord
109 "Get the uv-coordinates of the vertex named by vertex-index"
110 [#^Mesh mesh vertex-index]
111 (let [UV-buffer
112 (.getData
113 (.getBuffer
114 mesh
115 VertexBuffer$Type/TexCoord))]
116 [(.get UV-buffer (* vertex-index 2))
117 (.get UV-buffer (+ 1 (* vertex-index 2)))]))
118
119 (defn triangle-UV-coord
120 "Get the uv-cooridnates of the triangle's verticies."
121 [#^Mesh mesh width height triangle-index]
122 (map (fn [[u v]] (vector (* width u) (* height v)))
123 (map (partial vertex-UV-coord mesh)
124 (triangle-vertex-indices mesh triangle-index))))
125
126 (defn same-side?
127 "Given the points p1 and p2 and the reference point ref, is point p
128 on the same side of the line that goes through p1 and p2 as ref is?"
129 [p1 p2 ref p]
130 (<=
131 0
132 (.dot
133 (.cross (.subtract p2 p1) (.subtract p p1))
134 (.cross (.subtract p2 p1) (.subtract ref p1)))))
135
136 (defn triangle-seq [#^Triangle tri]
137 [(.get1 tri) (.get2 tri) (.get3 tri)])
138
139 (defn vector3f-seq [#^Vector3f v]
140 [(.getX v) (.getY v) (.getZ v)])
141
142 (defn inside-triangle?
143 "Is the point inside the triangle?"
144 {:author "Dylan Holmes"}
145 [#^Triangle tri #^Vector3f p]
146 (let [[vert-1 vert-2 vert-3] (triangle-seq tri)]
147 (and
148 (same-side? vert-1 vert-2 vert-3 p)
149 (same-side? vert-2 vert-3 vert-1 p)
150 (same-side? vert-3 vert-1 vert-2 p))))
151
152 (defn triangle->matrix4f
153 "Converts the triangle into a 4x4 matrix: The first three columns
154 contain the vertices of the triangle; the last contains the unit
155 normal of the triangle. The bottom row is filled with 1s."
156 [#^Triangle t]
157 (let [mat (Matrix4f.)
158 [vert-1 vert-2 vert-3]
159 ((comp vec map) #(.get t %) (range 3))
160 unit-normal (do (.calculateNormal t)(.getNormal t))
161 vertices [vert-1 vert-2 vert-3 unit-normal]]
162 (dorun
163 (for [row (range 4) col (range 3)]
164 (do
165 (.set mat col row (.get (vertices row)col))
166 (.set mat 3 row 1))))
167 mat))
168
169 (defn triangle-transformation
170 "Returns the affine transformation that converts each vertex in the
171 first triangle into the corresponding vertex in the second
172 triangle."
173 [#^Triangle tri-1 #^Triangle tri-2]
174 (.mult
175 (triangle->matrix4f tri-2)
176 (.invert (triangle->matrix4f tri-1))))
177
178 (defn point->vector2f [[u v]]
179 (Vector2f. u v))
180
181 (defn vector2f->vector3f [v]
182 (Vector3f. (.getX v) (.getY v) 0))
183
184 (defn map-triangle [f #^Triangle tri]
185 (Triangle.
186 (f 0 (.get1 tri))
187 (f 1 (.get2 tri))
188 (f 2 (.get3 tri))))
189
190 (defn points->triangle
191 "Convert a list of points into a triangle."
192 [points]
193 (apply #(Triangle. %1 %2 %3)
194 (map (fn [point]
195 (let [point (vec point)]
196 (Vector3f. (get point 0 0)
197 (get point 1 0)
198 (get point 2 0))))
199 (take 3 points))))
200
201 (defn convex-bounds
202 ;;dylan
203 "Returns the smallest square containing the given
204 vertices, as a vector of integers [left top width height]."
205 ;; "Dimensions of the smallest integer bounding square of the list of
206 ;; 2D verticies in the form: [x y width height]."
207 [uv-verts]
208 (let [xs (map first uv-verts)
209 ys (map second uv-verts)
210 x0 (Math/floor (apply min xs))
211 y0 (Math/floor (apply min ys))
212 x1 (Math/ceil (apply max xs))
213 y1 (Math/ceil (apply max ys))]
214 [x0 y0 (- x1 x0) (- y1 y0)]))
215
216 (defn sensors-in-triangle
217 ;;dylan
218 "Locate the touch sensors in the triangle, returning a map of their UV and geometry-relative coordinates."
219 ;;"Find the locations of the touch sensors within a triangle in both
220 ;; UV and gemoetry relative coordinates."
221 [image mesh tri-index]
222 (let [width (.getWidth image)
223 height (.getHeight image)
224 UV-vertex-coords (triangle-UV-coord mesh width height tri-index)
225 bounds (convex-bounds UV-vertex-coords)
226
227 cutout-triangle (points->triangle UV-vertex-coords)
228 UV-sensor-coords
229 (filter (comp (partial inside-triangle? cutout-triangle)
230 (fn [[u v]] (Vector3f. u v 0)))
231 (white-coordinates image bounds))
232 UV->geometry (triangle-transformation
233 cutout-triangle
234 (triangle mesh tri-index))
235 geometry-sensor-coords
236 (map (fn [[u v]] (.mult UV->geometry (Vector3f. u v 0)))
237 UV-sensor-coords)]
238 {:UV UV-sensor-coords :geometry geometry-sensor-coords}))
239
240 (defn-memo locate-feelers
241 "Search the geometry's tactile UV image for touch sensors, returning
242 their positions in geometry-relative coordinates."
243 [#^Geometry geo]
244 (let [mesh (.getMesh geo)
245 num-triangles (.getTriangleCount mesh)]
246 (if-let [image (tactile-sensor-image geo)]
247 (map
248 (partial sensors-in-triangle image mesh)
249 (range num-triangles))
250 (repeat (.getTriangleCount mesh) {:UV nil :geometry nil}))))
251
252 (defn-memo touch-topology [#^Gemoetry geo]
253 (vec (collapse (reduce concat (map :UV (locate-feelers geo))))))
254
255 (defn-memo feeler-coordinates [#^Geometry geo]
256 (vec (map :geometry (locate-feelers geo))))
257
258 (defn enable-touch [#^Geometry geo]
259 (let [feeler-coords (feeler-coordinates geo)
260 tris (triangles geo)
261 limit 0.1
262 ;;results (CollisionResults.)
263 ]
264 (if (empty? (touch-topology geo))
265 nil
266 (fn [node]
267 (let [sensor-origins
268 (map
269 #(map (partial local-to-world geo) %)
270 feeler-coords)
271 triangle-normals
272 (map (partial get-ray-direction geo)
273 tris)
274 rays
275 (flatten
276 (map (fn [origins norm]
277 (map #(doto (Ray. % norm)
278 (.setLimit limit)) origins))
279 sensor-origins triangle-normals))]
280 (vector
281 (touch-topology geo)
282 (vec
283 (for [ray rays]
284 (do
285 (let [results (CollisionResults.)]
286 (.collideWith node ray results)
287 (let [touch-objects
288 (filter #(not (= geo (.getGeometry %)))
289 results)]
290 (- 255
291 (if (empty? touch-objects) 255
292 (rem
293 (int
294 (* 255 (/ (.getDistance
295 (first touch-objects)) limit)))
296 256))))))))))))))
297
298
299 (defn touch [#^Node pieces]
300 (filter (comp not nil?)
301 (map enable-touch
302 (filter #(isa? (class %) Geometry)
303 (node-seq pieces)))))
304
305
306 #+end_src
307
308
309 * Example
310
311 #+name: touch-test
312 #+begin_src clojure
313 (ns cortex.test.touch
314 (:use (cortex world util touch))
315 (:import
316 com.jme3.scene.shape.Sphere
317 com.jme3.math.ColorRGBA
318 com.jme3.math.Vector3f
319 com.jme3.material.RenderState$BlendMode
320 com.jme3.renderer.queue.RenderQueue$Bucket
321 com.jme3.scene.shape.Box
322 com.jme3.scene.Node))
323
324 (defn ray-origin-debug
325 [ray color]
326 (make-shape
327 (assoc base-shape
328 :shape (Sphere. 5 5 0.05)
329 :name "arrow"
330 :color color
331 :texture false
332 :physical? false
333 :position
334 (.getOrigin ray))))
335
336 (defn ray-debug [ray color]
337 (make-shape
338 (assoc
339 base-shape
340 :name "debug-ray"
341 :physical? false
342 :shape (com.jme3.scene.shape.Line.
343 (.getOrigin ray)
344 (.add
345 (.getOrigin ray)
346 (.mult (.getDirection ray)
347 (float (.getLimit ray))))))))
348
349
350 (defn contact-color [contacts]
351 (case contacts
352 0 ColorRGBA/Gray
353 1 ColorRGBA/Red
354 2 ColorRGBA/Green
355 3 ColorRGBA/Yellow
356 4 ColorRGBA/Orange
357 5 ColorRGBA/Red
358 6 ColorRGBA/Magenta
359 7 ColorRGBA/Pink
360 8 ColorRGBA/White))
361
362 (defn update-ray-debug [node ray contacts]
363 (let [origin (.getChild node 0)]
364 (.setLocalTranslation origin (.getOrigin ray))
365 (.setColor (.getMaterial origin) "Color" (contact-color contacts))))
366
367 (defn init-node
368 [debug-node rays]
369 (.detachAllChildren debug-node)
370 (dorun
371 (for [ray rays]
372 (do
373 (.attachChild
374 debug-node
375 (doto (Node.)
376 (.attachChild (ray-origin-debug ray ColorRGBA/Gray))
377 (.attachChild (ray-debug ray ColorRGBA/Gray))
378 ))))))
379
380 (defn manage-ray-debug-node [debug-node geom touch-data limit]
381 (let [rays (normal-rays limit geom)]
382 (if (not= (count (.getChildren debug-node)) (count touch-data))
383 (init-node debug-node rays))
384 (dorun
385 (for [n (range (count touch-data))]
386 (update-ray-debug
387 (.getChild debug-node n) (nth rays n) (nth touch-data n))))))
388
389 (defn transparent-sphere []
390 (doto
391 (make-shape
392 (merge base-shape
393 {:position (Vector3f. 0 2 0)
394 :name "the blob."
395 :material "Common/MatDefs/Misc/Unshaded.j3md"
396 :texture "Textures/purpleWisp.png"
397 :physical? true
398 :mass 70
399 :color ColorRGBA/Blue
400 :shape (Sphere. 10 10 1)}))
401 (-> (.getMaterial)
402 (.getAdditionalRenderState)
403 (.setBlendMode RenderState$BlendMode/Alpha))
404 (.setQueueBucket RenderQueue$Bucket/Transparent)))
405
406 (defn transparent-box []
407 (doto
408 (make-shape
409 (merge base-shape
410 {:position (Vector3f. 0 2 0)
411 :name "box"
412 :material "Common/MatDefs/Misc/Unshaded.j3md"
413 :texture "Textures/purpleWisp.png"
414 :physical? true
415 :mass 70
416 :color ColorRGBA/Blue
417 :shape (Box. 1 1 1)}))
418 (-> (.getMaterial)
419 (.getAdditionalRenderState)
420 (.setBlendMode RenderState$BlendMode/Alpha))
421 (.setQueueBucket RenderQueue$Bucket/Transparent)))
422
423 (defn transparent-floor []
424 (doto
425 (box 5 0.2 5 :mass 0 :position (Vector3f. 0 -2 0)
426 :material "Common/MatDefs/Misc/Unshaded.j3md"
427 :texture "Textures/redWisp.png"
428 :name "floor")
429 (-> (.getMaterial)
430 (.getAdditionalRenderState)
431 (.setBlendMode RenderState$BlendMode/Alpha))
432 (.setQueueBucket RenderQueue$Bucket/Transparent)))
433
434 (defn test-skin
435 "Testing touch:
436 you should see a ball which responds to the table
437 and whatever balls hit it."
438 []
439 (let [b
440 ;;(transparent-box)
441 (transparent-sphere)
442 ;;(sphere)
443 f (transparent-floor)
444 debug-node (Node.)
445 node (doto (Node.) (.attachChild b) (.attachChild f))
446 root-node (doto (Node.) (.attachChild node)
447 (.attachChild debug-node))
448 ]
449
450 (world
451 root-node
452 {"key-return" (fire-cannon-ball node)}
453 (fn [world]
454 ;; (Capture/SimpleCaptureVideo
455 ;; world
456 ;; (file-str "/home/r/proj/cortex/tmp/blob.avi"))
457 ;; (no-logging)
458 ;;(enable-debug world)
459 ;; (set-accuracy world (/ 1 60))
460 )
461
462 (fn [& _]
463 (let [sensitivity 0.2
464 touch-data (touch-percieve sensitivity b node)]
465 (manage-ray-debug-node debug-node b touch-data sensitivity))
466 ))))
467
468
469 #+end_src
470
471
472
473
474
475 * COMMENT code generation
476 #+begin_src clojure :tangle ../src/cortex/touch.clj
477 <<skin-main>>
478 #+end_src
479
480 #+begin_src clojure :tangle ../src/cortex/test/touch.clj
481 <<touch-test>>
482 #+end_src
483
484
485
486
487
488