comparison org/touch.org @ 229:6f1be9525e40

fleshing out text in touch.org
author Robert McIntyre <rlm@mit.edu>
date Sat, 11 Feb 2012 19:13:39 -0700
parents 0589c35f04f2
children f9b7d674aed8
comparison
equal deleted inserted replaced
228:0589c35f04f2 229:6f1be9525e40
3 #+email: rlm@mit.edu 3 #+email: rlm@mit.edu
4 #+description: Simulated touch for AI research using JMonkeyEngine and clojure. 4 #+description: Simulated touch for AI research using JMonkeyEngine and clojure.
5 #+keywords: simulation, tactile sense, jMonkeyEngine3, clojure 5 #+keywords: simulation, tactile sense, jMonkeyEngine3, clojure
6 #+SETUPFILE: ../../aurellem/org/setup.org 6 #+SETUPFILE: ../../aurellem/org/setup.org
7 #+INCLUDE: ../../aurellem/org/level-0.org 7 #+INCLUDE: ../../aurellem/org/level-0.org
8
9
8 10
9 * Touch 11 * Touch
10 12
11 Touch is critical to navigation and spatial reasoning and as such I 13 Touch is critical to navigation and spatial reasoning and as such I
12 need a simulated version of it to give to my AI creatures. 14 need a simulated version of it to give to my AI creatures.
33 35
34 Implementing touch in jMonkeyEngine follows a different techinal route 36 Implementing touch in jMonkeyEngine follows a different techinal route
35 than vision and hearing. Those two senses piggybacked off 37 than vision and hearing. Those two senses piggybacked off
36 jMonkeyEngine's 3D audio and video rendering subsystems. To simulate 38 jMonkeyEngine's 3D audio and video rendering subsystems. To simulate
37 Touch, I use jMonkeyEngine's physics system to execute many small 39 Touch, I use jMonkeyEngine's physics system to execute many small
38 collision detections, one for each "hair". 40 collision detections, one for each "hair". The placement of the
39 41 "hairs" is determined by a UV-mapped image which shows where each hair
40 * Sensor Related Functions 42 should be on the 3D surface of the body.
41 #+begin_src clojure 43
42 (defn sensors-in-triangle 44
43 "Locate the touch sensors in the triangle, returning a map of their 45 * Defining Touch Meta-Data in Blender
44 UV and geometry-relative coordinates." 46
45 [image mesh tri-index] 47 Each geometry can have a single UV map which describes the position
46 (let [width (.getWidth image) 48 and length of the "hairs" which will constitute its sense of
47 height (.getHeight image) 49 touch. This image path is stored under the "touch" key. The image
48 UV-vertex-coords (triangle-UV-coord mesh width height tri-index) 50 itself is grayscale, with black meaning a hair length of 0 (no hair is
49 bounds (convex-bounds UV-vertex-coords) 51 present) and white meaning a hair length of =scale=, which is a float
50 52 stored under the key "scale". If the pixel is gray then the resultant
51 cutout-triangle (points->triangle UV-vertex-coords) 53 hair length is linearly interpolated between 0 and =scale=.
52 UV-sensor-coords 54
53 (filter (comp (partial inside-triangle? cutout-triangle) 55 #+begin_src clojure
54 (fn [[u v]] (Vector3f. u v 0))) 56 (defn tactile-sensor-profile
55 (white-coordinates image bounds)) 57 "Return the touch-sensor distribution image in BufferedImage format,
56 UV->geometry (triangle-transformation 58 or nil if it does not exist."
57 cutout-triangle 59 [#^Geometry obj]
58 (mesh-triangle mesh tri-index)) 60 (if-let [image-path (meta-data obj "touch")]
59 geometry-sensor-coords 61 (load-image image-path)))
60 (map (fn [[u v]] (.mult UV->geometry (Vector3f. u v 0))) 62 #+end_src
61 UV-sensor-coords)] 63
62 {:UV UV-sensor-coords :geometry geometry-sensor-coords})) 64
63 65 ** TODO add image showing example touch-uv map
64 (defn-memo locate-feelers 66 ** TODO add metadata display for worm
65 "Search the geometry's tactile UV profile for touch sensors,
66 returning their positions in geometry-relative coordinates."
67 [#^Geometry geo]
68 (let [mesh (.getMesh geo)
69 num-triangles (.getTriangleCount mesh)]
70 (if-let [image (tactile-sensor-profile geo)]
71 (map
72 (partial sensors-in-triangle image mesh)
73 (range num-triangles))
74 (repeat (.getTriangleCount mesh) {:UV nil :geometry nil}))))
75
76 (defn-memo touch-topology
77 "Return a sequence of vectors of the form [x y] describing the
78 \"topology\" of the tactile sensors. Points that are close together
79 in the touch-topology are generally close together in the simulation."
80 [#^Gemoetry geo]
81 (vec (collapse (reduce concat (map :UV (locate-feelers geo))))))
82
83 (defn-memo feeler-coordinates
84 "The location of the touch sensors in world-space coordinates."
85 [#^Geometry geo]
86 (vec (map :geometry (locate-feelers geo))))
87 #+end_src
88 67
89 * Triangle Manipulation Functions 68 * Triangle Manipulation Functions
69
70 The rigid bodies which make up a creature have an underlying
71 =Geometry=, which is a =Mesh= plus a =Material= and other important
72 data involved with displaying the body.
73
74 A =Mesh= is composed of =Triangles=, and each =Triangle= has three
75 verticies which have coordinates in XYZ space and UV space.
76
77 Here, =(triangles)= gets all the triangles which compose a mesh, and
78 =(triangle-UV-coord)= returns the the UV coordinates of the verticies
79 of a triangle.
90 80
91 #+begin_src clojure 81 #+begin_src clojure
92 (defn triangles 82 (defn triangles
93 "Return a sequence of all the Triangles which compose a given 83 "Return a sequence of all the Triangles which compose a given
94 Geometry." 84 Geometry."
139 (map (partial vertex-UV-coord mesh) 129 (map (partial vertex-UV-coord mesh)
140 (triangle-vertex-indices mesh triangle-index)))) 130 (triangle-vertex-indices mesh triangle-index))))
141 #+end_src 131 #+end_src
142 132
143 * Schrapnel Conversion Functions 133 * Schrapnel Conversion Functions
134
135 It is convienent to treat a =Triangle= as a sequence of verticies, and
136 a =Vector2f= and =Vector3f= as a sequence of floats. These conversion
137 functions make this easy. If these classes implemented =Iterable= then
138 this code would not be necessary. Hopefully they will in the future.
139
144 #+begin_src clojure 140 #+begin_src clojure
145 (defn triangle-seq [#^Triangle tri] 141 (defn triangle-seq [#^Triangle tri]
146 [(.get1 tri) (.get2 tri) (.get3 tri)]) 142 [(.get1 tri) (.get2 tri) (.get3 tri)])
147 143
148 (defn vector3f-seq [#^Vector3f v] 144 (defn vector3f-seq [#^Vector3f v]
171 (get point 2 0)))) 167 (get point 2 0))))
172 (take 3 points)))) 168 (take 3 points))))
173 #+end_src 169 #+end_src
174 170
175 * Triangle Affine Transforms 171 * Triangle Affine Transforms
172
173 The position of each hair is stored in a 2D image in UV
174 coordinates. To place the hair in 3D space we must convert from UV
175 coordinates to XYZ coordinates. Each =Triangle= has coordinates in
176 both UV-space and XYZ-space, which defines a unique [[http://mathworld.wolfram.com/AffineTransformation.html ][Affine Transform]]
177 for translating any coordinate within the UV triangle to the
178 cooresponding coordinate in the XYZ triangle.
176 179
177 #+begin_src clojure 180 #+begin_src clojure
178 (defn triangle->matrix4f 181 (defn triangle->matrix4f
179 "Converts the triangle into a 4x4 matrix: The first three columns 182 "Converts the triangle into a 4x4 matrix: The first three columns
180 contain the vertices of the triangle; the last contains the unit 183 contain the vertices of the triangle; the last contains the unit
200 (.mult 203 (.mult
201 (triangle->matrix4f tri-2) 204 (triangle->matrix4f tri-2)
202 (.invert (triangle->matrix4f tri-1)))) 205 (.invert (triangle->matrix4f tri-1))))
203 #+end_src 206 #+end_src
204 207
205 * Blender Meta-Data 208 * Triangle Boundaries
206 209
207 #+begin_src clojure 210 For efficiency's sake I will divide the UV-image into small squares
208 (defn tactile-sensor-profile 211 which inscribe each UV-triangle, then extract the points which lie
209 "Return the touch-sensor distribution image in BufferedImage format, 212 inside the triangle and map them to 3D-space using
210 or nil if it does not exist." 213 =(triangle-transform)= above. To do this I need a function,
211 [#^Geometry obj] 214 =(inside-triangle?)=, which determines whether a point is inside a
212 (if-let [image-path (meta-data obj "touch")] 215 triangle in 2D UV-space.
213 (load-image image-path))) 216
217 #+begin_src clojure
218 (defn convex-bounds
219 "Returns the smallest square containing the given vertices, as a
220 vector of integers [left top width height]."
221 [uv-verts]
222 (let [xs (map first uv-verts)
223 ys (map second uv-verts)
224 x0 (Math/floor (apply min xs))
225 y0 (Math/floor (apply min ys))
226 x1 (Math/ceil (apply max xs))
227 y1 (Math/ceil (apply max ys))]
228 [x0 y0 (- x1 x0) (- y1 y0)]))
229
230 (defn same-side?
231 "Given the points p1 and p2 and the reference point ref, is point p
232 on the same side of the line that goes through p1 and p2 as ref is?"
233 [p1 p2 ref p]
234 (<=
235 0
236 (.dot
237 (.cross (.subtract p2 p1) (.subtract p p1))
238 (.cross (.subtract p2 p1) (.subtract ref p1)))))
239
240 (defn inside-triangle?
241 "Is the point inside the triangle?"
242 {:author "Dylan Holmes"}
243 [#^Triangle tri #^Vector3f p]
244 (let [[vert-1 vert-2 vert-3] (triangle-seq tri)]
245 (and
246 (same-side? vert-1 vert-2 vert-3 p)
247 (same-side? vert-2 vert-3 vert-1 p)
248 (same-side? vert-3 vert-1 vert-2 p))))
249 #+end_src
250
251
252
253 * Sensor Related Functions
254
255 These functions analyze the touch-sensor-profile image convert the
256 location of each touch sensor from pixel coordinates to UV-coordinates
257 and XYZ-coordinates.
258
259 #+begin_src clojure
260 (defn sensors-in-triangle
261 "Locate the touch sensors in the triangle, returning a map of their
262 UV and geometry-relative coordinates."
263 [image mesh tri-index]
264 (let [width (.getWidth image)
265 height (.getHeight image)
266 UV-vertex-coords (triangle-UV-coord mesh width height tri-index)
267 bounds (convex-bounds UV-vertex-coords)
268
269 cutout-triangle (points->triangle UV-vertex-coords)
270 UV-sensor-coords
271 (filter (comp (partial inside-triangle? cutout-triangle)
272 (fn [[u v]] (Vector3f. u v 0)))
273 (white-coordinates image bounds))
274 UV->geometry (triangle-transformation
275 cutout-triangle
276 (mesh-triangle mesh tri-index))
277 geometry-sensor-coords
278 (map (fn [[u v]] (.mult UV->geometry (Vector3f. u v 0)))
279 UV-sensor-coords)]
280 {:UV UV-sensor-coords :geometry geometry-sensor-coords}))
281
282 (defn-memo locate-feelers
283 "Search the geometry's tactile UV profile for touch sensors,
284 returning their positions in geometry-relative coordinates."
285 [#^Geometry geo]
286 (let [mesh (.getMesh geo)
287 num-triangles (.getTriangleCount mesh)]
288 (if-let [image (tactile-sensor-profile geo)]
289 (map
290 (partial sensors-in-triangle image mesh)
291 (range num-triangles))
292 (repeat (.getTriangleCount mesh) {:UV nil :geometry nil}))))
293
294 (defn-memo touch-topology
295 "Return a sequence of vectors of the form [x y] describing the
296 \"topology\" of the tactile sensors. Points that are close together
297 in the touch-topology are generally close together in the simulation."
298 [#^Gemoetry geo]
299 (vec (collapse (reduce concat (map :UV (locate-feelers geo))))))
300
301 (defn-memo feeler-coordinates
302 "The location of the touch sensors in world-space coordinates."
303 [#^Geometry geo]
304 (vec (map :geometry (locate-feelers geo))))
214 #+end_src 305 #+end_src
215 306
216 * Physics Collision Objects 307 * Physics Collision Objects
217 #+begin_src clojure 308 #+begin_src clojure
218 (defn get-ray-origin 309 (defn get-ray-origin
236 geom 327 geom
237 (.add (.getCenter tri) (.getNormal tri)) n+c) 328 (.add (.getCenter tri) (.getNormal tri)) n+c)
238 (.subtract n+c (get-ray-origin geom tri)))) 329 (.subtract n+c (get-ray-origin geom tri))))
239 #+end_src 330 #+end_src
240 331
241 * Triangle Boundaries
242 #+begin_src clojure
243 (defn same-side?
244 "Given the points p1 and p2 and the reference point ref, is point p
245 on the same side of the line that goes through p1 and p2 as ref is?"
246 [p1 p2 ref p]
247 (<=
248 0
249 (.dot
250 (.cross (.subtract p2 p1) (.subtract p p1))
251 (.cross (.subtract p2 p1) (.subtract ref p1)))))
252
253 (defn inside-triangle?
254 "Is the point inside the triangle?"
255 {:author "Dylan Holmes"}
256 [#^Triangle tri #^Vector3f p]
257 (let [[vert-1 vert-2 vert-3] (triangle-seq tri)]
258 (and
259 (same-side? vert-1 vert-2 vert-3 p)
260 (same-side? vert-2 vert-3 vert-1 p)
261 (same-side? vert-3 vert-1 vert-2 p))))
262
263 (defn convex-bounds
264 "Returns the smallest square containing the given vertices, as a
265 vector of integers [left top width height]."
266 [uv-verts]
267 (let [xs (map first uv-verts)
268 ys (map second uv-verts)
269 x0 (Math/floor (apply min xs))
270 y0 (Math/floor (apply min ys))
271 x1 (Math/ceil (apply max xs))
272 y1 (Math/ceil (apply max ys))]
273 [x0 y0 (- x1 x0) (- y1 y0)]))
274 #+end_src
275 332
276 * Skin Creation 333 * Skin Creation
277 334
278 #+begin_src clojure 335 #+begin_src clojure
279 (defn touch-fn 336 (defn touch-fn
364 (:import com.jme3.collision.CollisionResults) 421 (:import com.jme3.collision.CollisionResults)
365 (:import com.jme3.scene.VertexBuffer$Type) 422 (:import com.jme3.scene.VertexBuffer$Type)
366 (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f))) 423 (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f)))
367 #+end_src 424 #+end_src
368 425
369 ;; Every Mesh has many triangles, each with its own index.
370 ;; Every vertex has its own index as well.
371 426
372 * Source Listing 427 * Source Listing
373 * Next 428 * Next
374 429
375 430