Mercurial > cortex
diff org/touch.org @ 247:4e220c8fb1ed
first pass at rough draft complete
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 12 Feb 2012 15:46:01 -0700 |
parents | 63da037ce1c5 |
children | 267add63b168 |
line wrap: on
line diff
1.1 --- a/org/touch.org Sun Feb 12 14:40:58 2012 -0700 1.2 +++ b/org/touch.org Sun Feb 12 15:46:01 2012 -0700 1.3 @@ -20,33 +20,34 @@ 1.4 can integrate a vast expanse of skin (i.e. your entire palm), or a 1.5 tiny patch of skin at the tip of your finger. The hairs of the skin 1.6 help detect objects before they even come into contact with the skin 1.7 -proper. 1.8 +proper. 1.9 1.10 Instead of measuring deformation or vibration, I surround each rigid 1.11 -part with a plenitude of hair-like objects which do not interact with 1.12 -the physical world. Physical objects can pass through them with no 1.13 -effect. The hairs are able to measure contact with other objects, and 1.14 -constantly report how much of their extent is covered. So, even though 1.15 -the creature's body parts do not deform, the hairs create a margin 1.16 -around those body parts which achieves a sense of touch which is a 1.17 -hybrid between a human's sense of deformation and sense from hairs. 1.18 +part with a plenitude of hair-like objects (/feelers/) which do not 1.19 +interact with the physical world. Physical objects can pass through 1.20 +them with no effect. The feelers are able to measure contact with 1.21 +other objects, and constantly report how much of their extent is 1.22 +covered. So, even though the creature's body parts do not deform, the 1.23 +feelers create a margin around those body parts which achieves a sense 1.24 +of touch which is a hybrid between a human's sense of deformation and 1.25 +sense from hairs. 1.26 1.27 Implementing touch in jMonkeyEngine follows a different techinal route 1.28 than vision and hearing. Those two senses piggybacked off 1.29 jMonkeyEngine's 3D audio and video rendering subsystems. To simulate 1.30 -Touch, I use jMonkeyEngine's physics system to execute many small 1.31 -collision detections, one for each "hair". The placement of the 1.32 -"hairs" is determined by a UV-mapped image which shows where each hair 1.33 -should be on the 3D surface of the body. 1.34 +touch, I use jMonkeyEngine's physics system to execute many small 1.35 +collision detections, one for each feeler. The placement of the 1.36 +feelers is determined by a UV-mapped image which shows where each 1.37 +feeler should be on the 3D surface of the body. 1.38 1.39 * Defining Touch Meta-Data in Blender 1.40 1.41 Each geometry can have a single UV map which describes the position of 1.42 -the "hairs" which will constitute its sense of touch. This image path 1.43 +the feelers which will constitute its sense of touch. This image path 1.44 is stored under the "touch" key. The image itself is black and white, 1.45 -with black meaning a hair length of 0 (no hair is present) and white 1.46 -meaning a hair length of =scale=, which is a float stored under the 1.47 -key "scale". I call these "hairs" /feelers/. 1.48 +with black meaning a feeler length of 0 (no feeler is present) and 1.49 +white meaning a feeler length of =scale=, which is a float stored 1.50 +under the key "scale". 1.51 1.52 #+name: meta-data 1.53 #+begin_src clojure 1.54 @@ -58,51 +59,314 @@ 1.55 (load-image image-path))) 1.56 1.57 (defn tactile-scale 1.58 - "Return the maximum length of a hair. All hairs are scalled between 1.59 - 0.0 and this length, depending on their color. Black is 0, and 1.60 - white is maximum length, and everything in between is scalled 1.61 - linearlly. Default scale is 0.01 jMonkeyEngine units." 1.62 + "Return the length of each feeler. Default scale is 0.01 1.63 + jMonkeyEngine units." 1.64 [#^Geometry obj] 1.65 (if-let [scale (meta-data obj "scale")] 1.66 scale 0.1)) 1.67 #+end_src 1.68 1.69 Here is an example of a UV-map which specifies the position of touch 1.70 -sensors along the surface of the worm. 1.71 +sensors along the surface of the upper segment of the worm. 1.72 1.73 #+attr_html: width=755 1.74 #+caption: This is the tactile-sensor-profile for the upper segment of the worm. It defines regions of high touch sensitivity (where there are many white pixels) and regions of low sensitivity (where white pixels are sparse). 1.75 [[../images/finger-UV.png]] 1.76 1.77 -* Skin Creation 1.78 +* Implementation Summary 1.79 + 1.80 +To simulate touch there are three conceptual steps. For each solid 1.81 +object in the creature, you first have to get UV image and scale 1.82 +paramater which define the position and length of the feelers. Then, 1.83 +you use the triangles which compose the mesh and the UV data stored in 1.84 +the mesh to determine the world-space position and orientation of each 1.85 +feeler. Once every frame, update these positions and orientations to 1.86 +match the current position and orientation of the object, and use 1.87 +physics collision detection to gather tactile data. 1.88 1.89 -=(touch-kernel)= generates the functions which implement the sense of 1.90 -touch for a creature. These functions must do 6 things to obtain touch 1.91 -data. 1.92 - 1.93 - - Get the tactile profile image and scale paramaters which describe 1.94 - the layout of feelers along the object's surface. 1.95 - =(tactile-sensor-profile)=, =(tactile-scale)= 1.96 +Extracting the meta-data has already been described. The third step, 1.97 +physics collision detection, is handled in =(touch-kernel)=. 1.98 +Translating the positions and orientations of the feelers from the 1.99 +UV-map to world-space is also a three-step process. 1.100 1.101 - Find the triangles which make up the mesh in pixel-space and in 1.102 - world-space. 1.103 - =(triangles)= =(pixel-triangles)= 1.104 - 1.105 - - Find the coordinates of each pixel in pixel space. These 1.106 - coordinates are used to make the touch-topology. 1.107 - =(feeler-pixel-coords)= 1.108 + world-space. =(triangles)= =(pixel-triangles)=. 1.109 1.110 - - Find the coordinates of each pixel in world-space. These 1.111 - coordinates are the origins of the feelers. =(feeler-origins)= 1.112 + - Find the coordinates of each feeler in world-space. These are the 1.113 + origins of the feelers. =(feeler-origins)=. 1.114 1.115 - Calculate the normals of the triangles in world space, and add 1.116 them to each of the origins of the feelers. These are the 1.117 - normalized coordinates of the tips of the feelers. 1.118 - For both of these, =(feeler-tips)= 1.119 + normalized coordinates of the tips of the feelers. =(feeler-tips)=. 1.120 1.121 - - Generate some sort of topology for the sensors. 1.122 - =(touch-topology)= 1.123 +* Triangle Math 1.124 +** Schrapnel Conversion Functions 1.125 1.126 +#+name: triangles-1 1.127 +#+begin_src clojure 1.128 +(defn vector3f-seq [#^Vector3f v] 1.129 + [(.getX v) (.getY v) (.getZ v)]) 1.130 + 1.131 +(defn triangle-seq [#^Triangle tri] 1.132 + [(vector3f-seq (.get1 tri)) 1.133 + (vector3f-seq (.get2 tri)) 1.134 + (vector3f-seq (.get3 tri))]) 1.135 + 1.136 +(defn ->vector3f 1.137 + ([coords] (Vector3f. (nth coords 0 0) 1.138 + (nth coords 1 0) 1.139 + (nth coords 2 0)))) 1.140 + 1.141 +(defn ->triangle [points] 1.142 + (apply #(Triangle. %1 %2 %3) (map ->vector3f points))) 1.143 +#+end_src 1.144 + 1.145 +It is convienent to treat a =Triangle= as a sequence of verticies, and 1.146 +a =Vector2f= and =Vector3f= as a sequence of floats. These conversion 1.147 +functions make this easy. If these classes implemented =Iterable= then 1.148 +=(seq)= would work on them automitacally. 1.149 +** Decomposing a 3D shape into Triangles 1.150 + 1.151 +The rigid bodies which make up a creature have an underlying 1.152 +=Geometry=, which is a =Mesh= plus a =Material= and other important 1.153 +data involved with displaying the body. 1.154 + 1.155 +A =Mesh= is composed of =Triangles=, and each =Triangle= has three 1.156 +verticies which have coordinates in world space and UV space. 1.157 + 1.158 +Here, =(triangles)= gets all the world-space triangles which compose a 1.159 +mesh, while =(pixel-triangles)= gets those same triangles expressed in 1.160 +pixel coordinates (which are UV coordinates scaled to fit the height 1.161 +and width of the UV image). 1.162 + 1.163 +#+name: triangles-2 1.164 +#+begin_src clojure 1.165 +(in-ns 'cortex.touch) 1.166 +(defn triangle 1.167 + "Get the triangle specified by triangle-index from the mesh." 1.168 + [#^Geometry geo triangle-index] 1.169 + (triangle-seq 1.170 + (let [scratch (Triangle.)] 1.171 + (.getTriangle (.getMesh geo) triangle-index scratch) scratch))) 1.172 + 1.173 +(defn triangles 1.174 + "Return a sequence of all the Triangles which compose a given 1.175 + Geometry." 1.176 + [#^Geometry geo] 1.177 + (map (partial triangle geo) (range (.getTriangleCount (.getMesh geo))))) 1.178 + 1.179 +(defn triangle-vertex-indices 1.180 + "Get the triangle vertex indices of a given triangle from a given 1.181 + mesh." 1.182 + [#^Mesh mesh triangle-index] 1.183 + (let [indices (int-array 3)] 1.184 + (.getTriangle mesh triangle-index indices) 1.185 + (vec indices))) 1.186 + 1.187 +(defn vertex-UV-coord 1.188 + "Get the UV-coordinates of the vertex named by vertex-index" 1.189 + [#^Mesh mesh vertex-index] 1.190 + (let [UV-buffer 1.191 + (.getData 1.192 + (.getBuffer 1.193 + mesh 1.194 + VertexBuffer$Type/TexCoord))] 1.195 + [(.get UV-buffer (* vertex-index 2)) 1.196 + (.get UV-buffer (+ 1 (* vertex-index 2)))])) 1.197 + 1.198 +(defn pixel-triangle [#^Geometry geo image index] 1.199 + (let [mesh (.getMesh geo) 1.200 + width (.getWidth image) 1.201 + height (.getHeight image)] 1.202 + (vec (map (fn [[u v]] (vector (* width u) (* height v))) 1.203 + (map (partial vertex-UV-coord mesh) 1.204 + (triangle-vertex-indices mesh index)))))) 1.205 + 1.206 +(defn pixel-triangles [#^Geometry geo image] 1.207 + (let [height (.getHeight image) 1.208 + width (.getWidth image)] 1.209 + (map (partial pixel-triangle geo image) 1.210 + (range (.getTriangleCount (.getMesh geo)))))) 1.211 +#+end_src 1.212 +** The Affine Transform from one Triangle to Another 1.213 + 1.214 +=(pixel-triangles)= gives us the mesh triangles expressed in pixel 1.215 +coordinates and =(triangles)= gives us the mesh triangles expressed in 1.216 +world coordinates. The tactile-sensor-profile gives the position of 1.217 +each feeler in pixel-space. In order to convert pixel-dpace 1.218 +coordinates into world-space coordinates we need something that takes 1.219 +coordinates on the surface of one triangle and gives the corresponding 1.220 +coordinates on the surface of another triangle. 1.221 + 1.222 +Triangles are [[http://mathworld.wolfram.com/AffineTransformation.html ][affine]], which means any triangle can be transformed into 1.223 +any other by a combination of translation, scaling, and 1.224 +rotation. jMonkeyEngine's =Matrix4f= objects can describe any affine 1.225 +transformation. The affine transformation from one triangle to another 1.226 +is readily computable if the triangle is expressed in terms of a $4x4$ 1.227 +matrix. 1.228 + 1.229 +\begin{bmatrix} 1.230 +x_1 & x_2 & x_3 & n_x \\ 1.231 +y_1 & y_2 & y_3 & n_y \\ 1.232 +z_1 & z_2 & z_3 & n_z \\ 1.233 +1 & 1 & 1 & 1 1.234 +\end{bmatrix} 1.235 + 1.236 +Here, the first three columns of the matrix are the verticies of the 1.237 +triangle. The last column is the right-handed unit normal of the 1.238 +triangle. 1.239 + 1.240 +With two triangles $T_{1}$ and $T_{2}$ each expressed as a matrix like 1.241 +above, the affine transform from $T_{1}$ to $T_{2}$ is 1.242 + 1.243 +$T_{2}T_{1}^{-1}$ 1.244 + 1.245 +The clojure code below recaptiulates the formulas above. 1.246 + 1.247 +#+name: triangles-3 1.248 +#+begin_src clojure 1.249 +(in-ns 'cortex.touch) 1.250 + 1.251 +(defn triangle->matrix4f 1.252 + "Converts the triangle into a 4x4 matrix: The first three columns 1.253 + contain the vertices of the triangle; the last contains the unit 1.254 + normal of the triangle. The bottom row is filled with 1s." 1.255 + [#^Triangle t] 1.256 + (let [mat (Matrix4f.) 1.257 + [vert-1 vert-2 vert-3] 1.258 + ((comp vec map) #(.get t %) (range 3)) 1.259 + unit-normal (do (.calculateNormal t)(.getNormal t)) 1.260 + vertices [vert-1 vert-2 vert-3 unit-normal]] 1.261 + (dorun 1.262 + (for [row (range 4) col (range 3)] 1.263 + (do 1.264 + (.set mat col row (.get (vertices row) col)) 1.265 + (.set mat 3 row 1)))) mat)) 1.266 + 1.267 +(defn triangles->affine-transform 1.268 + "Returns the affine transformation that converts each vertex in the 1.269 + first triangle into the corresponding vertex in the second 1.270 + triangle." 1.271 + [#^Triangle tri-1 #^Triangle tri-2] 1.272 + (.mult 1.273 + (triangle->matrix4f tri-2) 1.274 + (.invert (triangle->matrix4f tri-1)))) 1.275 +#+end_src 1.276 +** Triangle Boundaries 1.277 + 1.278 +For efficiency's sake I will divide the tactile-profile image into 1.279 +small squares which inscribe each pixel-triangle, then extract the 1.280 +points which lie inside the triangle and map them to 3D-space using 1.281 +=(triangle-transform)= above. To do this I need a function, 1.282 +=(convex-bounds)= which finds the smallest box which inscribes a 2D 1.283 +triangle. 1.284 + 1.285 +=(inside-triangle?)= determines whether a point is inside a triangle 1.286 +in 2D pixel-space. 1.287 + 1.288 +#+name: triangles-4 1.289 +#+begin_src clojure 1.290 +(defn convex-bounds 1.291 + "Returns the smallest square containing the given vertices, as a 1.292 + vector of integers [left top width height]." 1.293 + [verts] 1.294 + (let [xs (map first verts) 1.295 + ys (map second verts) 1.296 + x0 (Math/floor (apply min xs)) 1.297 + y0 (Math/floor (apply min ys)) 1.298 + x1 (Math/ceil (apply max xs)) 1.299 + y1 (Math/ceil (apply max ys))] 1.300 + [x0 y0 (- x1 x0) (- y1 y0)])) 1.301 + 1.302 +(defn same-side? 1.303 + "Given the points p1 and p2 and the reference point ref, is point p 1.304 + on the same side of the line that goes through p1 and p2 as ref is?" 1.305 + [p1 p2 ref p] 1.306 + (<= 1.307 + 0 1.308 + (.dot 1.309 + (.cross (.subtract p2 p1) (.subtract p p1)) 1.310 + (.cross (.subtract p2 p1) (.subtract ref p1))))) 1.311 + 1.312 +(defn inside-triangle? 1.313 + "Is the point inside the triangle?" 1.314 + {:author "Dylan Holmes"} 1.315 + [#^Triangle tri #^Vector3f p] 1.316 + (let [[vert-1 vert-2 vert-3] [(.get1 tri) (.get2 tri) (.get3 tri)]] 1.317 + (and 1.318 + (same-side? vert-1 vert-2 vert-3 p) 1.319 + (same-side? vert-2 vert-3 vert-1 p) 1.320 + (same-side? vert-3 vert-1 vert-2 p)))) 1.321 +#+end_src 1.322 + 1.323 +* Feeler Coordinates 1.324 + 1.325 +The triangle-related functions above make short work of calculating 1.326 +the positions and orientations of each feeler in world-space. 1.327 + 1.328 +#+name: sensors 1.329 +#+begin_src clojure 1.330 +(in-ns 'cortex.touch) 1.331 + 1.332 +(defn feeler-pixel-coords 1.333 + "Returns the coordinates of the feelers in pixel space in lists, one 1.334 + list for each triangle, ordered in the same way as (triangles) and 1.335 + (pixel-triangles)." 1.336 + [#^Geometry geo image] 1.337 + (map 1.338 + (fn [pixel-triangle] 1.339 + (filter 1.340 + (fn [coord] 1.341 + (inside-triangle? (->triangle pixel-triangle) 1.342 + (->vector3f coord))) 1.343 + (white-coordinates image (convex-bounds pixel-triangle)))) 1.344 + (pixel-triangles geo image))) 1.345 + 1.346 +(defn feeler-world-coords 1.347 + "Returns the coordinates of the feelers in world space in lists, one 1.348 + list for each triangle, ordered in the same way as (triangles) and 1.349 + (pixel-triangles)." 1.350 + [#^Geometry geo image] 1.351 + (let [transforms 1.352 + (map #(triangles->affine-transform 1.353 + (->triangle %1) (->triangle %2)) 1.354 + (pixel-triangles geo image) 1.355 + (triangles geo))] 1.356 + (map (fn [transform coords] 1.357 + (map #(.mult transform (->vector3f %)) coords)) 1.358 + transforms (feeler-pixel-coords geo image)))) 1.359 + 1.360 +(defn feeler-origins 1.361 + "The world space coordinates of the root of each feeler." 1.362 + [#^Geometry geo image] 1.363 + (reduce concat (feeler-world-coords geo image))) 1.364 + 1.365 +(defn feeler-tips 1.366 + "The world space coordinates of the tip of each feeler." 1.367 + [#^Geometry geo image] 1.368 + (let [world-coords (feeler-world-coords geo image) 1.369 + normals 1.370 + (map 1.371 + (fn [triangle] 1.372 + (.calculateNormal triangle) 1.373 + (.clone (.getNormal triangle))) 1.374 + (map ->triangle (triangles geo)))] 1.375 + 1.376 + (mapcat (fn [origins normal] 1.377 + (map #(.add % normal) origins)) 1.378 + world-coords normals))) 1.379 + 1.380 +(defn touch-topology 1.381 + "touch-topology? is not a function." 1.382 + [#^Geometry geo image] 1.383 + (collapse (reduce concat (feeler-pixel-coords geo image)))) 1.384 +#+end_src 1.385 +* Simulated Touch 1.386 + 1.387 +=(touch-kernel)= generates functions to be called from within a 1.388 +simulation that perform the necessary physics collisions to collect 1.389 +tactile data, and =(touch!)= recursively applies it to every node in 1.390 +the creature. 1.391 1.392 #+name: kernel 1.393 #+begin_src clojure 1.394 @@ -163,61 +427,8 @@ 1.395 (node-seq creature))))) 1.396 #+end_src 1.397 1.398 -* Sensor Related Functions 1.399 +* Visualizing Touch 1.400 1.401 -These functions analyze the touch-sensor-profile image convert the 1.402 -location of each touch sensor from pixel coordinates to UV-coordinates 1.403 -and XYZ-coordinates. 1.404 - 1.405 -#+name: sensors 1.406 -#+begin_src clojure 1.407 -(in-ns 'cortex.touch) 1.408 - 1.409 -(defn feeler-pixel-coords 1.410 - "Returns the coordinates of the feelers in pixel space in lists, one 1.411 - list for each triangle, ordered in the same way as (triangles) and 1.412 - (pixel-triangles)." 1.413 - [#^Geometry geo image] 1.414 - (map 1.415 - (fn [pixel-triangle] 1.416 - (filter 1.417 - (fn [coord] 1.418 - (inside-triangle? (->triangle pixel-triangle) 1.419 - (->vector3f coord))) 1.420 - (white-coordinates image (convex-bounds pixel-triangle)))) 1.421 - (pixel-triangles geo image))) 1.422 - 1.423 -(defn feeler-world-coords [#^Geometry geo image] 1.424 - (let [transforms 1.425 - (map #(triangles->affine-transform 1.426 - (->triangle %1) (->triangle %2)) 1.427 - (pixel-triangles geo image) 1.428 - (triangles geo))] 1.429 - (map (fn [transform coords] 1.430 - (map #(.mult transform (->vector3f %)) coords)) 1.431 - transforms (feeler-pixel-coords geo image)))) 1.432 - 1.433 -(defn feeler-origins [#^Geometry geo image] 1.434 - (reduce concat (feeler-world-coords geo image))) 1.435 - 1.436 -(defn feeler-tips [#^Geometry geo image] 1.437 - (let [world-coords (feeler-world-coords geo image) 1.438 - normals 1.439 - (map 1.440 - (fn [triangle] 1.441 - (.calculateNormal triangle) 1.442 - (.clone (.getNormal triangle))) 1.443 - (map ->triangle (triangles geo)))] 1.444 - 1.445 - (mapcat (fn [origins normal] 1.446 - (map #(.add % normal) origins)) 1.447 - world-coords normals))) 1.448 - 1.449 -(defn touch-topology [#^Geometry geo image] 1.450 - (collapse (reduce concat (feeler-pixel-coords geo image)))) 1.451 -#+end_src 1.452 - 1.453 -* Visualizing Touch 1.454 #+name: visualization 1.455 #+begin_src clojure 1.456 (in-ns 'cortex.touch) 1.457 @@ -239,205 +450,6 @@ 1.458 (.setRGB image ((coords i) 0) ((coords i) 1) 1.459 (apply touch->gray (sensor-data i))))) image)))) 1.460 #+end_src 1.461 - 1.462 - 1.463 - 1.464 -* Triangle Manipulation Functions 1.465 - 1.466 -The rigid bodies which make up a creature have an underlying 1.467 -=Geometry=, which is a =Mesh= plus a =Material= and other important 1.468 -data involved with displaying the body. 1.469 - 1.470 -A =Mesh= is composed of =Triangles=, and each =Triangle= has three 1.471 -verticies which have coordinates in XYZ space and UV space. 1.472 - 1.473 -Here, =(triangles)= gets all the triangles which compose a mesh, and 1.474 -=(triangle-UV-coord)= returns the the UV coordinates of the verticies 1.475 -of a triangle. 1.476 - 1.477 -#+name: triangles-1 1.478 -#+begin_src clojure 1.479 -(in-ns 'cortex.touch) 1.480 - 1.481 -(defn vector3f-seq [#^Vector3f v] 1.482 - [(.getX v) (.getY v) (.getZ v)]) 1.483 - 1.484 -(defn triangle-seq [#^Triangle tri] 1.485 - [(vector3f-seq (.get1 tri)) 1.486 - (vector3f-seq (.get2 tri)) 1.487 - (vector3f-seq (.get3 tri))]) 1.488 - 1.489 -(defn ->vector3f 1.490 - ([coords] (Vector3f. (nth coords 0 0) 1.491 - (nth coords 1 0) 1.492 - (nth coords 2 0)))) 1.493 - 1.494 -(defn ->triangle [points] 1.495 - (apply #(Triangle. %1 %2 %3) (map ->vector3f points))) 1.496 - 1.497 -(defn triangle 1.498 - "Get the triangle specified by triangle-index from the mesh." 1.499 - [#^Geometry geo triangle-index] 1.500 - (triangle-seq 1.501 - (let [scratch (Triangle.)] 1.502 - (.getTriangle (.getMesh geo) triangle-index scratch) scratch))) 1.503 - 1.504 -(defn triangles 1.505 - "Return a sequence of all the Triangles which compose a given 1.506 - Geometry." 1.507 - [#^Geometry geo] 1.508 - (map (partial triangle geo) (range (.getTriangleCount (.getMesh geo))))) 1.509 - 1.510 -(defn triangle-vertex-indices 1.511 - "Get the triangle vertex indices of a given triangle from a given 1.512 - mesh." 1.513 - [#^Mesh mesh triangle-index] 1.514 - (let [indices (int-array 3)] 1.515 - (.getTriangle mesh triangle-index indices) 1.516 - (vec indices))) 1.517 - 1.518 -(defn vertex-UV-coord 1.519 - "Get the UV-coordinates of the vertex named by vertex-index" 1.520 - [#^Mesh mesh vertex-index] 1.521 - (let [UV-buffer 1.522 - (.getData 1.523 - (.getBuffer 1.524 - mesh 1.525 - VertexBuffer$Type/TexCoord))] 1.526 - [(.get UV-buffer (* vertex-index 2)) 1.527 - (.get UV-buffer (+ 1 (* vertex-index 2)))])) 1.528 - 1.529 -(defn pixel-triangle [#^Geometry geo image index] 1.530 - (let [mesh (.getMesh geo) 1.531 - width (.getWidth image) 1.532 - height (.getHeight image)] 1.533 - (vec (map (fn [[u v]] (vector (* width u) (* height v))) 1.534 - (map (partial vertex-UV-coord mesh) 1.535 - (triangle-vertex-indices mesh index)))))) 1.536 - 1.537 -(defn pixel-triangles [#^Geometry geo image] 1.538 - (let [height (.getHeight image) 1.539 - width (.getWidth image)] 1.540 - (map (partial pixel-triangle geo image) 1.541 - (range (.getTriangleCount (.getMesh geo)))))) 1.542 - 1.543 -#+end_src 1.544 - 1.545 -* Triangle Affine Transforms 1.546 - 1.547 -The position of each hair is stored in a 2D image in UV 1.548 -coordinates. To place the hair in 3D space we must convert from UV 1.549 -coordinates to XYZ coordinates. Each =Triangle= has coordinates in 1.550 -both UV-space and XYZ-space, which defines a unique [[http://mathworld.wolfram.com/AffineTransformation.html ][Affine Transform]] 1.551 -for translating any coordinate within the UV triangle to the 1.552 -cooresponding coordinate in the XYZ triangle. 1.553 - 1.554 -#+name: triangles-3 1.555 -#+begin_src clojure 1.556 -(in-ns 'cortex.touch) 1.557 - 1.558 -(defn triangle->matrix4f 1.559 - "Converts the triangle into a 4x4 matrix: The first three columns 1.560 - contain the vertices of the triangle; the last contains the unit 1.561 - normal of the triangle. The bottom row is filled with 1s." 1.562 - [#^Triangle t] 1.563 - (let [mat (Matrix4f.) 1.564 - [vert-1 vert-2 vert-3] 1.565 - ((comp vec map) #(.get t %) (range 3)) 1.566 - unit-normal (do (.calculateNormal t)(.getNormal t)) 1.567 - vertices [vert-1 vert-2 vert-3 unit-normal]] 1.568 - (dorun 1.569 - (for [row (range 4) col (range 3)] 1.570 - (do 1.571 - (.set mat col row (.get (vertices row)col)) 1.572 - (.set mat 3 row 1)))) mat)) 1.573 - 1.574 -(defn triangles->affine-transform 1.575 - "Returns the affine transformation that converts each vertex in the 1.576 - first triangle into the corresponding vertex in the second 1.577 - triangle." 1.578 - [#^Triangle tri-1 #^Triangle tri-2] 1.579 - (.mult 1.580 - (triangle->matrix4f tri-2) 1.581 - (.invert (triangle->matrix4f tri-1)))) 1.582 -#+end_src 1.583 - 1.584 - 1.585 -* Schrapnel Conversion Functions 1.586 - 1.587 -It is convienent to treat a =Triangle= as a sequence of verticies, and 1.588 -a =Vector2f= and =Vector3f= as a sequence of floats. These conversion 1.589 -functions make this easy. If these classes implemented =Iterable= then 1.590 -this code would not be necessary. Hopefully they will in the future. 1.591 - 1.592 -* Triangle Boundaries 1.593 - 1.594 -For efficiency's sake I will divide the UV-image into small squares 1.595 -which inscribe each UV-triangle, then extract the points which lie 1.596 -inside the triangle and map them to 3D-space using 1.597 -=(triangle-transform)= above. To do this I need a function, 1.598 -=(inside-triangle?)=, which determines whether a point is inside a 1.599 -triangle in 2D UV-space. 1.600 - 1.601 -#+name: triangles-4 1.602 -#+begin_src clojure 1.603 -(defn convex-bounds 1.604 - "Returns the smallest square containing the given vertices, as a 1.605 - vector of integers [left top width height]." 1.606 - [verts] 1.607 - (let [xs (map first verts) 1.608 - ys (map second verts) 1.609 - x0 (Math/floor (apply min xs)) 1.610 - y0 (Math/floor (apply min ys)) 1.611 - x1 (Math/ceil (apply max xs)) 1.612 - y1 (Math/ceil (apply max ys))] 1.613 - [x0 y0 (- x1 x0) (- y1 y0)])) 1.614 - 1.615 -(defn same-side? 1.616 - "Given the points p1 and p2 and the reference point ref, is point p 1.617 - on the same side of the line that goes through p1 and p2 as ref is?" 1.618 - [p1 p2 ref p] 1.619 - (<= 1.620 - 0 1.621 - (.dot 1.622 - (.cross (.subtract p2 p1) (.subtract p p1)) 1.623 - (.cross (.subtract p2 p1) (.subtract ref p1))))) 1.624 - 1.625 -(defn inside-triangle? 1.626 - "Is the point inside the triangle?" 1.627 - {:author "Dylan Holmes"} 1.628 - [#^Triangle tri #^Vector3f p] 1.629 - (let [[vert-1 vert-2 vert-3] [(.get1 tri) (.get2 tri) (.get3 tri)]] 1.630 - (and 1.631 - (same-side? vert-1 vert-2 vert-3 p) 1.632 - (same-side? vert-2 vert-3 vert-1 p) 1.633 - (same-side? vert-3 vert-1 vert-2 p)))) 1.634 -#+end_src 1.635 - 1.636 -* Physics Collision Objects 1.637 - 1.638 -The "hairs" are actually =Rays= which extend from a point on a 1.639 -=Triangle= in the =Mesh= normal to the =Triangle's= surface. 1.640 - 1.641 -* Headers 1.642 - 1.643 -#+name: touch-header 1.644 -#+begin_src clojure 1.645 -(ns cortex.touch 1.646 - "Simulate the sense of touch in jMonkeyEngine3. Enables any Geometry 1.647 - to be outfitted with touch sensors with density determined by a UV 1.648 - image. In this way a Geometry can know what parts of itself are 1.649 - touching nearby objects. Reads specially prepared blender files to 1.650 - construct this sense automatically." 1.651 - {:author "Robert McIntyre"} 1.652 - (:use (cortex world util sense)) 1.653 - (:use clojure.contrib.def) 1.654 - (:import (com.jme3.scene Geometry Node Mesh)) 1.655 - (:import com.jme3.collision.CollisionResults) 1.656 - (:import com.jme3.scene.VertexBuffer$Type) 1.657 - (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f))) 1.658 -#+end_src 1.659 - 1.660 * Adding Touch to the Worm 1.661 1.662 #+name: test-touch 1.663 @@ -463,6 +475,26 @@ 1.664 (touch-display 1.665 (map #(% (.getRootNode world)) touch)))))) 1.666 #+end_src 1.667 + 1.668 +* Headers 1.669 + 1.670 +#+name: touch-header 1.671 +#+begin_src clojure 1.672 +(ns cortex.touch 1.673 + "Simulate the sense of touch in jMonkeyEngine3. Enables any Geometry 1.674 + to be outfitted with touch sensors with density determined by a UV 1.675 + image. In this way a Geometry can know what parts of itself are 1.676 + touching nearby objects. Reads specially prepared blender files to 1.677 + construct this sense automatically." 1.678 + {:author "Robert McIntyre"} 1.679 + (:use (cortex world util sense)) 1.680 + (:use clojure.contrib.def) 1.681 + (:import (com.jme3.scene Geometry Node Mesh)) 1.682 + (:import com.jme3.collision.CollisionResults) 1.683 + (:import com.jme3.scene.VertexBuffer$Type) 1.684 + (:import (com.jme3.math Triangle Vector3f Vector2f Ray Matrix4f))) 1.685 +#+end_src 1.686 + 1.687 * Source Listing 1.688 * Next 1.689 1.690 @@ -472,6 +504,7 @@ 1.691 <<touch-header>> 1.692 <<meta-data>> 1.693 <<triangles-1>> 1.694 +<<triangles-2>> 1.695 <<triangles-3>> 1.696 <<triangles-4>> 1.697 <<sensors>>