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