rlm@37
|
1 #+title: Simulated Sense of Touch
|
rlm@0
|
2 #+author: Robert McIntyre
|
rlm@0
|
3 #+email: rlm@mit.edu
|
rlm@37
|
4 #+description: Simulated touch for AI research using JMonkeyEngine and clojure.
|
rlm@37
|
5 #+keywords: simulation, tactile sense, jMonkeyEngine3, clojure
|
rlm@4
|
6 #+SETUPFILE: ../../aurellem/org/setup.org
|
rlm@4
|
7 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@0
|
8
|
rlm@37
|
9 * Touch
|
rlm@0
|
10
|
rlm@226
|
11 Touch is critical to navigation and spatial reasoning and as such I
|
rlm@226
|
12 need a simulated version of it to give to my AI creatures.
|
rlm@0
|
13
|
rlm@228
|
14 However, touch in my virtual can not exactly correspond to human touch
|
rlm@228
|
15 because my creatures are made out of completely rigid segments that
|
rlm@228
|
16 don't deform like human skin.
|
rlm@228
|
17
|
rlm@228
|
18 Human skin has a wide array of touch sensors, each of which speciliaze
|
rlm@228
|
19 in detecting different vibrational modes and pressures. These sensors
|
rlm@228
|
20 can integrate a vast expanse of skin (i.e. your entire palm), or a
|
rlm@228
|
21 tiny patch of skin at the tip of your finger. The hairs of the skin
|
rlm@228
|
22 help detect objects before they even come into contact with the skin
|
rlm@228
|
23 proper.
|
rlm@228
|
24
|
rlm@228
|
25 Instead of measuring deformation or vibration, I surround each rigid
|
rlm@228
|
26 part with a plenitude of hair-like objects which do not interact with
|
rlm@228
|
27 the physical world. Physical objects can pass through them with no
|
rlm@228
|
28 effect. The hairs are able to measure contact with other objects, and
|
rlm@228
|
29 constantly report how much of their extent is covered. So, even though
|
rlm@228
|
30 the creature's body parts do not deform, the hairs create a margin
|
rlm@228
|
31 around those body parts which achieves a sense of touch which is a
|
rlm@228
|
32 hybrid between a human's sense of deformation and sense from hairs.
|
rlm@228
|
33
|
rlm@228
|
34 Implementing touch in jMonkeyEngine follows a different techinal route
|
rlm@228
|
35 than vision and hearing. Those two senses piggybacked off
|
rlm@228
|
36 jMonkeyEngine's 3D audio and video rendering subsystems. To simulate
|
rlm@228
|
37 Touch, I use jMonkeyEngine's physics system to execute many small
|
rlm@229
|
38 collision detections, one for each "hair". The placement of the
|
rlm@229
|
39 "hairs" is determined by a UV-mapped image which shows where each hair
|
rlm@229
|
40 should be on the 3D surface of the body.
|
rlm@228
|
41
|
rlm@229
|
42
|
rlm@229
|
43 * Defining Touch Meta-Data in Blender
|
rlm@229
|
44
|
rlm@245
|
45 Each geometry can have a single UV map which describes the position of
|
rlm@245
|
46 the "hairs" which will constitute its sense of touch. This image path
|
rlm@245
|
47 is stored under the "touch" key. The image itself is black and white,
|
rlm@245
|
48 with black meaning a hair length of 0 (no hair is present) and white
|
rlm@245
|
49 meaning a hair length of =scale=, which is a float stored under the
|
rlm@245
|
50 key "scale". I call these "hairs" /feelers/.
|
rlm@229
|
51
|
rlm@231
|
52 #+name: meta-data
|
rlm@0
|
53 #+begin_src clojure
|
rlm@229
|
54 (defn tactile-sensor-profile
|
rlm@229
|
55 "Return the touch-sensor distribution image in BufferedImage format,
|
rlm@229
|
56 or nil if it does not exist."
|
rlm@229
|
57 [#^Geometry obj]
|
rlm@229
|
58 (if-let [image-path (meta-data obj "touch")]
|
rlm@229
|
59 (load-image image-path)))
|
rlm@233
|
60
|
rlm@233
|
61 (defn tactile-scale
|
rlm@233
|
62 "Return the maximum length of a hair. All hairs are scalled between
|
rlm@233
|
63 0.0 and this length, depending on their color. Black is 0, and
|
rlm@233
|
64 white is maximum length, and everything in between is scalled
|
rlm@233
|
65 linearlly. Default scale is 0.01 jMonkeyEngine units."
|
rlm@233
|
66 [#^Geometry obj]
|
rlm@233
|
67 (if-let [scale (meta-data obj "scale")]
|
rlm@233
|
68 scale 0.1))
|
rlm@228
|
69 #+end_src
|
rlm@156
|
70
|
rlm@229
|
71 ** TODO add image showing example touch-uv map
|
rlm@229
|
72 ** TODO add metadata display for worm
|
rlm@229
|
73
|
rlm@234
|
74
|
rlm@233
|
75 * Skin Creation
|
rlm@238
|
76
|
rlm@238
|
77 =(touch-kernel)= generates the functions which implement the sense of
|
rlm@238
|
78 touch for a creature. These functions must do 6 things to obtain touch
|
rlm@238
|
79 data.
|
rlm@238
|
80
|
rlm@238
|
81 - Get the tactile profile image and scale paramaters which describe
|
rlm@238
|
82 the layout of feelers along the object's surface.
|
rlm@239
|
83 =(tactile-sensor-profile)=, =(tactile-scale)=
|
rlm@239
|
84
|
rlm@238
|
85 - Find the triangles which make up the mesh in pixel-space and in
|
rlm@238
|
86 world-space.
|
rlm@239
|
87 =(triangles)= =(pixel-triangles)=
|
rlm@239
|
88
|
rlm@239
|
89 - Find the coordinates of each pixel in pixel space. These
|
rlm@239
|
90 coordinates are used to make the touch-topology.
|
rlm@240
|
91 =(feeler-pixel-coords)=
|
rlm@239
|
92
|
rlm@238
|
93 - Find the coordinates of each pixel in world-space. These
|
rlm@240
|
94 coordinates are the origins of the feelers. =(feeler-origins)=
|
rlm@239
|
95
|
rlm@238
|
96 - Calculate the normals of the triangles in world space, and add
|
rlm@238
|
97 them to each of the origins of the feelers. These are the
|
rlm@238
|
98 normalized coordinates of the tips of the feelers.
|
rlm@240
|
99 For both of these, =(feeler-tips)=
|
rlm@239
|
100
|
rlm@238
|
101 - Generate some sort of topology for the sensors.
|
rlm@239
|
102 =(touch-topology)=
|
rlm@239
|
103
|
rlm@238
|
104
|
rlm@233
|
105 #+name: kernel
|
rlm@233
|
106 #+begin_src clojure
|
rlm@233
|
107 (in-ns 'cortex.touch)
|
rlm@233
|
108
|
rlm@244
|
109 (defn set-ray [#^Ray ray #^Matrix4f transform
|
rlm@244
|
110 #^Vector3f origin #^Vector3f tip]
|
rlm@243
|
111 ;; Doing everything locally recduces garbage collection by enough to
|
rlm@243
|
112 ;; be worth it.
|
rlm@243
|
113 (.mult transform origin (.getOrigin ray))
|
rlm@243
|
114
|
rlm@243
|
115 (.mult transform tip (.getDirection ray))
|
rlm@244
|
116 (.subtractLocal (.getDirection ray) (.getOrigin ray)))
|
rlm@242
|
117
|
rlm@233
|
118 (defn touch-kernel
|
rlm@234
|
119 "Constructs a function which will return tactile sensory data from
|
rlm@234
|
120 'geo when called from inside a running simulation"
|
rlm@234
|
121 [#^Geometry geo]
|
rlm@243
|
122 (if-let
|
rlm@243
|
123 [profile (tactile-sensor-profile geo)]
|
rlm@243
|
124 (let [ray-reference-origins (feeler-origins geo profile)
|
rlm@243
|
125 ray-reference-tips (feeler-tips geo profile)
|
rlm@244
|
126 ray-length (tactile-scale geo)
|
rlm@243
|
127 current-rays (map (fn [_] (Ray.)) ray-reference-origins)
|
rlm@243
|
128 topology (touch-topology geo profile)]
|
rlm@244
|
129 (dorun (map #(.setLimit % ray-length) current-rays))
|
rlm@233
|
130 (fn [node]
|
rlm@243
|
131 (let [transform (.getWorldMatrix geo)]
|
rlm@243
|
132 (dorun
|
rlm@244
|
133 (map (fn [ray ref-origin ref-tip]
|
rlm@244
|
134 (set-ray ray transform ref-origin ref-tip))
|
rlm@243
|
135 current-rays ray-reference-origins
|
rlm@244
|
136 ray-reference-tips))
|
rlm@233
|
137 (vector
|
rlm@243
|
138 topology
|
rlm@233
|
139 (vec
|
rlm@243
|
140 (for [ray current-rays]
|
rlm@233
|
141 (do
|
rlm@233
|
142 (let [results (CollisionResults.)]
|
rlm@233
|
143 (.collideWith node ray results)
|
rlm@233
|
144 (let [touch-objects
|
rlm@233
|
145 (filter #(not (= geo (.getGeometry %)))
|
rlm@233
|
146 results)]
|
rlm@233
|
147 [(if (empty? touch-objects)
|
rlm@243
|
148 (.getLimit ray)
|
rlm@243
|
149 (.getDistance (first touch-objects)))
|
rlm@243
|
150 (.getLimit ray)])))))))))))
|
rlm@233
|
151
|
rlm@233
|
152 (defn touch!
|
rlm@233
|
153 "Endow the creature with the sense of touch. Returns a sequence of
|
rlm@233
|
154 functions, one for each body part with a tactile-sensor-proile,
|
rlm@233
|
155 each of which when called returns sensory data for that body part."
|
rlm@233
|
156 [#^Node creature]
|
rlm@233
|
157 (filter
|
rlm@233
|
158 (comp not nil?)
|
rlm@233
|
159 (map touch-kernel
|
rlm@233
|
160 (filter #(isa? (class %) Geometry)
|
rlm@233
|
161 (node-seq creature)))))
|
rlm@233
|
162 #+end_src
|
rlm@233
|
163
|
rlm@238
|
164 * Sensor Related Functions
|
rlm@238
|
165
|
rlm@238
|
166 These functions analyze the touch-sensor-profile image convert the
|
rlm@238
|
167 location of each touch sensor from pixel coordinates to UV-coordinates
|
rlm@238
|
168 and XYZ-coordinates.
|
rlm@238
|
169
|
rlm@238
|
170 #+name: sensors
|
rlm@238
|
171 #+begin_src clojure
|
rlm@240
|
172 (in-ns 'cortex.touch)
|
rlm@240
|
173
|
rlm@240
|
174 (defn feeler-pixel-coords
|
rlm@239
|
175 "Returns the coordinates of the feelers in pixel space in lists, one
|
rlm@239
|
176 list for each triangle, ordered in the same way as (triangles) and
|
rlm@239
|
177 (pixel-triangles)."
|
rlm@239
|
178 [#^Geometry geo image]
|
rlm@240
|
179 (map
|
rlm@240
|
180 (fn [pixel-triangle]
|
rlm@240
|
181 (filter
|
rlm@240
|
182 (fn [coord]
|
rlm@240
|
183 (inside-triangle? (->triangle pixel-triangle)
|
rlm@240
|
184 (->vector3f coord)))
|
rlm@240
|
185 (white-coordinates image (convex-bounds pixel-triangle))))
|
rlm@240
|
186 (pixel-triangles geo image)))
|
rlm@239
|
187
|
rlm@242
|
188 (defn feeler-world-coords [#^Geometry geo image]
|
rlm@240
|
189 (let [transforms
|
rlm@240
|
190 (map #(triangles->affine-transform
|
rlm@240
|
191 (->triangle %1) (->triangle %2))
|
rlm@240
|
192 (pixel-triangles geo image)
|
rlm@240
|
193 (triangles geo))]
|
rlm@242
|
194 (map (fn [transform coords]
|
rlm@240
|
195 (map #(.mult transform (->vector3f %)) coords))
|
rlm@240
|
196 transforms (feeler-pixel-coords geo image))))
|
rlm@239
|
197
|
rlm@242
|
198 (defn feeler-origins [#^Geometry geo image]
|
rlm@242
|
199 (reduce concat (feeler-world-coords geo image)))
|
rlm@242
|
200
|
rlm@240
|
201 (defn feeler-tips [#^Geometry geo image]
|
rlm@242
|
202 (let [world-coords (feeler-world-coords geo image)
|
rlm@241
|
203 normals
|
rlm@241
|
204 (map
|
rlm@241
|
205 (fn [triangle]
|
rlm@241
|
206 (.calculateNormal triangle)
|
rlm@241
|
207 (.clone (.getNormal triangle)))
|
rlm@241
|
208 (map ->triangle (triangles geo)))]
|
rlm@242
|
209
|
rlm@242
|
210 (mapcat (fn [origins normal]
|
rlm@242
|
211 (map #(.add % normal) origins))
|
rlm@242
|
212 world-coords normals)))
|
rlm@241
|
213
|
rlm@241
|
214 (defn touch-topology [#^Geometry geo image]
|
rlm@243
|
215 (collapse (reduce concat (feeler-pixel-coords geo image))))
|
rlm@238
|
216 #+end_src
|
rlm@238
|
217
|
rlm@233
|
218 * Visualizing Touch
|
rlm@233
|
219 #+name: visualization
|
rlm@233
|
220 #+begin_src clojure
|
rlm@233
|
221 (in-ns 'cortex.touch)
|
rlm@233
|
222
|
rlm@233
|
223 (defn touch->gray
|
rlm@245
|
224 "Convert a pair of [distance, max-distance] into a grayscale pixel."
|
rlm@233
|
225 [distance max-distance]
|
rlm@245
|
226 (gray (- 255 (rem (int (* 255 (/ distance max-distance))) 256))))
|
rlm@233
|
227
|
rlm@233
|
228 (defn view-touch
|
rlm@245
|
229 "Creates a function which accepts a list of touch sensor-data and
|
rlm@233
|
230 displays each element to the screen."
|
rlm@233
|
231 []
|
rlm@233
|
232 (view-sense
|
rlm@233
|
233 (fn
|
rlm@233
|
234 [[coords sensor-data]]
|
rlm@233
|
235 (let [image (points->image coords)]
|
rlm@233
|
236 (dorun
|
rlm@233
|
237 (for [i (range (count coords))]
|
rlm@233
|
238 (.setRGB image ((coords i) 0) ((coords i) 1)
|
rlm@233
|
239 (apply touch->gray (sensor-data i)))))
|
rlm@233
|
240 image))))
|
rlm@233
|
241 #+end_src
|
rlm@233
|
242
|
rlm@233
|
243
|
rlm@233
|
244
|
rlm@228
|
245 * Triangle Manipulation Functions
|
rlm@228
|
246
|
rlm@229
|
247 The rigid bodies which make up a creature have an underlying
|
rlm@229
|
248 =Geometry=, which is a =Mesh= plus a =Material= and other important
|
rlm@229
|
249 data involved with displaying the body.
|
rlm@229
|
250
|
rlm@229
|
251 A =Mesh= is composed of =Triangles=, and each =Triangle= has three
|
rlm@229
|
252 verticies which have coordinates in XYZ space and UV space.
|
rlm@229
|
253
|
rlm@229
|
254 Here, =(triangles)= gets all the triangles which compose a mesh, and
|
rlm@229
|
255 =(triangle-UV-coord)= returns the the UV coordinates of the verticies
|
rlm@229
|
256 of a triangle.
|
rlm@229
|
257
|
rlm@231
|
258 #+name: triangles-1
|
rlm@228
|
259 #+begin_src clojure
|
rlm@239
|
260 (in-ns 'cortex.touch)
|
rlm@239
|
261
|
rlm@239
|
262 (defn vector3f-seq [#^Vector3f v]
|
rlm@239
|
263 [(.getX v) (.getY v) (.getZ v)])
|
rlm@239
|
264
|
rlm@239
|
265 (defn triangle-seq [#^Triangle tri]
|
rlm@239
|
266 [(vector3f-seq (.get1 tri))
|
rlm@239
|
267 (vector3f-seq (.get2 tri))
|
rlm@239
|
268 (vector3f-seq (.get3 tri))])
|
rlm@239
|
269
|
rlm@240
|
270 (defn ->vector3f
|
rlm@240
|
271 ([coords] (Vector3f. (nth coords 0 0)
|
rlm@240
|
272 (nth coords 1 0)
|
rlm@240
|
273 (nth coords 2 0))))
|
rlm@239
|
274
|
rlm@239
|
275 (defn ->triangle [points]
|
rlm@239
|
276 (apply #(Triangle. %1 %2 %3) (map ->vector3f points)))
|
rlm@239
|
277
|
rlm@239
|
278 (defn triangle
|
rlm@245
|
279 "Get the triangle specified by triangle-index from the mesh."
|
rlm@239
|
280 [#^Geometry geo triangle-index]
|
rlm@239
|
281 (triangle-seq
|
rlm@239
|
282 (let [scratch (Triangle.)]
|
rlm@239
|
283 (.getTriangle (.getMesh geo) triangle-index scratch) scratch)))
|
rlm@239
|
284
|
rlm@228
|
285 (defn triangles
|
rlm@228
|
286 "Return a sequence of all the Triangles which compose a given
|
rlm@228
|
287 Geometry."
|
rlm@239
|
288 [#^Geometry geo]
|
rlm@239
|
289 (map (partial triangle geo) (range (.getTriangleCount (.getMesh geo)))))
|
rlm@228
|
290
|
rlm@228
|
291 (defn triangle-vertex-indices
|
rlm@228
|
292 "Get the triangle vertex indices of a given triangle from a given
|
rlm@228
|
293 mesh."
|
rlm@228
|
294 [#^Mesh mesh triangle-index]
|
rlm@228
|
295 (let [indices (int-array 3)]
|
rlm@228
|
296 (.getTriangle mesh triangle-index indices)
|
rlm@228
|
297 (vec indices)))
|
rlm@228
|
298
|
rlm@228
|
299 (defn vertex-UV-coord
|
rlm@228
|
300 "Get the UV-coordinates of the vertex named by vertex-index"
|
rlm@228
|
301 [#^Mesh mesh vertex-index]
|
rlm@228
|
302 (let [UV-buffer
|
rlm@228
|
303 (.getData
|
rlm@228
|
304 (.getBuffer
|
rlm@228
|
305 mesh
|
rlm@228
|
306 VertexBuffer$Type/TexCoord))]
|
rlm@228
|
307 [(.get UV-buffer (* vertex-index 2))
|
rlm@228
|
308 (.get UV-buffer (+ 1 (* vertex-index 2)))]))
|
rlm@228
|
309
|
rlm@239
|
310 (defn pixel-triangle [#^Geometry geo image index]
|
rlm@239
|
311 (let [mesh (.getMesh geo)
|
rlm@239
|
312 width (.getWidth image)
|
rlm@239
|
313 height (.getHeight image)]
|
rlm@239
|
314 (vec (map (fn [[u v]] (vector (* width u) (* height v)))
|
rlm@239
|
315 (map (partial vertex-UV-coord mesh)
|
rlm@239
|
316 (triangle-vertex-indices mesh index))))))
|
rlm@228
|
317
|
rlm@239
|
318 (defn pixel-triangles [#^Geometry geo image]
|
rlm@239
|
319 (let [height (.getHeight image)
|
rlm@239
|
320 width (.getWidth image)]
|
rlm@239
|
321 (map (partial pixel-triangle geo image)
|
rlm@239
|
322 (range (.getTriangleCount (.getMesh geo))))))
|
rlm@229
|
323
|
rlm@228
|
324 #+end_src
|
rlm@228
|
325
|
rlm@228
|
326 * Triangle Affine Transforms
|
rlm@228
|
327
|
rlm@229
|
328 The position of each hair is stored in a 2D image in UV
|
rlm@229
|
329 coordinates. To place the hair in 3D space we must convert from UV
|
rlm@229
|
330 coordinates to XYZ coordinates. Each =Triangle= has coordinates in
|
rlm@229
|
331 both UV-space and XYZ-space, which defines a unique [[http://mathworld.wolfram.com/AffineTransformation.html ][Affine Transform]]
|
rlm@229
|
332 for translating any coordinate within the UV triangle to the
|
rlm@229
|
333 cooresponding coordinate in the XYZ triangle.
|
rlm@229
|
334
|
rlm@231
|
335 #+name: triangles-3
|
rlm@228
|
336 #+begin_src clojure
|
rlm@243
|
337 (in-ns 'cortex.touch)
|
rlm@243
|
338
|
rlm@228
|
339 (defn triangle->matrix4f
|
rlm@228
|
340 "Converts the triangle into a 4x4 matrix: The first three columns
|
rlm@228
|
341 contain the vertices of the triangle; the last contains the unit
|
rlm@228
|
342 normal of the triangle. The bottom row is filled with 1s."
|
rlm@228
|
343 [#^Triangle t]
|
rlm@228
|
344 (let [mat (Matrix4f.)
|
rlm@228
|
345 [vert-1 vert-2 vert-3]
|
rlm@228
|
346 ((comp vec map) #(.get t %) (range 3))
|
rlm@228
|
347 unit-normal (do (.calculateNormal t)(.getNormal t))
|
rlm@228
|
348 vertices [vert-1 vert-2 vert-3 unit-normal]]
|
rlm@228
|
349 (dorun
|
rlm@228
|
350 (for [row (range 4) col (range 3)]
|
rlm@228
|
351 (do
|
rlm@228
|
352 (.set mat col row (.get (vertices row)col))
|
rlm@245
|
353 (.set mat 3 row 1)))) mat))
|
rlm@228
|
354
|
rlm@240
|
355 (defn triangles->affine-transform
|
rlm@228
|
356 "Returns the affine transformation that converts each vertex in the
|
rlm@228
|
357 first triangle into the corresponding vertex in the second
|
rlm@228
|
358 triangle."
|
rlm@228
|
359 [#^Triangle tri-1 #^Triangle tri-2]
|
rlm@228
|
360 (.mult
|
rlm@228
|
361 (triangle->matrix4f tri-2)
|
rlm@228
|
362 (.invert (triangle->matrix4f tri-1))))
|
rlm@228
|
363 #+end_src
|
rlm@228
|
364
|
rlm@239
|
365
|
rlm@239
|
366 * Schrapnel Conversion Functions
|
rlm@239
|
367
|
rlm@239
|
368 It is convienent to treat a =Triangle= as a sequence of verticies, and
|
rlm@239
|
369 a =Vector2f= and =Vector3f= as a sequence of floats. These conversion
|
rlm@239
|
370 functions make this easy. If these classes implemented =Iterable= then
|
rlm@239
|
371 this code would not be necessary. Hopefully they will in the future.
|
rlm@239
|
372
|
rlm@239
|
373
|
rlm@229
|
374 * Triangle Boundaries
|
rlm@229
|
375
|
rlm@229
|
376 For efficiency's sake I will divide the UV-image into small squares
|
rlm@229
|
377 which inscribe each UV-triangle, then extract the points which lie
|
rlm@229
|
378 inside the triangle and map them to 3D-space using
|
rlm@229
|
379 =(triangle-transform)= above. To do this I need a function,
|
rlm@229
|
380 =(inside-triangle?)=, which determines whether a point is inside a
|
rlm@229
|
381 triangle in 2D UV-space.
|
rlm@228
|
382
|
rlm@231
|
383 #+name: triangles-4
|
rlm@228
|
384 #+begin_src clojure
|
rlm@229
|
385 (defn convex-bounds
|
rlm@229
|
386 "Returns the smallest square containing the given vertices, as a
|
rlm@229
|
387 vector of integers [left top width height]."
|
rlm@240
|
388 [verts]
|
rlm@240
|
389 (let [xs (map first verts)
|
rlm@240
|
390 ys (map second verts)
|
rlm@229
|
391 x0 (Math/floor (apply min xs))
|
rlm@229
|
392 y0 (Math/floor (apply min ys))
|
rlm@229
|
393 x1 (Math/ceil (apply max xs))
|
rlm@229
|
394 y1 (Math/ceil (apply max ys))]
|
rlm@229
|
395 [x0 y0 (- x1 x0) (- y1 y0)]))
|
rlm@229
|
396
|
rlm@229
|
397 (defn same-side?
|
rlm@229
|
398 "Given the points p1 and p2 and the reference point ref, is point p
|
rlm@229
|
399 on the same side of the line that goes through p1 and p2 as ref is?"
|
rlm@229
|
400 [p1 p2 ref p]
|
rlm@229
|
401 (<=
|
rlm@229
|
402 0
|
rlm@229
|
403 (.dot
|
rlm@229
|
404 (.cross (.subtract p2 p1) (.subtract p p1))
|
rlm@229
|
405 (.cross (.subtract p2 p1) (.subtract ref p1)))))
|
rlm@229
|
406
|
rlm@229
|
407 (defn inside-triangle?
|
rlm@229
|
408 "Is the point inside the triangle?"
|
rlm@229
|
409 {:author "Dylan Holmes"}
|
rlm@229
|
410 [#^Triangle tri #^Vector3f p]
|
rlm@240
|
411 (let [[vert-1 vert-2 vert-3] [(.get1 tri) (.get2 tri) (.get3 tri)]]
|
rlm@229
|
412 (and
|
rlm@229
|
413 (same-side? vert-1 vert-2 vert-3 p)
|
rlm@229
|
414 (same-side? vert-2 vert-3 vert-1 p)
|
rlm@229
|
415 (same-side? vert-3 vert-1 vert-2 p))))
|
rlm@229
|
416 #+end_src
|
rlm@229
|
417
|
rlm@228
|
418 * Physics Collision Objects
|
rlm@230
|
419
|
rlm@234
|
420 The "hairs" are actually =Rays= which extend from a point on a
|
rlm@230
|
421 =Triangle= in the =Mesh= normal to the =Triangle's= surface.
|
rlm@230
|
422
|
rlm@226
|
423 * Headers
|
rlm@231
|
424
|
rlm@231
|
425 #+name: touch-header
|
rlm@226
|
426 #+begin_src clojure
|
rlm@226
|
427 (ns cortex.touch
|
rlm@226
|
428 "Simulate the sense of touch in jMonkeyEngine3. Enables any Geometry
|
rlm@226
|
429 to be outfitted with touch sensors with density determined by a UV
|
rlm@226
|
430 image. In this way a Geometry can know what parts of itself are
|
rlm@226
|
431 touching nearby objects. Reads specially prepared blender files to
|
rlm@226
|
432 construct this sense automatically."
|
rlm@226
|
433 {:author "Robert McIntyre"}
|
rlm@226
|
434 (:use (cortex world util sense))
|
rlm@226
|
435 (:use clojure.contrib.def)
|
rlm@226
|
436 (:import (com.jme3.scene Geometry Node Mesh))
|
rlm@226
|
437 (:import com.jme3.collision.CollisionResults)
|
rlm@226
|
438 (:import com.jme3.scene.VertexBuffer$Type)
|
rlm@226
|
439 (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f)))
|
rlm@226
|
440 #+end_src
|
rlm@37
|
441
|
rlm@232
|
442 * Adding Touch to the Worm
|
rlm@232
|
443
|
rlm@232
|
444 #+name: test-touch
|
rlm@232
|
445 #+begin_src clojure
|
rlm@232
|
446 (ns cortex.test.touch
|
rlm@232
|
447 (:use (cortex world util sense body touch))
|
rlm@232
|
448 (:use cortex.test.body))
|
rlm@232
|
449
|
rlm@232
|
450 (cortex.import/mega-import-jme3)
|
rlm@232
|
451
|
rlm@232
|
452 (defn test-touch []
|
rlm@232
|
453 (let [the-worm (doto (worm) (body!))
|
rlm@232
|
454 touch (touch! the-worm)
|
rlm@232
|
455 touch-display (view-touch)]
|
rlm@232
|
456 (world (nodify [the-worm (floor)])
|
rlm@232
|
457 standard-debug-controls
|
rlm@232
|
458
|
rlm@232
|
459 (fn [world]
|
rlm@244
|
460 (speed-up world)
|
rlm@232
|
461 (light-up-everything world))
|
rlm@232
|
462
|
rlm@232
|
463 (fn [world tpf]
|
rlm@244
|
464 (touch-display (map #(% (.getRootNode world)) touch))
|
rlm@243
|
465 ))))
|
rlm@232
|
466 #+end_src
|
rlm@228
|
467 * Source Listing
|
rlm@228
|
468 * Next
|
rlm@228
|
469
|
rlm@228
|
470
|
rlm@226
|
471 * COMMENT Code Generation
|
rlm@39
|
472 #+begin_src clojure :tangle ../src/cortex/touch.clj
|
rlm@231
|
473 <<touch-header>>
|
rlm@231
|
474 <<meta-data>>
|
rlm@231
|
475 <<triangles-1>>
|
rlm@231
|
476 <<triangles-3>>
|
rlm@231
|
477 <<triangles-4>>
|
rlm@231
|
478 <<sensors>>
|
rlm@231
|
479 <<kernel>>
|
rlm@231
|
480 <<visualization>>
|
rlm@0
|
481 #+end_src
|
rlm@0
|
482
|
rlm@232
|
483
|
rlm@68
|
484 #+begin_src clojure :tangle ../src/cortex/test/touch.clj
|
rlm@232
|
485 <<test-touch>>
|
rlm@39
|
486 #+end_src
|
rlm@39
|
487
|
rlm@0
|
488
|
rlm@0
|
489
|
rlm@0
|
490
|
rlm@32
|
491
|
rlm@32
|
492
|
rlm@226
|
493
|