Mercurial > cortex
changeset 0:92f8d83b5d0b
initial import: I've made hearing and vision, and am working on touch.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 16 Oct 2011 05:12:19 -0700 |
parents | |
children | 4588025678b0 |
files | .hgignore assets/Sounds/dream.wav assets/Sounds/pure.wav assets/Sounds/silence.wav assets/Textures/BronzeCopper030.jpg assets/Textures/PokeCopper.jpg assets/Textures/purpleWisp.png assets/Textures/redWisp.png org/body.org org/capture-video.org org/cortex.org org/skin.org |
diffstat | 12 files changed, 2734 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Sun Oct 16 05:12:19 2011 -0700 1.3 @@ -0,0 +1,4 @@ 1.4 +syntax: glob 1.5 +video* 1.6 +src* 1.7 +html*
2.1 Binary file assets/Sounds/dream.wav has changed
3.1 Binary file assets/Sounds/pure.wav has changed
4.1 Binary file assets/Sounds/silence.wav has changed
5.1 Binary file assets/Textures/BronzeCopper030.jpg has changed
6.1 Binary file assets/Textures/PokeCopper.jpg has changed
7.1 Binary file assets/Textures/purpleWisp.png has changed
8.1 Binary file assets/Textures/redWisp.png has changed
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/org/body.org Sun Oct 16 05:12:19 2011 -0700 9.3 @@ -0,0 +1,37 @@ 9.4 +#+title: The BODY!!! 9.5 +#+author: Robert McIntyre 9.6 +#+email: rlm@mit.edu 9.7 +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js" 9.8 +#+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/> 9.9 +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes 9.10 +#+INCLUDE: ../aurellem/src/templates/level-0.org 9.11 +#+description: Simulating a body (movement, tough, propioception) in jMonkeyEngine3. 9.12 + 9.13 + 9.14 +* Body ! 9.15 + 9.16 +#+srcname: body-main 9.17 +#+begin_src clojure 9.18 +(ns body.body) 9.19 +(use 'cortex.world) 9.20 +(use 'cortex.import) 9.21 +(use 'clojure.contrib.def) 9.22 +(cortex.import/mega-import-jme3) 9.23 +(rlm.rlm-commands/help) 9.24 + 9.25 + 9.26 + 9.27 +#+end_src 9.28 + 9.29 + 9.30 + 9.31 + 9.32 + 9.33 + 9.34 + 9.35 + 9.36 +* COMMENT generate Source. 9.37 +#+begin_src clojure :tangle ../src/body/body.clj 9.38 +<<body-main>> 9.39 +#+end_src 9.40 +
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/org/capture-video.org Sun Oct 16 05:12:19 2011 -0700 10.3 @@ -0,0 +1,633 @@ 10.4 +#+title: Capture Live Video Feeds from JMonkeyEngine 10.5 +#+author: Robert McIntyre 10.6 +#+email: rlm@mit.edu 10.7 +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js" 10.8 +#+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/> 10.9 +#+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t 10.10 +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes 10.11 +#+description: Capture video from a JMonkeyEngine3 Application with Xuggle, and use gstreamer to compress the video to upload to YouTube. 10.12 +#+keywords: JME3, video, Xuggle, JMonkeyEngine, youtube, capture video, Java 10.13 +#+INCLUDE: ../aurellem/src/templates/level-0.org 10.14 +:PROPERTIES: 10.15 +:EXPORT_FILE_NAME: ../whatever.html 10.16 +:END: 10.17 + 10.18 + 10.19 +* The Problem 10.20 +So you've made your cool new JMonkeyEngine3 game and you want to 10.21 +create a demo video to show off your hard work. Screen capturing is 10.22 +the most straightforward way to do this, but it can slow down your 10.23 +game and produce low-quality video as a result. A better way is to 10.24 +record a video feed directly from the game while it is 10.25 +running. 10.26 + 10.27 +In this post, I'll explain how you can alter your JMonkeyEngine3 game 10.28 +to output video while it is running. The main trick is to alter the 10.29 +pace of JMonkeyEngine3's in-game time: we allow the engine as much 10.30 +time as it needs to compute complicated in-game events and to encode 10.31 +video frames. As a result, the game appears to speed up and slow down 10.32 +as the computational demands shift, but the end result is perfectly 10.33 +smooth video output at a constant framerate. 10.34 + 10.35 + 10.36 +* Game-time vs. User-time vs. Video-time 10.37 + 10.38 +A standard JME3 application that extends =SimpleApplication= or 10.39 +=Application= tries as hard as it can to keep in sync with 10.40 +/user-time/. If a ball is rolling at 1 game-mile per game-hour in the 10.41 +game, and you wait for one user-hour as measured by the clock on your 10.42 +wall, then the ball should have traveled exactly one game-mile. In 10.43 +order to keep sync with the real world, the game throttles its physics 10.44 +engine and graphics display. If the computations involved in running 10.45 +the game are too intense, then the game will first skip frames, then 10.46 +sacrifice physics accuracy. If there are particuraly demanding 10.47 +computations, then you may only get 1 fps, and the ball may tunnel 10.48 +through the floor or obstacles due to inaccurate physics simulation, 10.49 +but after the end of one user-hour, that ball will have traveled one 10.50 +game-mile. 10.51 + 10.52 +When we're recording video, we don't care if the game-time syncs with 10.53 +user-time, but instead whether the time in the recorded video 10.54 +(video-time) syncs with user-time. To continue the analogy, if we 10.55 +recorded the ball rolling at 1 game-mile per game-hour and watched the 10.56 +video later, we would want to see 30 fps video of the ball rolling at 10.57 +1 video-mile per /user-hour/. It doesn't matter how much user-time it 10.58 +took to simulate that hour of game-time to make the high-quality 10.59 +recording. 10.60 + 10.61 +* COMMENT Two examples to clarify the point: 10.62 +** Recording from a Simple Simulation 10.63 + 10.64 +*** Without a Special Timer 10.65 +You have a simulation of a ball rolling on an infinite empty plane at 10.66 +one game-mile per game-hour, and a really good computer. Normally, 10.67 +JME3 will throttle the physics engine and graphics display to sync the 10.68 +game-time with user-time. If it takes one-thousandth of a second 10.69 +user-time to simulate one-sixtieth of a second game time and another 10.70 +one-thousandth of a second to draw to the screen, then JME3 will just 10.71 +sit around for the remainder of $\frac{1}{60} - \frac{2}{1000}$ 10.72 +user-seconds, then calculate the next frame in $\frac{2}{1000}$ 10.73 +user-seconds, then wait, and so on. For every second of user time that 10.74 +passes, one second of game-time passes, and the game will run at 60 10.75 +frames per user-second. 10.76 + 10.77 + 10.78 +*** With a Special Timer 10.79 +Then, you change the game's timer so that user-time will be synced to 10.80 +video-time. Assume that encoding a single frame takes 0 seconds 10.81 +user-time to complete. 10.82 + 10.83 +Now, JME3 takes advantage of all available resources. It still takes 10.84 +one-thousandth of a second to calculate a physics tick, and another 10.85 +one-thousandth to render to the screen. Then it takes 0 seconds to 10.86 +write the video frame to disk and encode the video. In only one second 10.87 +of user time, JME3 will complete 500 physics-tick/render/encode-video 10.88 +cycles, and $\frac{500}{60}=8\frac{1}{3}$ seconds of game-time will 10.89 +have passed. Game-time appears to dilate $8\frac{1}{3}\times$ with 10.90 +respect to user-time, and in only 7.2 minutes user-time, one hour of 10.91 +video will have been recorded. The game itself will run at 500 fps. 10.92 +When someone watches the video, they will see 60 frames per 10.93 +user-second, and $\frac{1}{60}$ video-seconds will pass each frame. It 10.94 +will take exactly one hour user-time (and one hour video-time) for the 10.95 +ball in the video to travel one video-mile. 10.96 + 10.97 +** Recording from a Complex Simulation 10.98 + 10.99 +*** Without a Special Timer 10.100 +You have a simulation of a ball rolling on an infinite empty plane at 10.101 +one game-mile per game-hour accompanied by multiple explosions 10.102 +involving thousands of nodes, particle effects, and complicated shadow 10.103 +shaders to create realistic shadows. You also have a slow 10.104 +laptop. Normally, JME3 must sacrifice rendering and physics simulation 10.105 +to try to keep up. If it takes $\frac{1}{120}$ of a user-second to 10.106 +calculate $\frac{1}{60}$ game-seconds, and an additional 10.107 +$\frac{1}{60}$ of a user-second to render to screen, then JME3 has 10.108 +it's work cut out for it. In order to render to the screen, it will 10.109 +first step the game forward by up to four physics ticks before 10.110 +rendering to the screen. If it still isn't fast enough then it will 10.111 +decrease the accuracy of the physics engine until game-time and user 10.112 +time are synched or a certain threshold is reached, at which point the 10.113 +game visibly slows down. In this case, JME3 continuously repeat a 10.114 +cycle of two physics ticks, and one screen render. For every 10.115 +user-second that passes, one game-second will pass, but the game will 10.116 +run at 30 fps instead of 60 fps like before. 10.117 + 10.118 +*** With a Special Timer 10.119 +Then, you change the game's timer so that user-time will be synced to 10.120 +video-time. Once again, assume video encoding takes $\frac{1}{60}$ of 10.121 +a user-second. 10.122 + 10.123 +Now, JME3 will spend $\frac{1}{120}$ of a user-second to step the 10.124 +physics tick $\frac{1}{60}$ game-seconds, $\frac{1}{60}$ to draw to 10.125 +the screen, and an additional $\frac{1}{60}$ to encode the video and 10.126 +write the frame to disk. This is a total of $\frac{1}{24}$ 10.127 +user-seconds for each $\frac{1}{60}$ game-seconds. It will take 10.128 +$(\frac{60}{24} = 2.5)$ user-hours to record one game-hour and game-time 10.129 +will appear to flow two-fifths as fast as user time while the game is 10.130 +running. However, just as in example one, when all is said and done we 10.131 +will have an hour long video at 60 fps. 10.132 + 10.133 + 10.134 +* COMMENT proposed names for the new timer 10.135 +# METRONOME 10.136 +# IsoTimer 10.137 +# EvenTimer 10.138 +# PulseTimer 10.139 +# FixedTimer 10.140 +# RigidTimer 10.141 +# FixedTempo 10.142 +# RegularTimer 10.143 +# MetronomeTimer 10.144 +# ConstantTimer 10.145 +# SteadyTimer 10.146 + 10.147 + 10.148 +* =IsoTimer= records time like a metronome 10.149 + 10.150 +The easiest way to achieve this special timing is to create a new 10.151 +timer that always reports the same framerate to JME3 every time it is 10.152 +called. 10.153 + 10.154 + 10.155 +=./jme3/src/core/com/jme3/system/IsoTimer.java= 10.156 +#+include ./jme3/src/core/com/jme3/system/IsoTimer.java src java 10.157 + 10.158 +If an Application uses this =IsoTimer= instead of the normal one, we 10.159 +can be sure that every call to =simpleUpdate=, for example, corresponds 10.160 +to exactly $(\frac{1}{fps})$ seconds of game-time. 10.161 + 10.162 +In order to facilitate setting the =Timer= in user code, I added 10.163 +getter and setter methods to =Application.java=. 10.164 + 10.165 +In =./jme3/src/core/com/jme3/app/Application.java= I added: 10.166 +#+include ./jme3/src/core/com/jme3/app/Application.java src java :lines "340-356" 10.167 + 10.168 +* Encoding to Video 10.169 + 10.170 +Now that the issue of time is solved, we just need a function that 10.171 +writes each frame to a video. We can put this function somewhere 10.172 +where it will be called exactly one per frame. 10.173 + 10.174 +JME3 already provides exactly the class we need: the =SceneProcessor= 10.175 +class can be attached to any viewport and the methods defined therein 10.176 +will be called at the appropriate points in the rendering process. 10.177 + 10.178 +If you want to generate video from Java, a great option is [[http://www.xuggle.com/][Xuggle]]. It 10.179 +takes care of everything related to video encoding and decoding and 10.180 +runs on Windows, Linux and Mac. Out of all the video frameworks for 10.181 +Java I personally like this one the best. 10.182 + 10.183 +Here is a =SceneProcessor= that uses [[http://www.xuggle.com/][Xuggle]] to write each frame to a 10.184 +video file. 10.185 + 10.186 +=./jme3/src/core/com/jme3/app/VideoProcessor.java= 10.187 +#+include ./jme3/src/core/com/jme3/app/VideoProcessor.java src java 10.188 + 10.189 +With this, we are able to record video! 10.190 + 10.191 +* Hello Video! 10.192 + 10.193 +I've taken [[http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/helloworld/HelloLoop.java][=./jme3/src/test/jme3test/helloworld/HelloLoop.java=]] and 10.194 +augmented it with video output as follows: 10.195 + 10.196 +=./jme3/src/test/jme3test/helloworld/HelloVideo.java= 10.197 +#+include ./jme3/src/test/jme3test/helloworld/HelloVideo.java src java 10.198 + 10.199 +The videos are created in the =hello-video= directory 10.200 + 10.201 +#+begin_src sh :results verbatim 10.202 +du -h hello-video/* 10.203 +#+end_src 10.204 + 10.205 +#+results: 10.206 +: 932K hello-video/hello-video-moving.flv 10.207 +: 640K hello-video/hello-video-static.flv 10.208 + 10.209 +And can be immediately uploaded to youtube 10.210 + 10.211 +- [[http://www.youtube.com/watch?v=C8gxVAySaPg][hello-video-moving.flv]] 10.212 +#+BEGIN_HTML 10.213 +<iframe width="425" height="349" 10.214 + src="http://www.youtube.com/embed/C8gxVAySaPg" 10.215 + frameborder="0" allowfullscreen> 10.216 +</iframe> 10.217 +#+END_HTML 10.218 +- [[http://www.youtube.com/watch?v=pHcFOtIS07Q][hello-video-static.flv]] 10.219 +#+BEGIN_HTML 10.220 +<iframe width="425" height="349" 10.221 + src="http://www.youtube.com/embed/pHcFOtIS07Q" 10.222 + frameborder="0" allowfullscreen> 10.223 +</iframe> 10.224 + 10.225 +#+END_HTML 10.226 + 10.227 + 10.228 + 10.229 +* Summary 10.230 +It's quite easy to augment your own application to record video, 10.231 +almost regardless of how complicated the actual application is. You 10.232 +can also record from multiple ViewPorts as the above example shows. 10.233 + 10.234 +The process for adding video recording to your application is as 10.235 +follows: 10.236 + 10.237 +Assuming you want to record at 30 fps, add: 10.238 + 10.239 +#+begin_src java :exports code 10.240 +this.setTimer(new IsoTimer(30)); 10.241 +#+end_src 10.242 + 10.243 +Somewhere in the initialization of your Application. Right now, you 10.244 +will have to add the =setTimer= method to =Application=, but hopefully 10.245 +this method will be included soon by the JMonkeyEngine3 team. 10.246 + 10.247 +Then, you create a =VideoProcessor= object and attach it to the 10.248 +=ViewPort= from which you want to record. 10.249 + 10.250 +If you want to record from the game's main =ViewPort= to a file called 10.251 +=/home/r/record.flv=, then add: 10.252 + 10.253 +#+begin_src java :exports code 10.254 +viewPort.addProcessor(new VideoProcessor(new File("/home/r/record.flv"))); 10.255 +#+end_src 10.256 + 10.257 +Do this for each =ViewPort= from which you want to record. The more 10.258 +ViewPorts from which you record, the slower the game will run, but 10.259 +this slowness will not affect the final video output. 10.260 + 10.261 +* More Examples 10.262 +** Hello Physics 10.263 +=HelloVideo= is boring. Let's add some video capturing to =HelloPhysics= 10.264 +and create something fun! 10.265 + 10.266 +This example is a modified version of =HelloPhysics= that creates four 10.267 +simultaneous views of the same scene of cannonballs careening into a 10.268 +brick wall. 10.269 + 10.270 +=./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java= 10.271 +#+include ./jme3/src/test/jme3test/helloworld/HelloPhysicsWithVideo.java src java 10.272 + 10.273 +Running the program outputs four videos into the =./physics-videos= 10.274 +directory. 10.275 + 10.276 +#+begin_src sh :exports both :results verbatim 10.277 +ls ./physics-videos | grep - 10.278 +#+end_src 10.279 + 10.280 +#+results: 10.281 +: lower-left.flv 10.282 +: lower-right.flv 10.283 +: upper-left.flv 10.284 +: upper-right.flv 10.285 + 10.286 +The videos are fused together with the following =gstreamer= commands: 10.287 + 10.288 +#+begin_src sh :results silent 10.289 +cd physics-videos 10.290 + 10.291 +gst-launch-0.10 \ 10.292 + filesrc location=./upper-right.flv ! decodebin ! \ 10.293 + videoscale ! ffmpegcolorspace ! \ 10.294 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.295 + videobox border-alpha=0 left=-640 ! \ 10.296 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.297 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.298 + jpegenc ! avimux ! filesink location=upper.flv \ 10.299 + \ 10.300 + filesrc location=./upper-left.flv ! decodebin ! \ 10.301 + videoscale ! ffmpegcolorspace ! \ 10.302 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.303 + videobox right=-640 ! mix. 10.304 +#+end_src 10.305 + 10.306 +#+begin_src sh :results silent 10.307 +cd physics-videos 10.308 + 10.309 +gst-launch-0.10 \ 10.310 + filesrc location=./lower-left.flv ! decodebin ! \ 10.311 + videoscale ! ffmpegcolorspace ! \ 10.312 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.313 + videobox border-alpha=0 left=-640 ! \ 10.314 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.315 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.316 + jpegenc ! avimux ! filesink location=lower.flv \ 10.317 + \ 10.318 + filesrc location=./lower-right.flv ! decodebin ! \ 10.319 + videoscale ! ffmpegcolorspace ! \ 10.320 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.321 + videobox right=-640 ! mix. 10.322 +#+end_src 10.323 + 10.324 +#+begin_src sh :results silent 10.325 +cd physics-videos 10.326 + 10.327 +gst-launch-0.10 \ 10.328 + filesrc location=./upper.flv ! decodebin ! \ 10.329 + videoscale ! ffmpegcolorspace ! \ 10.330 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.331 + videobox border-alpha=0 bottom=-480 ! \ 10.332 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.333 + video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \ 10.334 + jpegenc ! avimux ! filesink location=../youtube/helloPhysics.flv \ 10.335 + \ 10.336 + filesrc location=./lower.flv ! decodebin ! \ 10.337 + videoscale ! ffmpegcolorspace ! \ 10.338 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.339 + videobox top=-480 ! mix. 10.340 +#+end_src 10.341 + 10.342 +#+begin_src sh :results verbatim 10.343 +du -h youtube/helloPhysics.flv 10.344 +#+end_src 10.345 + 10.346 +#+results: 10.347 +: 180M physics-videos/helloPhysics.flv 10.348 + 10.349 + 10.350 +Thats a terribly large size! 10.351 +Let's compress it: 10.352 + 10.353 +** Compressing the HelloPhysics Video 10.354 +First, we'll scale the video, then, we'll decrease it's bitrate. The 10.355 +end result will be perfect for upload to YouTube. 10.356 + 10.357 +#+begin_src sh :results silent 10.358 +cd youtube 10.359 + 10.360 +gst-launch-0.10 \ 10.361 + filesrc location=./helloPhysics.flv ! decodebin ! \ 10.362 + videoscale ! ffmpegcolorspace ! \ 10.363 + `: # the original size is 1280 by 960` \ 10.364 + video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \ 10.365 + videoscale ! \ 10.366 + `: # here we scale the video down` \ 10.367 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.368 + `: # and here we limit the bitrate` \ 10.369 + theoraenc bitrate=1024 quality=30 ! \ 10.370 + oggmux ! progressreport update-freq=1 ! \ 10.371 + filesink location=./helloPhysics.ogg 10.372 +#+end_src 10.373 + 10.374 +#+begin_src sh :results verbatim 10.375 +du -h youtube/helloPhysics.ogg 10.376 +#+end_src 10.377 + 10.378 +#+results: 10.379 +: 13M youtube/helloPhysics.ogg 10.380 + 10.381 +[[http://www.youtube.com/watch?v=WIJt9aRGusc][helloPhysics.ogg]] 10.382 + 10.383 +#+begin_html 10.384 +<iframe width="425" height="349" 10.385 + src="http://www.youtube.com/embed/WIJt9aRGusc?hl=en&fs=1" 10.386 + frameborder="0" allowfullscreen> 10.387 +</iframe> 10.388 +#+end_html 10.389 + 10.390 + 10.391 +** COMMENT failed attempts 10.392 +Let's try the [[http://diracvideo.org/][Dirac]] video encoder. 10.393 + 10.394 +#+begin_src sh :results verbatim 10.395 +cd youtube 10.396 +START=$(date +%s) 10.397 +gst-launch-0.10 \ 10.398 + filesrc location=./helloPhysics.flv ! decodebin ! \ 10.399 + videoscale ! ffmpegcolorspace ! \ 10.400 + video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \ 10.401 + schroenc ! filesink location=./helloPhysics.drc > /dev/null 10.402 +echo `expr $(( $(date +%s) - $START))` 10.403 +#+end_src 10.404 + 10.405 + 10.406 +#+results: 10.407 +: 142 10.408 + 10.409 +That took 142 seconds. Let's see how it does compression-wise: 10.410 + 10.411 +#+begin_src sh :results verbatim 10.412 +du -h ./youtube/helloPhysics.drc 10.413 +#+end_src 10.414 + 10.415 +#+results: 10.416 +: 22M ./physics-videos/helloPhysics.drc 10.417 + 10.418 + 10.419 +#+begin_src sh :results verbatim 10.420 +cd youtube 10.421 +START=$(date +%s) 10.422 +gst-launch-0.10 \ 10.423 + filesrc location=./helloPhysics.flv ! decodebin ! \ 10.424 + videoscale ! ffmpegcolorspace ! \ 10.425 + video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \ 10.426 + theoraenc ! oggmux ! filesink location=./helloPhysics.ogg \ 10.427 + > /dev/null 10.428 +echo `expr $(( $(date +%s) - $START))` 10.429 +#+end_src 10.430 + 10.431 +#+results: 10.432 +: 123 10.433 + 10.434 +#+begin_src sh :results verbatim 10.435 +du -h youtube/helloPhysics.ogg 10.436 +#+end_src 10.437 + 10.438 +#+results: 10.439 +: 59M physics-videos/helloPhysics.ogg 10.440 + 10.441 + 10.442 +=*.drc= files can not be uploaded to YouTube, so I'll go for the 10.443 +avi file. 10.444 + 10.445 + 10.446 +** COMMENT text for videos 10.447 +Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) using Xuggle 10.448 +(www.xuggle.com/). Everything is explained at 10.449 +http://aurellem.org/cortex/capture-video.html. 10.450 + 10.451 + 10.452 +Video output from JMonkeyEngine3 (www.jmonkeyengine.org/) HelloPhysics 10.453 +demo application using Xuggle (www.xuggle.com/). Everything is 10.454 +explained at http://aurellem.org/cortex/capture-video.html. Here, 10.455 +four points of view are simultaneously recorded and then glued 10.456 +together later. 10.457 + 10.458 + JME3 Xuggle Aurellem video capture 10.459 + 10.460 + 10.461 +* Sample Videos 10.462 +I encoded most of the original JME3 Hello demos for your viewing 10.463 +pleasure, all using the =VideoProcessor= and =IsoTimer= classes. 10.464 + 10.465 +** HelloTerrain 10.466 +[[http://youtu.be/5_4wyDFwrVQ][HelloTerrain.avi]] 10.467 + 10.468 +#+begin_html 10.469 +<iframe width="425" height="349" 10.470 + src="http://www.youtube.com/embed/5_4wyDFwrVQ" 10.471 + frameborder="0" allowfullscreen> 10.472 +</iframe> 10.473 +#+end_html 10.474 + 10.475 +** HelloAssets 10.476 +[[http://www.youtube.com/watch?v=oGg-Q6k1BM4][HelloAssets.avi]] 10.477 + 10.478 +#+begin_html 10.479 +<iframe width="425" height="349" 10.480 + src="http://www.youtube.com/embed/oGg-Q6k1BM4?hl=en&fs=1" 10.481 + frameborder="0" allowfullscreen> 10.482 +</iframe> 10.483 +#+end_html 10.484 + 10.485 +** HelloEffects 10.486 +[[http://www.youtube.com/watch?v=TuxlLMe53hA][HelloEffects]] 10.487 + 10.488 +#+begin_html 10.489 +<iframe width="425" height="349" 10.490 + src="http://www.youtube.com/embed/TuxlLMe53hA?hl=en&fs=1" 10.491 + frameborder="0" allowfullscreen> 10.492 +</iframe> 10.493 +#+end_html 10.494 + 10.495 +** HelloCollision 10.496 +[[http://www.youtube.com/watch?v=GPlvJkiZfFw][HelloCollision.avi]] 10.497 + 10.498 +#+begin_html 10.499 +<iframe width="425" height="349" 10.500 + src="http://www.youtube.com/embed/GPlvJkiZfFw?hl=en&fs=1" 10.501 + frameborder="0" allowfullscreen> 10.502 +</iframe> 10.503 +#+end_html 10.504 + 10.505 +** HelloAnimation 10.506 +[[http://www.youtube.com/watch?v=SDCfOSPYUkg][HelloAnimation.avi]] 10.507 + 10.508 +#+begin_html 10.509 +<iframe width="425" height="349" 10.510 + src="http://www.youtube.com/embed/SDCfOSPYUkg?hl=en&fs=1" 10.511 + frameborder="0" allowfullscreen> 10.512 +</iframe> 10.513 +#+end_html 10.514 + 10.515 +** HelloNode 10.516 +[[http://www.youtube.com/watch?v=pL-0fR0-ilQ][HelloNode.avi]] 10.517 + 10.518 +#+begin_html 10.519 +<iframe width="425" height="349" 10.520 + src="http://www.youtube.com/embed/pL-0fR0-ilQ?hl=en&fs=1" 10.521 + frameborder="0" allowfullscreen> 10.522 +</iframe> 10.523 +#+end_html 10.524 + 10.525 +** HelloLoop 10.526 +[[http://www.youtube.com/watch?v=mosZzzcdE5w][HelloLoop.avi]] 10.527 + 10.528 +#+begin_html 10.529 +<iframe width="425" height="349" 10.530 + src="http://www.youtube.com/embed/mosZzzcdE5w?hl=en&fs=1" 10.531 + frameborder="0" allowfullscreen> 10.532 +</iframe> 10.533 +#+end_html 10.534 + 10.535 + 10.536 +*** COMMENT x-form the other stupid 10.537 +progressreport update-freq=1 10.538 + 10.539 +gst-launch-0.10 \ 10.540 + filesrc location=./helloPhy ! decodebin ! \ 10.541 + videoscale ! ffmpegcolorspace ! \ 10.542 + video/x-raw-yuv, width=1280, height=960, framerate=25/1 ! \ 10.543 + x264enc ! avimux ! filesink location=helloPhysics.avi \ 10.544 + 10.545 + 10.546 +gst-launch-0.10 \ 10.547 + filesrc location=./HelloAnimationStatic.flv ! decodebin ! \ 10.548 + videoscale ! ffmpegcolorspace ! \ 10.549 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.550 + videobox border-alpha=0 left=-640 ! \ 10.551 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.552 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.553 + x264enc ! avimux ! progressreport update-freq=1 ! \ 10.554 + filesink location=../youtube/HelloAnimation.avi \ 10.555 + \ 10.556 + filesrc location=./HelloAnimationMotion.flv ! decodebin ! \ 10.557 + videoscale ! ffmpegcolorspace ! \ 10.558 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.559 + videobox right=-640 ! mix. 10.560 + 10.561 +gst-launch-0.10 \ 10.562 + filesrc location=./HelloCollisionMotion.flv ! decodebin ! \ 10.563 + videoscale ! ffmpegcolorspace ! \ 10.564 + video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \ 10.565 + x264enc bitrate=1024 ! avimux ! \ 10.566 + filesink location=../youtube/HelloCollision.avi 10.567 + 10.568 +gst-launch-0.10 \ 10.569 + filesrc location=./HelloEffectsStatic.flv ! decodebin ! \ 10.570 + videoscale ! ffmpegcolorspace ! \ 10.571 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.572 + videobox border-alpha=0 left=-640 ! \ 10.573 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.574 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.575 + x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \ 10.576 + filesink location=../youtube/HelloEffects.avi \ 10.577 + \ 10.578 + filesrc location=./HelloEffectsMotion.flv ! decodebin ! \ 10.579 + videoscale ! ffmpegcolorspace ! \ 10.580 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.581 + videobox right=-640 ! mix. 10.582 + 10.583 +gst-launch-0.10 \ 10.584 + filesrc location=./HelloTerrainMotion.flv ! decodebin ! \ 10.585 + videoscale ! ffmpegcolorspace ! \ 10.586 + video/x-raw-yuv, width=800, height=600, framerate=25/1 ! \ 10.587 + x264enc bitrate=1024 ! avimux ! \ 10.588 + filesink location=../youtube/HelloTerrain.avi 10.589 + 10.590 + 10.591 +gst-launch-0.10 \ 10.592 + filesrc location=./HelloAssetsStatic.flv ! decodebin ! \ 10.593 + videoscale ! ffmpegcolorspace ! \ 10.594 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.595 + videobox border-alpha=0 left=-640 ! \ 10.596 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.597 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.598 + x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \ 10.599 + filesink location=../youtube/HelloAssets.avi \ 10.600 + \ 10.601 + filesrc location=./HelloAssetsMotion.flv ! decodebin ! \ 10.602 + videoscale ! ffmpegcolorspace ! \ 10.603 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.604 + videobox right=-640 ! mix. 10.605 + 10.606 + 10.607 +gst-launch-0.10 \ 10.608 + filesrc location=./HelloNodeStatic.flv ! decodebin ! \ 10.609 + videoscale ! ffmpegcolorspace ! \ 10.610 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.611 + videobox border-alpha=0 left=-640 ! \ 10.612 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.613 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.614 + x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \ 10.615 + filesink location=../youtube/HelloNode.avi \ 10.616 + \ 10.617 + filesrc location=./HelloNodeMotion.flv ! decodebin ! \ 10.618 + videoscale ! ffmpegcolorspace ! \ 10.619 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.620 + videobox right=-640 ! mix. 10.621 + 10.622 +gst-launch-0.10 \ 10.623 + filesrc location=./HelloLoopStatic.flv ! decodebin ! \ 10.624 + videoscale ! ffmpegcolorspace ! \ 10.625 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.626 + videobox border-alpha=0 left=-640 ! \ 10.627 + videomixer name=mix ! ffmpegcolorspace ! videorate ! \ 10.628 + video/x-raw-yuv, width=1280, height=480, framerate=25/1 ! \ 10.629 + x264enc bitrate=1024 ! avimux ! progressreport update-freq=1 ! \ 10.630 + filesink location=../youtube/HelloLoop.avi \ 10.631 + \ 10.632 + filesrc location=./HelloLoopMotion.flv ! decodebin ! \ 10.633 + videoscale ! ffmpegcolorspace ! \ 10.634 + video/x-raw-yuv, width=640, height=480, framerate=25/1 ! \ 10.635 + videobox right=-640 ! mix. 10.636 +
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/org/cortex.org Sun Oct 16 05:12:19 2011 -0700 11.3 @@ -0,0 +1,1738 @@ 11.4 +#+title: Simulated Senses 11.5 +#+author: Robert McIntyre 11.6 +#+email: rlm@mit.edu 11.7 +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js" 11.8 +#+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/> 11.9 +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes 11.10 +#+INCLUDE: ../aurellem/src/templates/level-0.org 11.11 +#+description: Simulating senses for AI research using JMonkeyEngine3 11.12 + 11.13 +* Background 11.14 +Artificial Intelligence has tried and failed for more than half a 11.15 +century to produce programs as flexible, creative, and “intelligent” 11.16 +as the human mind itself. Clearly, we are still missing some important 11.17 +ideas concerning intelligent programs or we would have strong AI 11.18 +already. What idea could be missing? 11.19 + 11.20 +When Turing first proposed his famous “Turing Test” in the 11.21 +groundbreaking paper [[./sources/turing.pdf][/Computing Machines and Intelligence/]], he gave 11.22 +little importance to how a computer program might interact with the 11.23 +world: 11.24 + 11.25 +#+BEGIN_QUOTE 11.26 +\ldquo{}We need not be too concerned about the legs, eyes, etc. The example of 11.27 +Miss Helen Keller shows that education can take place provided that 11.28 +communication in both directions between teacher and pupil can take 11.29 +place by some means or other.\rdquo{} 11.30 +#+END_QUOTE 11.31 + 11.32 +And from the example of Hellen Keller he went on to assume that the 11.33 +only thing a fledgling AI program could need by way of communication 11.34 +is a teletypewriter. But Hellen Keller did possess vision and hearing 11.35 +for the first few months of her life, and her tactile sense was far 11.36 +more rich than any text-stream could hope to achieve. She possessed a 11.37 +body she could move freely, and had continual access to the real world 11.38 +to learn from her actions. 11.39 + 11.40 +I believe that our programs are suffering from too little sensory 11.41 +input to become really intelligent. Imagine for a moment that you 11.42 +lived in a world completely cut off form all sensory stimulation. You 11.43 +have no eyes to see, no ears to hear, no mouth to speak. No body, no 11.44 +taste, no feeling whatsoever. The only sense you get at all is a 11.45 +single point of light, flickering on and off in the void. If this was 11.46 +your life from birth, you would never learn anything, and could never 11.47 +become intelligent. Actual humans placed in sensory deprivation 11.48 +chambers experience hallucinations and can begin to loose their sense 11.49 +of reality in as little as 15 minutes[sensory-deprivation]. Most of 11.50 +the time, the programs we write are in exactly this situation. They do 11.51 +not interface with cameras and microphones, and they do not control a 11.52 +real or simulated body or interact with any sort of world. 11.53 + 11.54 + 11.55 +* Simulation vs. Reality 11.56 +I want demonstrate that multiple senses are what enable 11.57 +intelligence. There are two ways of playing around with senses and 11.58 +computer programs: 11.59 + 11.60 +The first is to go entirely with simulation: virtual world, virtual 11.61 +character, virtual senses. The advantages are that when everything is 11.62 +a simulation, experiments in that simulation are absolutely 11.63 +reproducible. It's also easier to change the character and world to 11.64 +explore new situations and different sensory combinations. 11.65 + 11.66 + 11.67 +** Issues with Simulation 11.68 + 11.69 +If the world is to be simulated on a computer, then not only do you 11.70 +have to worry about whether the character's senses are rich enough to 11.71 +learn from the world, but whether the world itself is rendered with 11.72 +enough detail and realism to give enough working material to the 11.73 +character's senses. To name just a few difficulties facing modern 11.74 +physics simulators: destructibility of the environment, simulation of 11.75 +water/other fluids, large areas, nonrigid bodies, lots of objects, 11.76 +smoke. I don't know of any computer simulation that would allow a 11.77 +character to take a rock and grind it into fine dust, then use that 11.78 +dust to make a clay sculpture, at least not without spending years 11.79 +calculating the interactions of every single small grain of 11.80 +dust. Maybe a simulated world with today's limitations doesn't provide 11.81 +enough richness for real intelligence to evolve. 11.82 + 11.83 +** Issues with Reality 11.84 + 11.85 +The other approach for playing with senses is to hook your software up 11.86 +to real cameras, microphones, robots, etc., and let it loose in the 11.87 +real world. This has the advantage of eliminating concerns about 11.88 +simulating the world at the expense of increasing the complexity of 11.89 +implementing the senses. Instead of just grabbing the current rendered 11.90 +frame for processing, you have to use an actual camera with real 11.91 +lenses and interact with photons to get an image. It is much harder to 11.92 +change the character, which is now partly a physical robot of some 11.93 +sort, since doing so involves changing things around in the real world 11.94 +instead of modifying lines of code. While the real world is very rich 11.95 +and definitely provides enough stimulation for intelligence to develop 11.96 +as evidenced by our own existence, it is also uncontrollable in the 11.97 +sense that a particular situation cannot be recreated perfectly or 11.98 +saved for later use. It is harder to conduct science because it is 11.99 +harder to repeat an experiment. The worst thing about using the real 11.100 +world instead of a simulation is the matter of time. Instead of 11.101 +simulated time you get the constant and unstoppable flow of real 11.102 +time. This severely limits the sorts of software you can use to 11.103 +program the AI because all sense inputs must be handled in real 11.104 +time. Complicated ideas may have to be implemented in hardware or may 11.105 +simply be impossible given the current speed of our 11.106 +processors. Contrast this with a simulation, in which the flow of time 11.107 +in the simulated world can be slowed down to accommodate the 11.108 +limitations of the character's programming. In terms of cost, doing 11.109 +everything in software is far cheaper than building custom real-time 11.110 +hardware. All you need is a laptop and some patience. 11.111 + 11.112 +* Choose a Simulation Engine 11.113 + 11.114 +Mainly because of issues with controlling the flow of time, I chose to 11.115 +simulate both the world and the character. I set out to make a minimal 11.116 +world in which I could embed a character with multiple senses. My main 11.117 +goal is to make an environment where I can perform further experiments 11.118 +in simulated senses. 11.119 + 11.120 +As Carl Sagan once said, "If you wish to make an apple pie from 11.121 +scratch, you must first invent the universe.” I examined many 11.122 +different 3D environments to try and find something I would use as the 11.123 +base for my simulation; eventually the choice came down to three 11.124 +engines: the Quake II engine, the Source Engine, and jMonkeyEngine. 11.125 + 11.126 +** Quake II/Jake2 11.127 + 11.128 +I spent a bit more than a month working with the Quake II Engine from 11.129 +ID software to see if I could use it for my purposes. All the source 11.130 +code was released by ID software into the Public Domain several years 11.131 +ago, and as a result it has been ported and modified for many 11.132 +different reasons. This engine was famous for its advanced use of 11.133 +realistic shading and had decent and fast physics 11.134 +simulation. Researchers at Princeton [[http://www.nature.com/nature/journal/v461/n7266/pdf/nature08499.pdf][used this code]] to study spatial 11.135 +information encoding in the hippocampal cells of rats. Those 11.136 +researchers created a special Quake II level that simulated a maze, 11.137 +and added an interface where a mouse could run around inside a ball in 11.138 +various directions to move the character in the simulated maze. They 11.139 +measured hippocampal activity during this exercise to try and tease 11.140 +out the method in which spatial data was stored in that area of the 11.141 +brain. I find this promising because if a real living rat can interact 11.142 +with a computer simulation of a maze in the same way as it interacts 11.143 +with a real-world maze, then maybe that simulation is close enough to 11.144 +reality that a simulated sense of vision and motor control interacting 11.145 +with that simulation could reveal useful information about the real 11.146 +thing. It happens that there is a Java port of the original C source 11.147 +code called Jake2. The port demonstrates Java's OpenGL bindings and 11.148 +runs anywhere from 90% to 105% as fast as the C version. After 11.149 +reviewing much of the source of Jake2, I eventually rejected it 11.150 +because the engine is too tied to the concept of a first-person 11.151 +shooter game. One of the problems I had was that there does not seem 11.152 +to be any easy way to attach multiple cameras to a single 11.153 +character. There are also several physics clipping issues that are 11.154 +corrected in a way that only applies to the main character and does 11.155 +not apply to arbitrary objects. While there is a large community of 11.156 +level modders, I couldn't find a community to support using the engine 11.157 +to make new things. 11.158 + 11.159 +** Source Engine 11.160 + 11.161 +The Source Engine evolved from the Quake II and Quake I engines and is 11.162 +used by Valve in the Half-Life series of games. The physics simulation 11.163 +in the Source Engine is quite accurate and probably the best out of 11.164 +all the engines I investigated. There is also an extensive community 11.165 +actively working with the engine. However, applications that use the 11.166 +Source Engine must be written in C++, the code is not open, it only 11.167 +runs on Windows, and the tools that come with the SDK to handle models 11.168 +and textures are complicated and awkward to use. 11.169 + 11.170 +** jMonkeyEngine 11.171 + 11.172 +jMonkeyEngine is a new library for creating games in Java. It uses 11.173 +OpenGL to render to the screen and uses screengraphs to avoid drawing 11.174 +things that do not appear on the screen. It has an active community 11.175 +and several games in the pipeline. The engine was not built to serve 11.176 +any particular game but is instead meant to be used for any 3D 11.177 +game. After experimenting with each of these three engines and a few 11.178 +others for about 2 months I settled on jMonkeyEngine. I chose it 11.179 +because it had the most features out of all the open projects I looked 11.180 +at, and because I could then write my code in Clojure, an 11.181 +implementation of LISP that runs on the JVM. 11.182 + 11.183 +* Setup 11.184 + 11.185 +First, I checked out the source to jMonkeyEngine: 11.186 + 11.187 +#+srcname: checkout 11.188 +#+begin_src sh :results verbatim 11.189 +svn checkout http://jmonkeyengine.googlecode.com/svn/trunk/engine jme3 11.190 +#+end_src 11.191 + 11.192 +#+results: checkout 11.193 +: Checked out revision 7975. 11.194 + 11.195 + 11.196 +Building jMonkeyEngine is easy enough: 11.197 + 11.198 +#+srcname: build 11.199 +#+begin_src sh :results verbatim 11.200 +cd jme3 11.201 +ant jar | tail -n 2 11.202 +#+end_src 11.203 + 11.204 +#+results: build 11.205 +: BUILD SUCCESSFUL 11.206 +: Total time: 15 seconds 11.207 + 11.208 + 11.209 +Also build the javadoc: 11.210 + 11.211 +#+srcname: javadoc 11.212 +#+begin_src sh :results verbatim 11.213 +cd jme3 11.214 +ant javadoc | tail -n 2 11.215 +#+end_src 11.216 + 11.217 +#+results: javadoc 11.218 +: BUILD SUCCESSFUL 11.219 +: Total time: 12 seconds 11.220 + 11.221 +Now, move the jars from the compilation into the project's lib folder. 11.222 + 11.223 +#+srcname: move-jars 11.224 +#+begin_src sh :results verbatim 11.225 +mkdir -p lib 11.226 +mkdir -p src 11.227 +cp jme3/dist/jMonkeyEngine3.jar lib/ 11.228 +cp jme3/dist/lib/* lib/ 11.229 +ls lib 11.230 +#+end_src 11.231 + 11.232 +#+results: move-jars 11.233 +#+begin_example 11.234 +eventbus-1.4.jar 11.235 +jbullet.jar 11.236 +jheora-jst-debug-0.6.0.jar 11.237 +jinput.jar 11.238 +jME3-jbullet.jar 11.239 +jME3-lwjgl-natives.jar 11.240 +jME3-testdata.jar 11.241 +jME3-test.jar 11.242 +jMonkeyEngine3.jar 11.243 +j-ogg-oggd.jar 11.244 +j-ogg-vorbisd.jar 11.245 +lwjgl.jar 11.246 +nifty-1.3.jar 11.247 +nifty-default-controls-1.3.jar 11.248 +nifty-examples-1.3.jar 11.249 +nifty-lwjgl-renderer-1.3.jar 11.250 +nifty-openal-soundsystem-1.0.jar 11.251 +nifty-style-black-1.3.jar 11.252 +nifty-style-grey-1.0.jar 11.253 +noise-0.0.1-SNAPSHOT.jar 11.254 +stack-alloc.jar 11.255 +vecmath.jar 11.256 +xmlpull-xpp3-1.1.4c.jar 11.257 +#+end_example 11.258 + 11.259 +It's good to create a =assets= directory in the style that the 11.260 +=AssetManager= will like. 11.261 + 11.262 +#+srcname: create-assets 11.263 +#+begin_src sh :results verbatim 11.264 +mkdir -p assets 11.265 +mkdir -p assets/Interface 11.266 +mkdir -p assets/Materials 11.267 +mkdir -p assets/MatDefs 11.268 +mkdir -p assets/Models 11.269 +mkdir -p assets/Scenes 11.270 +mkdir -p assets/Shaders 11.271 +mkdir -p assets/Sounds 11.272 +mkdir -p assets/Textures 11.273 +tree -L 1 assets 11.274 +#+end_src 11.275 + 11.276 +#+results: create-assets 11.277 +#+begin_example 11.278 +assets 11.279 +|-- Interface 11.280 +|-- MatDefs 11.281 +|-- Materials 11.282 +|-- Models 11.283 +|-- Scenes 11.284 +|-- Shaders 11.285 +|-- Sounds 11.286 +`-- Textures 11.287 + 11.288 +8 directories, 0 files 11.289 +#+end_example 11.290 + 11.291 + 11.292 +The java classpath should have all the jars contained in the =lib= 11.293 +directory as well as the src directory. 11.294 + 11.295 +For example, here is the file I use to run my REPL for clojure. 11.296 + 11.297 +#+include: "~/swank-all" src sh :exports code 11.298 + 11.299 +The important thing here is that =cortex/lib/*=, =cortex/src=, and 11.300 +=cortex/assets= appear on the classpath. (=cortex= is the base 11.301 +directory of this project.) 11.302 + 11.303 +#+srcname: pwd 11.304 +#+begin_src sh 11.305 +pwd 11.306 +#+end_src 11.307 + 11.308 +#+results: pwd 11.309 +: /home/r/cortex 11.310 + 11.311 + 11.312 +* Simulation Base 11.313 + 11.314 +** Imports 11.315 +First, I'll import jme core classes. 11.316 +#+srcname: import 11.317 +#+begin_src clojure :results silent 11.318 +(ns cortex.import 11.319 + (:require swank.util.class-browse)) 11.320 + 11.321 +(defn import-jme3 [] 11.322 + (import '[com.jme3.system AppSettings JmeSystem]) 11.323 + (import '[com.jme3.app Application SimpleApplication]) 11.324 + (import 'com.jme3.material.Material) 11.325 + (import '[com.jme3.math Vector3f ColorRGBA Quaternion Transform]) 11.326 + (import '[com.jme3.scene Node Geometry]) 11.327 + (import '[com.jme3.scene.shape Box Sphere Sphere$TextureMode]) 11.328 + (import 'com.jme3.font.BitmapText) 11.329 + (import '[com.jme3.input KeyInput InputManager]) 11.330 + (import '[com.jme3.input.controls 11.331 + ActionListener AnalogListener KeyTrigger MouseButtonTrigger]) 11.332 + (import '[com.jme3.asset AssetManager DesktopAssetManager] ) 11.333 + (import '[com.jme3.asset.plugins HttpZipLocator ZipLocator]) 11.334 + (import '[com.jme3.light PointLight DirectionalLight]) 11.335 + (import '[com.jme3.animation AnimControl Skeleton Bone]) 11.336 + (import '[com.jme3.bullet.collision.shapes 11.337 + MeshCollisionShape SphereCollisionShape BoxCollisionShape]) 11.338 + (import 'com.jme3.renderer.queue.RenderQueue$ShadowMode) 11.339 + (import 'jme3test.TestChooser) 11.340 + (import '[com.jme3.bullet PhysicsTickListener PhysicsSpace]) 11.341 + (import '[com.jme3.bullet.joints SixDofJoint HingeJoint 11.342 + SliderJoint Point2PointJoint ConeJoint])) 11.343 + 11.344 + 11.345 +(defmacro permissive-import* [class-symbol] 11.346 + `(try 11.347 + (import ~class-symbol) 11.348 + (catch Exception e# 11.349 + (println "can't import " ~class-symbol)))) 11.350 + 11.351 +(defn permissive-import [class-symbol] 11.352 + (eval (list 'cortex.import/permissive-import* class-symbol))) 11.353 + 11.354 +(defn selection-import [selection-fn] 11.355 + (dorun 11.356 + (map (comp permissive-import symbol) 11.357 + (filter selection-fn 11.358 + (map :name 11.359 + swank.util.class-browse/available-classes))))) 11.360 + 11.361 +(defn mega-import-jme3 11.362 + "ALL the jme classes. For REPL use." 11.363 + [] 11.364 + (selection-import 11.365 + #(and 11.366 + (.startsWith % "com.jme3.") 11.367 + ;; Don't import the Lwjgl stuff since it can throw exceptions 11.368 + ;; upon being loaded. 11.369 + (not (re-matches #".*Lwjgl.*" %))))) 11.370 +#+end_src 11.371 + 11.372 +The =mega-import-jme3= is quite usefull for debugging purposes since 11.373 +it allows completion for almost all of JME's classes 11.374 + 11.375 +** Simplification 11.376 +*** World 11.377 + 11.378 +It is comvienent to wrap the JME elements that deal with creating a 11.379 +world, creation of basic objects, and Keyboard input with a nicer 11.380 +interface (at least for my purposes). 11.381 + 11.382 +#+srcname: world-inputs 11.383 +#+begin_src clojure :results silent 11.384 +(ns cortex.world) 11.385 +(require 'cortex.import) 11.386 +(use 'clojure.contrib.def) 11.387 +(rlm.rlm-commands/help) 11.388 +(cortex.import/mega-import-jme3) 11.389 + 11.390 +(defvar *app-settings* 11.391 + (doto (AppSettings. true) 11.392 + (.setFullscreen false) 11.393 + (.setTitle "Aurellem.") 11.394 + ;; disable 32 bit stuff for now 11.395 + ;;(.setAudioRenderer "Send") 11.396 + ) 11.397 + "These settings control how the game is displayed on the screen for 11.398 + debugging purposes. Use binding forms to change this if desired. 11.399 + Full-screen mode does not work on some computers.") 11.400 + 11.401 +(defn asset-manager 11.402 + "returns a new, configured assetManager" [] 11.403 + (JmeSystem/newAssetManager 11.404 + (.getResource 11.405 + (.getContextClassLoader (Thread/currentThread)) 11.406 + "com/jme3/asset/Desktop.cfg"))) 11.407 + 11.408 +(defmacro no-exceptions 11.409 + "Sweet relief like I never knew." 11.410 + [& forms] 11.411 + `(try ~@forms (catch Exception e# (.printStackTrace e#)))) 11.412 + 11.413 +(defn thread-exception-removal [] 11.414 + (println "removing exceptions from " (Thread/currentThread)) 11.415 + (.setUncaughtExceptionHandler 11.416 + (Thread/currentThread) 11.417 + (proxy [Thread$UncaughtExceptionHandler] [] 11.418 + (uncaughtException 11.419 + [thread thrown] 11.420 + (println "uncaught-exception thrown in " thread) 11.421 + (println (.getMessage thrown)))))) 11.422 + 11.423 +(def println-repl (bound-fn [& args] (apply println args))) 11.424 + 11.425 +(use '[pokemon [lpsolve :only [constant-map]]]) 11.426 + 11.427 +(defn no-op [& _]) 11.428 + 11.429 +(defn all-keys 11.430 + "Construct a map of strings representing all the manual inputs from 11.431 + either the keyboard or mouse." 11.432 + [] 11.433 + (let [inputs (constant-map KeyInput)] 11.434 + (assoc 11.435 + (zipmap (map (fn [field] 11.436 + (.toLowerCase (re-gsub #"_" "-" field))) (vals inputs)) 11.437 + (map (fn [val] (KeyTrigger. val)) (keys inputs))) 11.438 + ;;explicitly add mouse controls 11.439 + "mouse-left" (MouseButtonTrigger. 0) 11.440 + "mouse-middle" (MouseButtonTrigger. 2) 11.441 + "mouse-right" (MouseButtonTrigger. 1)))) 11.442 + 11.443 +(defn initialize-inputs 11.444 + "more java-interop cruft to establish keybindings for a particular virtual world" 11.445 + [game input-manager key-map] 11.446 + (doall (map (fn [[name trigger]] 11.447 + (.addMapping ^InputManager input-manager 11.448 + name (into-array (class trigger) [trigger]))) key-map)) 11.449 + (doall (map (fn [name] 11.450 + (.addListener ^InputManager input-manager game 11.451 + (into-array String [name]))) (keys key-map)))) 11.452 + 11.453 +#+end_src 11.454 + 11.455 +These functions are all for debug controlling of the world through 11.456 +keyboard and mouse. 11.457 + 11.458 +We reuse =constant-map= from =pokemon.lpsolve= to get the numerical 11.459 +values for all the keys defined in the =KeyInput= class. The 11.460 +documentation for =constant-map= is: 11.461 + 11.462 +#+begin_src clojure :results output 11.463 +(doc pokemon.lpsolve/constant-map) 11.464 +#+end_src 11.465 + 11.466 +#+results: 11.467 +: ------------------------- 11.468 +: pokemon.lpsolve/constant-map 11.469 +: ([class]) 11.470 +: Takes a class and creates a map of the static constant integer 11.471 +: fields with their names. This helps with C wrappers where they have 11.472 +: just defined a bunch of integer constants instead of enums 11.473 + 11.474 + 11.475 +Then, =all-keys= converts the constant names like =KEY_J= to the more 11.476 +clojure-like =key-j=, and returns a map from these keys to 11.477 +jMonkeyEngine KeyTrigger objects, the use of which will soon become 11.478 +apparent. =all-keys= also adds the three mouse button controls to the 11.479 +map. 11.480 + 11.481 +#+srcname: world 11.482 +#+begin_src clojure :results silent 11.483 +(in-ns 'cortex.world) 11.484 + 11.485 +(defn traverse 11.486 + "apply f to every non-node, deeply" 11.487 + [f node] 11.488 + (if (isa? (class node) Node) 11.489 + (dorun (map (partial traverse f) (.getChildren node))) 11.490 + (f node))) 11.491 + 11.492 +(def gravity (Vector3f. 0 -9.81 0)) 11.493 + 11.494 +(defn world 11.495 + [root-node key-map setup-fn update-fn] 11.496 + (let [physics-manager (BulletAppState.) 11.497 + shadow-renderer (BasicShadowRenderer. (asset-manager) (int 256)) 11.498 + ;;maybe use a better shadow renderer someday! 11.499 + ;;shadow-renderer (PssmShadowRenderer. (asset-manager) 256 1) 11.500 + ] 11.501 + (doto 11.502 + (proxy [SimpleApplication ActionListener] [] 11.503 + (simpleInitApp 11.504 + [] 11.505 + (no-exceptions 11.506 + (.setTimer this (IsoTimer. 60)) 11.507 + ;; Create key-map. 11.508 + (.setFrustumFar (.getCamera this) 300) 11.509 + (initialize-inputs this (.getInputManager this) (all-keys)) 11.510 + ;; Don't take control of the mouse 11.511 + (org.lwjgl.input.Mouse/setGrabbed false) 11.512 + ;; add all objects to the world 11.513 + (.attachChild (.getRootNode this) root-node) 11.514 + ;; enable physics 11.515 + ;; add a physics manager 11.516 + (.attach (.getStateManager this) physics-manager) 11.517 + (.setGravity (.getPhysicsSpace physics-manager) gravity) 11.518 + 11.519 + 11.520 + ;; go through every object and add it to the physics manager 11.521 + ;; if relavant. 11.522 + (traverse (fn [geom] 11.523 + (dorun 11.524 + (for [n (range (.getNumControls geom))] 11.525 + (do 11.526 + (println-repl "adding control " (.getControl geom n)) 11.527 + (.add (.getPhysicsSpace physics-manager) 11.528 + (.getControl geom n)))))) 11.529 + (.getRootNode this)) 11.530 + ;;(.addAll (.getPhysicsSpace physics-manager) (.getRootNode this)) 11.531 + 11.532 + (setup-fn this) 11.533 + (.setDirection shadow-renderer 11.534 + (.normalizeLocal (Vector3f. -1 -1 -1))) 11.535 + (.addProcessor (.getViewPort this) shadow-renderer) 11.536 + (.setShadowMode (.getRootNode this) RenderQueue$ShadowMode/Off) 11.537 + )) 11.538 + (simpleUpdate 11.539 + [tpf] 11.540 + (no-exceptions 11.541 + (update-fn this tpf))) 11.542 + (onAction 11.543 + [binding value tpf] 11.544 + ;; whenever a key is pressed, call the function returned from 11.545 + ;; key-map. 11.546 + (no-exceptions 11.547 + (if-let [react (key-map binding)] 11.548 + (react this value))))) 11.549 + ;; don't show a menu to change options. 11.550 + 11.551 + (.setShowSettings false) 11.552 + (.setPauseOnLostFocus false) 11.553 + (.setSettings *app-settings*)))) 11.554 + 11.555 +(defn apply-map 11.556 + "Like apply, but works for maps and functions that expect an implicit map 11.557 + and nothing else as in (fn [& {}]). 11.558 +------- Example ------- 11.559 + (defn jjj [& {:keys [www] :or {www \"oph yeah\"} :as env}] (println www)) 11.560 + (apply-map jjj {:www \"whatever\"}) 11.561 + -->\"whatever\"" 11.562 + [fn m] 11.563 + (apply fn (reduce #(into %1 %2) [] m))) 11.564 + 11.565 +#+end_src 11.566 + 11.567 + 11.568 +=world= is the most important function here. 11.569 +*** TODO more documentation 11.570 + 11.571 +#+srcname: world-shapes 11.572 +#+begin_src clojure :results silent 11.573 +(in-ns 'cortex.world) 11.574 +(defrecord shape-description 11.575 + [name 11.576 + color 11.577 + mass 11.578 + friction 11.579 + texture 11.580 + material 11.581 + position 11.582 + rotation 11.583 + shape 11.584 + physical?]) 11.585 + 11.586 +(def base-shape 11.587 + (shape-description. 11.588 + "default-shape" 11.589 + false 11.590 + ;;ColorRGBA/Blue 11.591 + 1.0 ;; mass 11.592 + 1.0 ;; friction 11.593 + ;; texture 11.594 + "Textures/Terrain/BrickWall/BrickWall.jpg" 11.595 + ;; material 11.596 + "Common/MatDefs/Misc/Unshaded.j3md" 11.597 + Vector3f/ZERO 11.598 + Quaternion/IDENTITY 11.599 + (Box. Vector3f/ZERO 0.5 0.5 0.5) 11.600 + true)) 11.601 + 11.602 +(defn make-shape 11.603 + [#^shape-description d] 11.604 + (let [mat (Material. (asset-manager) (:material d)) 11.605 + geom (Geometry. (:name d) (:shape d))] 11.606 + (if (:texture d) 11.607 + (let [key (TextureKey. (:texture d))] 11.608 + (.setGenerateMips key true) 11.609 + (.setTexture mat "ColorMap" (.loadTexture (asset-manager) key)))) 11.610 + (if (:color d) (.setColor mat "Color" (:color d))) 11.611 + (.setMaterial geom mat) 11.612 + (if-let [rotation (:rotation d)] (.rotate geom rotation)) 11.613 + (.setLocalTranslation geom (:position d)) 11.614 + (if (:physical? d) 11.615 + (let [impact-shape (doto (GImpactCollisionShape. (.getMesh geom)) (.setMargin 0)) 11.616 + physics-control (RigidBodyControl. 11.617 + impact-shape 11.618 + (float (:mass d)))] 11.619 + (.createJmeMesh impact-shape) 11.620 + (.addControl geom physics-control) 11.621 + ;;(.setSleepingThresholds physics-control (float 0) (float 0)) 11.622 + (.setFriction physics-control (:friction d)))) 11.623 + ;;the default is to keep this node in the physics engine forever. 11.624 + ;;these commands must come after the control is added to the geometry. 11.625 + ;; 11.626 + geom)) 11.627 + 11.628 +(defn box 11.629 + ([l w h & {:as options}] 11.630 + (let [options (merge base-shape options)] 11.631 + (make-shape (assoc options 11.632 + :shape (Box. l w h))))) 11.633 + ([] (box 0.5 0.5 0.5))) 11.634 + 11.635 +(defn sphere 11.636 + ([r & {:as options}] 11.637 + (let [options (merge base-shape options)] 11.638 + (make-shape (assoc options 11.639 + :shape (Sphere. 32 32 (float r)))))) 11.640 + ([] (sphere 0.5))) 11.641 + 11.642 +(defn add-element [game node] 11.643 + (.addAll 11.644 + (.getPhysicsSpace 11.645 + (.getState 11.646 + (.getStateManager game) 11.647 + BulletAppState)) 11.648 + node) 11.649 + (.attachChild (.getRootNode game) node)) 11.650 + 11.651 +(defn set-gravity* 11.652 + [game gravity] 11.653 + (traverse 11.654 + (fn [geom] 11.655 + (if-let 11.656 + [control (.getControl geom RigidBodyControl)] 11.657 + (do 11.658 + (.setGravity control gravity) 11.659 + (.applyImpulse control Vector3f/ZERO Vector3f/ZERO) 11.660 + ))) 11.661 + (.getRootNode game))) 11.662 + 11.663 +#+end_src 11.664 + 11.665 +These are convienence functions for creating JME objects and 11.666 +manipulating a world. 11.667 + 11.668 +#+srcname: world-view 11.669 +#+begin_src clojure :results silent 11.670 +(in-ns 'cortex.world) 11.671 + 11.672 +(defprotocol Viewable 11.673 + (view [something])) 11.674 + 11.675 +(extend-type com.jme3.scene.Geometry 11.676 + Viewable 11.677 + (view [geo] 11.678 + (view (doto (Node.)(.attachChild geo))))) 11.679 + 11.680 +(extend-type com.jme3.scene.Node 11.681 + Viewable 11.682 + (view [node] 11.683 + (.start 11.684 + (world node 11.685 + {} 11.686 + (fn [world] 11.687 + (.enableDebug 11.688 + (.getPhysicsSpace 11.689 + (.getState 11.690 + (.getStateManager world) 11.691 + BulletAppState)) 11.692 + (asset-manager)) 11.693 + (set-gravity* world Vector3f/ZERO) 11.694 +;; (set-gravity* world (Vector3f. 0 (float -0.4) 0)) 11.695 + (let [sun (doto (DirectionalLight.) 11.696 + (.setDirection (.normalizeLocal (Vector3f. 1 0 -2))) 11.697 + (.setColor ColorRGBA/White))] 11.698 + (.addLight (.getRootNode world) sun))) 11.699 + no-op)))) 11.700 + 11.701 +(defn position-camera [game] 11.702 + (doto (.getCamera game) 11.703 + (.setLocation (Vector3f. 0 6 6)) 11.704 + (.lookAt Vector3f/ZERO (Vector3f. 0 1 0)))) 11.705 + 11.706 +#+end_src 11.707 + 11.708 +Here I make the =Viewable= protocol and extend it to JME's types. Now 11.709 +hello-world can be written as easily as: 11.710 + 11.711 +#+begin_src clojure :results silent 11.712 +(cortex.world/view (cortex.world/box)) 11.713 +#+end_src 11.714 + 11.715 +** Hello 11.716 +Here are the jmonkeyengine "Hello" programs translated to clojure. 11.717 +*** Hello Simple App 11.718 +Here is the hello world example for jme3 in clojure. 11.719 +It's a more or less direct translation from the java source 11.720 +from 11.721 +http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_simpleapplication. 11.722 + 11.723 +Of note is the fact that since we don't have access to the 11.724 +=AssetManager= via extendig =SimpleApplication=, we have to build one 11.725 +ourselves. 11.726 + 11.727 +#+srcname: hello-simple-app 11.728 +#+begin_src clojure :results silent 11.729 +(ns hello.hello-simple-app) 11.730 +(require 'cortex.import) 11.731 +(use 'clojure.contrib.def) 11.732 +(rlm.rlm-commands/help) 11.733 +(cortex.import/import-jme3) 11.734 +(use 'cortex.world) 11.735 + 11.736 + 11.737 +(def cube (Box. Vector3f/ZERO 1 1 1)) 11.738 + 11.739 +(def geom (Geometry. "Box" cube)) 11.740 + 11.741 +(def mat (Material. (asset-manager) "Common/MatDefs/Misc/Unshaded.j3md")) 11.742 + 11.743 +(.setColor mat "Color" ColorRGBA/Blue) 11.744 + 11.745 +(.setMaterial geom mat) 11.746 + 11.747 +(defn simple-app [] 11.748 + (doto 11.749 + (proxy [SimpleApplication] [] 11.750 + (simpleInitApp 11.751 + [] 11.752 + ;; Don't take control of the mouse 11.753 + (org.lwjgl.input.Mouse/setGrabbed false) 11.754 + (.attachChild (.getRootNode this) geom))) 11.755 + ;; don't show a menu to change options. 11.756 + (.setShowSettings false) 11.757 + (.setPauseOnLostFocus false) 11.758 + (.setSettings *app-settings*))) 11.759 +#+end_src 11.760 + 11.761 +Running this program will begin a new jMonkeyEngine game which 11.762 +displays a single blue cube. 11.763 + 11.764 +#+begin_src clojure :exports code :results silent 11.765 +(.start (hello.hello-simple-app/simple-app)) 11.766 +#+end_src 11.767 + 11.768 +#+caption: the simplest JME game. 11.769 +[[./images/simple-app.jpg]] 11.770 + 11.771 + 11.772 + 11.773 +*** Hello Physics 11.774 +From http://jmonkeyengine.org/wiki/doku.php/jme3:beginner:hello_physics 11.775 + 11.776 +#+srcname: brick-wall-header 11.777 +#+begin_src clojure :results silent 11.778 +(ns hello.brick-wall) 11.779 +(require 'cortex.import) 11.780 +(use 'clojure.contrib.def) 11.781 +(rlm.rlm-commands/help) 11.782 +(cortex.import/mega-import-jme3) 11.783 +(use '[pokemon [lpsolve :only [constant-map]]]) 11.784 +(use 'cortex.world) 11.785 +#+end_src 11.786 + 11.787 +#+srcname: brick-wall-body 11.788 +#+begin_src clojure :results silent 11.789 +(in-ns 'hello.brick-wall) 11.790 + 11.791 +(defn floor 11.792 + "make a sturdy, unmovable physical floor" 11.793 + [] 11.794 + (box 20 1 20 :mass 0 :color false :position (Vector3f. 0 -2 0))) 11.795 + 11.796 +(def brick-length 0.48) 11.797 +(def brick-width 0.24) 11.798 +(def brick-height 0.12) 11.799 + 11.800 + 11.801 +(defn brick* [position] 11.802 + (doto (box brick-length brick-height brick-width 11.803 + :position position :name "brick" 11.804 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.805 + :texture "Textures/Terrain/BrickWall/BrickWall.jpg" 11.806 + :mass 36) 11.807 + (-> 11.808 + (.getMesh) 11.809 + (.scaleTextureCoordinates (Vector2f. 1 0.5))) 11.810 + ;;(.setShadowMode RenderQueue$ShadowMode/CastAndReceive) 11.811 + ) 11.812 + ) 11.813 + 11.814 +(defn inception-brick-wall 11.815 + "construct a physical brick wall" 11.816 + [] 11.817 + (let [node (Node. "brick-wall")] 11.818 + (dorun 11.819 + (map (comp #(.attachChild node %) brick*) 11.820 + (for 11.821 + [x (range 15) 11.822 + y (range 10) 11.823 + z (range 1)] 11.824 + (Vector3f. 11.825 + (* brick-length x 1.03) 11.826 + (* brick-width y y 10) 11.827 + (* brick-height z))))) 11.828 + node)) 11.829 + 11.830 +(defn gravity-toggle 11.831 + [new-value] 11.832 + (fn [game value] 11.833 + (println-repl "set gravity to " new-value) 11.834 + (if value 11.835 + (set-gravity* game new-value) 11.836 + (set-gravity* game gravity)))) 11.837 + 11.838 +(defn fire-cannon-ball [] 11.839 + (fn [game value] 11.840 + (if (not value) 11.841 + (let [camera (.getCamera game) 11.842 + cannon-ball 11.843 + (sphere 0.7 11.844 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.845 + :texture "Textures/PokeCopper.jpg" 11.846 + :position 11.847 + (.add (.getLocation camera) 11.848 + (.mult (.getDirection camera) (float 1))) 11.849 + :mass 3)] ;200 0.05 11.850 + (.setShadowMode cannon-ball RenderQueue$ShadowMode/CastAndReceive) 11.851 + (.setLinearVelocity 11.852 + (.getControl cannon-ball RigidBodyControl) 11.853 + (.mult (.getDirection camera) (float 50))) ;50 11.854 + (add-element game cannon-ball))))) 11.855 + 11.856 +(defn floor* [] 11.857 + (doto (box 10 0.1 5 :name "floor" ;10 0.1 5 ; 240 0.1 240 11.858 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.859 + :texture "Textures/Terrain/Pond/Pond.png" 11.860 + :position (Vector3f. 0 -0.1 0 ) 11.861 + :mass 0) 11.862 + (-> 11.863 + (.getMesh) 11.864 + (.scaleTextureCoordinates (Vector2f. 3 6)));64 64 11.865 + (-> 11.866 + (.getMaterial) 11.867 + (.getTextureParam "ColorMap") 11.868 + (.getTextureValue) 11.869 + (.setWrap Texture$WrapMode/Repeat)) 11.870 + (.setShadowMode RenderQueue$ShadowMode/Receive) 11.871 + )) 11.872 + 11.873 +(defn brick-wall* [] 11.874 + (let [node (Node. "brick-wall")] 11.875 + (dorun 11.876 + (map 11.877 + (comp #(.attachChild node %) brick*) 11.878 + (for [y (range 15) 11.879 + x (range 4) 11.880 + z (range 1)] 11.881 + (Vector3f. 11.882 + (+ (* 2 x brick-length) 11.883 + (if (even? (+ y z)) 11.884 + (/ brick-length 4) (/ brick-length -4))) 11.885 + (+ (* brick-height (inc (* 2 y)))) 11.886 + (* 2 z brick-width) )))) 11.887 + (.setShadowMode node RenderQueue$ShadowMode/CastAndReceive) 11.888 + node)) 11.889 + 11.890 +(defn brick-wall-game-run [] 11.891 + (doto 11.892 + (world 11.893 + (doto (Node.) (.attachChild (floor*)) 11.894 + (.attachChild (brick-wall*)) 11.895 + ) 11.896 + {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81)) 11.897 + "key-m" (gravity-toggle (Vector3f. 0 0 9.81)) 11.898 + "key-l" (gravity-toggle (Vector3f. 9.81 0 0)) 11.899 + "key-j" (gravity-toggle (Vector3f. -9.81 0 0)) 11.900 + "key-k" (gravity-toggle Vector3f/ZERO) 11.901 + "key-u" (gravity-toggle (Vector3f. 0 9.81 0)) 11.902 + "key-o" (gravity-toggle (Vector3f. 0 -9.81 0)) 11.903 + "key-f" (fn[game value] 11.904 + (if (not value) (add-element game (brick-wall*)))) 11.905 + "key-return" (fire-cannon-ball)} 11.906 + position-camera 11.907 + (fn [& _])) 11.908 + (.start))) 11.909 +#+end_src 11.910 + 11.911 +#+begin_src clojure :results silent 11.912 +(hello.brick-wall/brick-wall-game-run) 11.913 +#+end_src 11.914 + 11.915 +#+caption: the brick wall standing 11.916 +[[./images/brick-wall-standing.jpg]] 11.917 + 11.918 +#+caption: the brick wall after it has been knocked over by a "pok\eacute{}ball" 11.919 +[[./images/brick-wall-knocked-down.jpg]] 11.920 + 11.921 +*** Other Brick Games 11.922 +#+srcname: other-games 11.923 +#+begin_src clojure :results silent 11.924 +(ns cortex.other-games 11.925 + {:author "Dylan Holmes"}) 11.926 +(use 'cortex.world) 11.927 +(use 'hello.brick-wall) 11.928 +(use 'cortex.import) 11.929 +(cortex.import/mega-import-jme3) 11.930 + 11.931 +(defn scad [position] 11.932 + (doto (box 0.1 0.1 0.1 11.933 + :position position :name "brick" 11.934 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.935 + :texture "Textures/Terrain/BrickWall/BrickWall.jpg" 11.936 + :mass 20) 11.937 + (-> 11.938 + (.getMesh) 11.939 + (.scaleTextureCoordinates (Vector2f. 1 0.5)) 11.940 + ) 11.941 + (-> (.getControl RigidBodyControl) 11.942 + (.setLinearVelocity (Vector3f. 0 100 0)) 11.943 + ) 11.944 + 11.945 + ;;(.setShadowMode RenderQueue$ShadowMode/Cast) 11.946 + )) 11.947 + 11.948 + 11.949 +(defn shrapnel [] 11.950 + (let [node (Node. "explosion-day")] 11.951 + (dorun 11.952 + (map 11.953 + (comp #(.attachChild node %) scad) 11.954 + (for [y (range 15) 11.955 + x (range 4) 11.956 + z (range 1)] 11.957 + (Vector3f. 11.958 + (+ (* 2 x brick-height) 11.959 + (if (even? (+ y z)) (/ brick-height 4) (/ brick-height -4))) 11.960 + (+ (* brick-height (inc (* 2 y)))) 11.961 + (* 2 z brick-height) )))) 11.962 + node)) 11.963 + 11.964 + 11.965 +(def domino-height 0.48) 11.966 +(def domino-thickness 0.12) 11.967 +(def domino-width 0.24) 11.968 + 11.969 +(def domino-thickness 0.05) 11.970 +(def domino-width 0.5) 11.971 +(def domino-height 1) 11.972 + 11.973 +(defn domino 11.974 + ([position] 11.975 + (domino position (Quaternion/IDENTITY))) 11.976 + ([position rotation] 11.977 + (doto (box domino-width domino-height domino-thickness 11.978 + :position position :name "domino" 11.979 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.980 + :texture "Textures/Terrain/BrickWall/BrickWall.jpg" 11.981 + :mass 1 11.982 + :rotation rotation) 11.983 + (.setShadowMode RenderQueue$ShadowMode/CastAndReceive) 11.984 + ))) 11.985 + 11.986 + 11.987 +(defn domino-row [] 11.988 + (let [node (Node. "domino-row")] 11.989 + (dorun 11.990 + (map 11.991 + (comp #(.attachChild node %) domino) 11.992 + (for [ 11.993 + z (range 10) 11.994 + x (range 5) 11.995 + ] 11.996 + (Vector3f. 11.997 + (+ (* z domino-width) (* x 5 domino-width)) 11.998 + (/ domino-height 1) 11.999 + (* -5.5 domino-thickness z) )))) 11.1000 + 11.1001 + node)) 11.1002 + 11.1003 +(defn domino-cycle [] 11.1004 + (let [node (Node. "domino-cycle")] 11.1005 + (dorun 11.1006 + (map 11.1007 + (comp #(.attachChild node %) (partial apply domino) ) 11.1008 + (for [n (range 720)] 11.1009 + (let [space (* domino-height 5.5) 11.1010 + r (fn[n] (* (+ n 3) domino-width 0.5)) 11.1011 + t (fn[n] (reduce 11.1012 + + 11.1013 + (map 11.1014 + (fn dt[n] (/ space (* 2 (Math/PI) (r n)))) 11.1015 + (range n)))) 11.1016 + t (t n) 11.1017 + r (r n) 11.1018 + ct (Math/cos t) 11.1019 + st (Math/sin t) 11.1020 + ] 11.1021 + (list 11.1022 + (Vector3f. 11.1023 + (* -1 r st) 11.1024 + (/ domino-height 1) 11.1025 + (* r ct)) 11.1026 + (.fromAngleAxis (Quaternion.) 11.1027 + (- (/ 3.1415926 2) t) (Vector3f. 0 1 0)) 11.1028 + ))) 11.1029 + )) 11.1030 + node)) 11.1031 + 11.1032 + 11.1033 +(defn domino-game-run [] 11.1034 + (doto 11.1035 + (world 11.1036 + (doto (Node.) (.attachChild (floor*)) 11.1037 + ) 11.1038 + {"key-i" (gravity-toggle (Vector3f. 0 0 -9.81)) 11.1039 + "key-m" (gravity-toggle (Vector3f. 0 0 9.81)) 11.1040 + "key-l" (gravity-toggle (Vector3f. 9.81 0 0)) 11.1041 + "key-j" (gravity-toggle (Vector3f. -9.81 0 0)) 11.1042 + "key-k" (gravity-toggle (Vector3f. 0 9.81 0) ) 11.1043 + "key-u" (fn[g v] ((gravity-toggle (Vector3f. 0 -0 0)) g true)) 11.1044 + "key-o" (gravity-toggle (Vector3f. 0 -9.81 0)) 11.1045 + 11.1046 + "key-space" 11.1047 + (fn[game value] 11.1048 + 11.1049 + (if (not value) 11.1050 + (let [d (domino (Vector3f. 0 (/ domino-height 0.25) 0) 11.1051 + (.fromAngleAxis (Quaternion.) 11.1052 + (/ Math/PI 2) (Vector3f. 0 1 0)))] 11.1053 + (add-element game d)))) 11.1054 + "key-f" 11.1055 + (fn[game value](if (not value) (add-element game (domino-cycle)))) 11.1056 + "key-return" (fire-cannon-ball)} 11.1057 + position-camera 11.1058 + (fn [& _])) 11.1059 + (.start))) 11.1060 +#+end_src 11.1061 + 11.1062 +#+begin_src clojure :results silent 11.1063 +(cortex.other-games/domino-game-run) 11.1064 +#+end_src 11.1065 + 11.1066 +#+caption: floating dominos 11.1067 +[[./images/dominos.jpg]] 11.1068 + 11.1069 +*** Hello Loop 11.1070 +#+srcname: hello-loop 11.1071 +#+begin_src clojure :results silent 11.1072 +(ns hello.loop) 11.1073 +(use 'cortex.world) 11.1074 +(use 'cortex.import) 11.1075 +(cortex.import/mega-import-jme3) 11.1076 +(rlm.rlm-commands/help) 11.1077 + 11.1078 +(defn blue-cube [] 11.1079 + (box 1 1 1 11.1080 + :color ColorRGBA/Blue 11.1081 + :texture false 11.1082 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.1083 + :name "blue-cube" 11.1084 + :physical? false)) 11.1085 + 11.1086 +(defn blue-cube-game [] 11.1087 + (let [cube (blue-cube) 11.1088 + root (doto (Node.) (.attachChild cube))] 11.1089 + (world root 11.1090 + {} 11.1091 + no-op 11.1092 + (fn [game tpf] 11.1093 + (.rotate cube 0.0 (* 2 tpf) 0.0))))) 11.1094 +#+end_src 11.1095 + 11.1096 +*** Hello Collision 11.1097 + 11.1098 +#+srcname: hello-collision 11.1099 +#+begin_src clojure :results silent 11.1100 +(ns hello.collision) 11.1101 +(use 'cortex.world) 11.1102 +(use 'cortex.import) 11.1103 +(use 'clojure.contrib.def) 11.1104 + 11.1105 + 11.1106 +(cortex.import/mega-import-jme3) 11.1107 +(rlm.rlm-commands/help) 11.1108 +(use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]]) 11.1109 + 11.1110 + 11.1111 +(defn environment [] 11.1112 + (let 11.1113 + [scene-model 11.1114 + (doto 11.1115 + (.loadModel 11.1116 + (doto (asset-manager) 11.1117 + (.registerLocator 11.1118 + "/home/r/cortex/assets/zips/town.zip" ZipLocator)) 11.1119 + "main.scene") 11.1120 + (.setLocalScale (float 2.0))) 11.1121 + collision-shape 11.1122 + (CollisionShapeFactory/createMeshShape #^Node scene-model) 11.1123 + landscape (RigidBodyControl. collision-shape 0)] 11.1124 + (.setShadowMode scene-model RenderQueue$ShadowMode/CastAndReceive) 11.1125 + (.addControl scene-model landscape) 11.1126 + scene-model)) 11.1127 + 11.1128 +(defn player-fn [] 11.1129 + (doto 11.1130 + (CharacterControl. 11.1131 + (CapsuleCollisionShape. (float 1.5) (float 6)(float 1)) 11.1132 + (float 0.05)) 11.1133 + (.setJumpSpeed 20) 11.1134 + (.setFallSpeed 30) 11.1135 + (.setGravity 30) ;30 11.1136 + (.setPhysicsLocation (Vector3f. 0 10 0)))) 11.1137 + 11.1138 +(defn lights [] 11.1139 + [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 1 1 1) (float 1)))) 11.1140 + (doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 1 0.7 0 1) (float 1)))) 11.1141 + (doto (DirectionalLight.) 11.1142 + (.setColor (.mult ColorRGBA/White (float 0.9) )) 11.1143 + (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))]) 11.1144 + 11.1145 +(defn night-lights [] 11.1146 + [(doto (AmbientLight.) (.setColor (.mult (ColorRGBA. 0.275 0.467 0.784 1) (float 0.3)))) 11.1147 + (doto (DirectionalLight.) 11.1148 + (.setColor (.mult ColorRGBA/White (float 0.2) )) 11.1149 + (.setDirection (.normalizeLocal (Vector3f. 2.8 -28 2.8))))]) 11.1150 + 11.1151 +(def player (atom (player-fn))) 11.1152 + 11.1153 +(defn setup-fn [game] 11.1154 + (dorun (map #(.addLight (.getRootNode game) %) (lights))) 11.1155 + ;; set the color of the sky 11.1156 + (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1)) 11.1157 + ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1) 11.1158 + (doto (.getFlyByCamera game) 11.1159 + (.setMoveSpeed (float 100)) 11.1160 + (.setRotationSpeed 3)) 11.1161 + (.add 11.1162 + (.getPhysicsSpace 11.1163 + (.getState (.getStateManager game) BulletAppState)) 11.1164 + @player) 11.1165 + 11.1166 + (doto (Node.) (.attachChild (.getRootNode game)) 11.1167 + (.attachChild (brick-wall*)) 11.1168 + ) 11.1169 + 11.1170 +) 11.1171 + 11.1172 + 11.1173 +(def walking-up? (atom false)) 11.1174 +(def walking-down? (atom false)) 11.1175 +(def walking-left? (atom false)) 11.1176 +(def walking-right? (atom false)) 11.1177 + 11.1178 +(defn set-walk [walk-atom game value] 11.1179 + ;;(println-repl "setting stuff to " value) 11.1180 + (reset! walk-atom value)) 11.1181 + 11.1182 +(defn responses [] 11.1183 + {"key-w" (partial set-walk walking-up?) 11.1184 + "key-d" (partial set-walk walking-right?) 11.1185 + "key-s" (partial set-walk walking-down?) 11.1186 + "key-a" (partial set-walk walking-left?) 11.1187 + "key-return" (fire-cannon-ball) 11.1188 + "key-space" (fn [game value] (.jump @player)) 11.1189 + }) 11.1190 + 11.1191 +(defn update-fn 11.1192 + [game tpf] 11.1193 + (let [camera (.getCamera game) 11.1194 + cam-dir (.multLocal 11.1195 + (.clone 11.1196 + (.getDirection camera)) (float 0.6)) 11.1197 + cam-left (.multLocal 11.1198 + (.clone 11.1199 + (.getLeft camera)) (float 0.4)) 11.1200 + walk-direction (Vector3f. 0 0 0)] 11.1201 + 11.1202 + (cond 11.1203 + @walking-up? (.addLocal walk-direction cam-dir) 11.1204 + @walking-right? (.addLocal walk-direction (.negate cam-left)) 11.1205 + @walking-down? (.addLocal walk-direction (.negate cam-dir)) 11.1206 + @walking-left? (.addLocal walk-direction cam-left)) 11.1207 + (.setWalkDirection @player walk-direction) 11.1208 + (.setLocation camera (.getPhysicsLocation @player)))) 11.1209 + 11.1210 +(defn run-game [] 11.1211 + (.start 11.1212 + (world (environment) 11.1213 + (responses) 11.1214 + setup-fn 11.1215 + update-fn))) 11.1216 +#+end_src 11.1217 + 11.1218 +*** Hello Terrain 11.1219 +#+srcname: hello-terrain 11.1220 +#+begin_src clojure :results silent 11.1221 +(ns hello.terrain) 11.1222 +(use 'cortex.world) 11.1223 +(use 'cortex.import) 11.1224 +(use 'clojure.contrib.def) 11.1225 +(import jme3tools.converters.ImageToAwt) 11.1226 + 11.1227 + 11.1228 +(cortex.import/mega-import-jme3) 11.1229 +(rlm.rlm-commands/help) 11.1230 +(use '[hello [brick-wall :only [fire-cannon-ball brick-wall*]]]) 11.1231 + 11.1232 + 11.1233 +(defn setup-fn [type game] 11.1234 + (.setMoveSpeed (.getFlyByCamera game) 50) 11.1235 + (.setFrustumFar (.getCamera game) 10000) 11.1236 + (let [env (environment type) 11.1237 + cameras [(.getCamera game)] 11.1238 + control (TerrainLodControl. env cameras)] 11.1239 + ;;(.addControl env control) 11.1240 + (.attachChild (.getRootNode game) env))) 11.1241 + 11.1242 +(defn environment [type] 11.1243 + (let 11.1244 + [mat_terrain 11.1245 + (Material. (asset-manager) "Common/MatDefs/Terrain/Terrain.j3md") 11.1246 + grass (.loadTexture (asset-manager) "Textures/Terrain/splat/grass.jpg") 11.1247 + dirt (.loadTexture (asset-manager) "Textures/Terrain/splat/dirt.jpg") 11.1248 + rock (.loadTexture (asset-manager) "Textures/Terrain/splat/road.jpg") 11.1249 + heightmap-image (.loadTexture (asset-manager) 11.1250 + ({:mountain "Textures/Terrain/splat/mountains512.png" 11.1251 + :fortress "Textures/Terrain/splat/fortress512.png" 11.1252 + }type)) 11.1253 + heightmap (ImageBasedHeightMap. 11.1254 + (ImageToAwt/convert (.getImage heightmap-image) false true 0)) 11.1255 + terrain (do (.load heightmap) 11.1256 + (TerrainQuad. "my terrain" 65 513 (.getHeightMap heightmap))) 11.1257 + ] 11.1258 + 11.1259 + (dorun (map #(.setWrap % Texture$WrapMode/Repeat) 11.1260 + [grass dirt rock])) 11.1261 + 11.1262 + (doto mat_terrain 11.1263 + (.setTexture "Tex1" grass) 11.1264 + (.setFloat "Tex1Scale" (float 64)) 11.1265 + 11.1266 + (.setTexture "Tex2" dirt) 11.1267 + (.setFloat "Tex2Scale" (float 32)) 11.1268 + 11.1269 + (.setTexture "Tex3" rock) 11.1270 + (.setFloat "Tex3Scale" (float 128)) 11.1271 + 11.1272 + (.setTexture "Alpha" 11.1273 + (.loadTexture 11.1274 + (asset-manager) 11.1275 + ({:mountain "Textures/Terrain/splat/alphamap.png" 11.1276 + :fortress "Textures/Terrain/splat/alphamap2.png"} type)))) 11.1277 + 11.1278 + (doto terrain 11.1279 + (.setMaterial mat_terrain) 11.1280 + (.setLocalTranslation 0 -100 0) 11.1281 + (.setLocalScale 2 1 2)))) 11.1282 + 11.1283 + 11.1284 + 11.1285 +(defn run-terrain-game [type] 11.1286 + (.start 11.1287 + (world 11.1288 + (Node.) 11.1289 + {} 11.1290 + (partial setup-fn type) 11.1291 + no-op))) 11.1292 +#+end_src 11.1293 + 11.1294 + 11.1295 + 11.1296 +#+srcname: hello-animation 11.1297 +#+begin_src clojure :results silent 11.1298 +(ns hello.animation) 11.1299 +(use 'cortex.world) 11.1300 +(use 'cortex.import) 11.1301 +(use 'clojure.contrib.def) 11.1302 +(cortex.import/mega-import-jme3) 11.1303 +(rlm.rlm-commands/help) 11.1304 +(use '[hello [collision :only [lights]]]) 11.1305 + 11.1306 +(defn stand 11.1307 + [channel] 11.1308 + (doto channel 11.1309 + (.setAnim "stand" (float 0.5)) 11.1310 + (.setLoopMode LoopMode/DontLoop) 11.1311 + (.setSpeed (float 1)))) 11.1312 + 11.1313 +(defn anim-listener [] 11.1314 + (proxy [AnimEventListener] [] 11.1315 + (onAnimChange 11.1316 + [control channel animation-name] 11.1317 + (println-repl "RLM --- onAnimChange")) 11.1318 + (onAnimCycleDone 11.1319 + [control channel animation-name] 11.1320 + (if (= animation-name "Walk") 11.1321 + (stand channel) 11.1322 + )))) 11.1323 + 11.1324 +(defn setup-fn [channel game] 11.1325 + (dorun (map #(.addLight (.getRootNode game) %) (lights))) 11.1326 + ;; set the color of the sky 11.1327 + (.setBackgroundColor (.getViewPort game) (ColorRGBA. 0.3 0.4 0.9 1)) 11.1328 + ;(.setBackgroundColor (.getViewPort game) (ColorRGBA. 0 0 0 1) 11.1329 + (.setAnim channel "stand") 11.1330 + (doto (.getFlyByCamera game) 11.1331 + (.setMoveSpeed (float 10)) 11.1332 + (.setRotationSpeed 1))) 11.1333 + 11.1334 +(defn walk [channel] 11.1335 + (println-repl "zzz") 11.1336 + (doto channel 11.1337 + (.setAnim "Walk" (float 0.5)) 11.1338 + (.setLoopMode LoopMode/Loop))) 11.1339 + 11.1340 + 11.1341 +(defn key-map [channel] 11.1342 + {"key-space" (fn [game value] 11.1343 + (if (not value) 11.1344 + (walk channel)))}) 11.1345 + 11.1346 +(defn player [] 11.1347 + (let [model (.loadModel (asset-manager) "Models/Oto/Oto.mesh.xml") 11.1348 + control (.getControl model AnimControl)] 11.1349 + (.setLocalScale model (float 0.5)) 11.1350 + (.clearListeners control) 11.1351 + (.addListener control (anim-control)) 11.1352 + model)) 11.1353 + 11.1354 + 11.1355 + 11.1356 +(defn run-anim-game [] 11.1357 + (let [ninja (player) 11.1358 + control (.getControl ninja AnimControl) 11.1359 + channel (.createChannel control)] 11.1360 + (.start 11.1361 + (world 11.1362 + ninja 11.1363 + (key-map channel) 11.1364 + (partial setup-fn channel) 11.1365 + no-op)))) 11.1366 +#+end_src 11.1367 + 11.1368 +*** Hello Materials 11.1369 +#+srcname: material 11.1370 +#+begin_src clojure :results silent 11.1371 +(ns hello.material) 11.1372 +(use 'cortex.world) 11.1373 +(use 'cortex.import) 11.1374 +(use 'clojure.contrib.def) 11.1375 +(cortex.import/mega-import-jme3) 11.1376 +(rlm.rlm-commands/help) 11.1377 + 11.1378 +(defn simple-cube [] 11.1379 + (box 1 1 1 11.1380 + :position (Vector3f. -3 1.1 0) 11.1381 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.1382 + :texture "Interface/Logo/Monkey.jpg" 11.1383 + :physical? false)) 11.1384 + 11.1385 +(defn leaky-box [] 11.1386 + (box 1 1 1 11.1387 + :position (Vector3f. 3 -1 0) 11.1388 + :material "Common/MatDefs/Misc/ColoredTextured.j3md" 11.1389 + :texture "Textures/ColoredTex/Monkey.png" 11.1390 + :color (ColorRGBA. 1 0 1 1) 11.1391 + :physical? false)) 11.1392 + 11.1393 +(defn transparent-box [] 11.1394 + (doto 11.1395 + (box 1 1 0.1 11.1396 + :position Vector3f/ZERO 11.1397 + :name "window frame" 11.1398 + :material "Common/MatDefs/Misc/Unshaded.j3md" 11.1399 + :texture "Textures/ColoredTex/Monkey.png" 11.1400 + :physical? false) 11.1401 + (-> (.getMaterial) 11.1402 + (.getAdditionalRenderState) 11.1403 + (.setBlendMode RenderState$BlendMode/Alpha)) 11.1404 + (.setQueueBucket RenderQueue$Bucket/Transparent))) 11.1405 + 11.1406 +(defn bumpy-sphere [] 11.1407 + (doto 11.1408 + (sphere 2 11.1409 + :position (Vector3f. 0 2 -2) 11.1410 + :name "Shiny rock" 11.1411 + :material "Common/MatDefs/Light/Lighting.j3md" 11.1412 + :texture false 11.1413 + :physical? false) 11.1414 + (-> (.getMesh) 11.1415 + (doto 11.1416 + (.setTextureMode Sphere$TextureMode/Projected) 11.1417 + (TangentBinormalGenerator/generate))) 11.1418 + (-> (.getMaterial) 11.1419 + (doto 11.1420 + (.setTexture "DiffuseMap" (.loadTexture (asset-manager) 11.1421 + "Textures/Terrain/Pond/Pond.png")) 11.1422 + (.setTexture "NormalMap" (.loadTexture (asset-manager) 11.1423 + "Textures/Terrain/Pond/Pond_normal.png")) 11.1424 + (.setFloat "Shininess" (float 5)))) 11.1425 + (.rotate (float 1.6) 0 0))) 11.1426 + 11.1427 + 11.1428 +(defn start-game [] 11.1429 + (.start 11.1430 + (world 11.1431 + (let [root (Node.)] 11.1432 + (dorun (map #(.attachChild root %) 11.1433 + [(simple-cube) (leaky-box) (transparent-box) (bumpy-sphere)])) 11.1434 + root) 11.1435 + {} 11.1436 + (fn [world] 11.1437 + (let [sun (doto (DirectionalLight.) 11.1438 + (.setDirection (.normalizeLocal (Vector3f. 1 0 -2))) 11.1439 + (.setColor ColorRGBA/White))] 11.1440 + (.addLight (.getRootNode world) sun))) 11.1441 + no-op 11.1442 + ))) 11.1443 +#+end_src 11.1444 + 11.1445 + 11.1446 + 11.1447 +* The Body 11.1448 +** Eyes 11.1449 + 11.1450 +Ultimately I want to make creatures with eyes. Each eye can be 11.1451 +independely moved and should see its own version of the world 11.1452 +depending on where it is. 11.1453 +#+srcname: eyes 11.1454 +#+begin_src clojure 11.1455 +(ns body.eye) 11.1456 +(use 'cortex.world) 11.1457 +(use 'cortex.import) 11.1458 +(use 'clojure.contrib.def) 11.1459 +(cortex.import/mega-import-jme3) 11.1460 +(rlm.rlm-commands/help) 11.1461 +(import java.nio.ByteBuffer) 11.1462 +(import java.awt.image.BufferedImage) 11.1463 +(import java.awt.Color) 11.1464 +(import java.awt.Dimension) 11.1465 +(import java.awt.Graphics) 11.1466 +(import java.awt.Graphics2D) 11.1467 +(import java.awt.event.WindowAdapter) 11.1468 +(import java.awt.event.WindowEvent) 11.1469 +(import java.awt.image.BufferedImage) 11.1470 +(import java.nio.ByteBuffer) 11.1471 +(import javax.swing.JFrame) 11.1472 +(import javax.swing.JPanel) 11.1473 +(import javax.swing.SwingUtilities) 11.1474 +(import javax.swing.ImageIcon) 11.1475 +(import javax.swing.JOptionPane) 11.1476 +(import java.awt.image.ImageObserver) 11.1477 + 11.1478 + 11.1479 + 11.1480 +(defn scene-processor 11.1481 + "deals with converting FrameBuffers to BufferedImages so 11.1482 + that the continuation function can be defined only in terms 11.1483 + of what it does with BufferedImages" 11.1484 + [continuation] 11.1485 + (let [byte-buffer (atom nil) 11.1486 + renderer (atom nil) 11.1487 + image (atom nil)] 11.1488 + (proxy [SceneProcessor] [] 11.1489 + (initialize 11.1490 + [renderManager viewPort] 11.1491 + (let [cam (.getCamera viewPort) 11.1492 + width (.getWidth cam) 11.1493 + height (.getHeight cam)] 11.1494 + (reset! renderer (.getRenderer renderManager)) 11.1495 + (reset! byte-buffer 11.1496 + (BufferUtils/createByteBuffer 11.1497 + (* width height 4))) 11.1498 + (reset! image (BufferedImage. width height 11.1499 + BufferedImage/TYPE_4BYTE_ABGR)))) 11.1500 + (isInitialized [] (not (nil? @byte-buffer))) 11.1501 + (reshape [_ _ _]) 11.1502 + (preFrame [_]) 11.1503 + (postQueue [_]) 11.1504 + (postFrame 11.1505 + [#^FrameBuffer fb] 11.1506 + (.clear @byte-buffer) 11.1507 + (.readFrameBuffer @renderer fb @byte-buffer) 11.1508 + (Screenshots/convertScreenShot @byte-buffer @image) 11.1509 + (continuation @image)) 11.1510 + (cleanup [])))) 11.1511 + 11.1512 +(defn add-eye 11.1513 + "Add an eye to the world, and call continuation on 11.1514 + every frame produced" 11.1515 + [world camera continuation] 11.1516 + (let [width (.getWidth camera) 11.1517 + height (.getHeight camera) 11.1518 + render-manager (.getRenderManager world) 11.1519 + viewport (.createMainView render-manager "eye-view" camera)] 11.1520 + (doto viewport 11.1521 + (.setBackgroundColor ColorRGBA/Black) 11.1522 + (.setClearFlags true true true) 11.1523 + (.addProcessor (scene-processor continuation)) 11.1524 + (.attachScene (.getRootNode world))))) 11.1525 + 11.1526 +(defn make-display-frame [display width height] 11.1527 + (SwingUtilities/invokeLater 11.1528 + (fn [] 11.1529 + (.setPreferredSize display (Dimension. width height)) 11.1530 + (doto (JFrame. "Eye Camera!") 11.1531 + (-> (.getContentPane) (.add display)) 11.1532 + (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE) 11.1533 + (.pack) 11.1534 + (.setLocationRelativeTo nil) 11.1535 + (.setResizable false) 11.1536 + (.setVisible true))))) 11.1537 + 11.1538 +(defn image-monitor [#^BufferedImage image] 11.1539 + (proxy [JPanel] [] 11.1540 + (paintComponent 11.1541 + [g] 11.1542 + (proxy-super paintComponent g) 11.1543 + (locking image 11.1544 + (.drawImage g image 0 0 11.1545 + (proxy [ImageObserver] 11.1546 + [] 11.1547 + (imageUpdate 11.1548 + [] 11.1549 + (proxy-super imageUpdate)))))))) 11.1550 + 11.1551 +(defn movie-image [] 11.1552 + (let [setup 11.1553 + (runonce 11.1554 + (fn [#^BufferedImage image] 11.1555 + (let [width (.getWidth image) 11.1556 + height (.getHeight image) 11.1557 + display (image-monitor image) 11.1558 + frame (make-display-frame display width height)] 11.1559 + display)))] 11.1560 + (fn [#^BufferedImage image] 11.1561 + (.repaint (setup image))))) 11.1562 + 11.1563 + 11.1564 +(defn observer 11.1565 + "place thy eye!" 11.1566 + [world camera] 11.1567 + (let [eye camera 11.1568 + width (.getWidth eye) 11.1569 + height (.getHeight eye)] 11.1570 + (no-exceptions 11.1571 + (add-eye 11.1572 + world 11.1573 + eye 11.1574 + (movie-image))))) 11.1575 +#+end_src 11.1576 + 11.1577 +#+srcname: test-vision 11.1578 +#+begin_src clojure 11.1579 + 11.1580 +(ns test.vision) 11.1581 +(use 'cortex.world) 11.1582 +(use 'cortex.import) 11.1583 +(use 'clojure.contrib.def) 11.1584 +(use 'body.eye) 11.1585 +(cortex.import/mega-import-jme3) 11.1586 +(rlm.rlm-commands/help) 11.1587 +(import java.nio.ByteBuffer) 11.1588 +(import java.awt.image.BufferedImage) 11.1589 +(import java.awt.Color) 11.1590 +(import java.awt.Dimension) 11.1591 +(import java.awt.Graphics) 11.1592 +(import java.awt.Graphics2D) 11.1593 +(import java.awt.event.WindowAdapter) 11.1594 +(import java.awt.event.WindowEvent) 11.1595 +(import java.awt.image.BufferedImage) 11.1596 +(import java.nio.ByteBuffer) 11.1597 +(import javax.swing.JFrame) 11.1598 +(import javax.swing.JPanel) 11.1599 +(import javax.swing.SwingUtilities) 11.1600 +(import javax.swing.ImageIcon) 11.1601 +(import javax.swing.JOptionPane) 11.1602 +(import java.awt.image.ImageObserver) 11.1603 + 11.1604 + 11.1605 +(def width 200) 11.1606 +(def height 200) 11.1607 + 11.1608 +(defn camera [] 11.1609 + (doto (Camera. width height) 11.1610 + (.setFrustumPerspective 45 1 1 1000) 11.1611 + (.setLocation (Vector3f. -3 0 -5)) 11.1612 + (.lookAt Vector3f/ZERO Vector3f/UNIT_Y))) 11.1613 + 11.1614 +(defn camera2 [] 11.1615 + (doto (Camera. width height) 11.1616 + (.setFrustumPerspective 45 1 1 1000) 11.1617 + (.setLocation (Vector3f. 3 0 -5)) 11.1618 + (.lookAt Vector3f/ZERO Vector3f/UNIT_Y))) 11.1619 + 11.1620 +(defn setup-fn [world] 11.1621 + (let [eye (camera) 11.1622 + width (.getWidth eye) 11.1623 + height (.getHeight eye)] 11.1624 + (no-exceptions 11.1625 + (add-eye 11.1626 + world 11.1627 + eye 11.1628 + (runonce visual)) 11.1629 + (add-eye 11.1630 + world 11.1631 + (camera2) 11.1632 + (runonce visual))))) 11.1633 + 11.1634 +(defn spider-eye [position] 11.1635 + (doto (Camera. 200 200 ) 11.1636 + (.setFrustumPerspective 45 1 1 1000) 11.1637 + (.setLocation position) 11.1638 + (.lookAt Vector3f/ZERO Vector3f/UNIT_Y))) 11.1639 + 11.1640 +(defn setup-fn* [world] 11.1641 + (let [eye (camera) 11.1642 + width (.getWidth eye) 11.1643 + height (.getHeight eye)] 11.1644 + ;;(.setClearFlags (.getViewPort world) true true true) 11.1645 + (observer world (.getCamera world)) 11.1646 + (observer world (spider-eye (Vector3f. 3 0 -5))) 11.1647 + ;;(observer world (spider-eye (Vector3f. 0 0 -5))) 11.1648 + ;; (observer world (spider-eye (Vector3f. -3 0 -5))) 11.1649 + ;; (observer world (spider-eye (Vector3f. 0 3 -5))) 11.1650 + ;; (observer world (spider-eye (Vector3f. 0 -3 -5))) 11.1651 + ;; (observer world (spider-eye (Vector3f. 3 3 -5))) 11.1652 + ;; (observer world (spider-eye (Vector3f. -3 3 -5))) 11.1653 + ;; (observer world (spider-eye (Vector3f. 3 -3 -5))) 11.1654 + ;; (observer world (spider-eye (Vector3f. -3 -3 -5))) 11.1655 + 11.1656 + ) 11.1657 + world) 11.1658 + 11.1659 +(defn test-world [] 11.1660 + (let [thing (box 1 1 1 :physical? false)] 11.1661 + (world 11.1662 + (doto (Node.) 11.1663 + (.attachChild thing)) 11.1664 + {} 11.1665 + setup-fn 11.1666 + (fn [world tpf] 11.1667 + (.rotate thing (* tpf 0.2) 0 0) 11.1668 + )))) 11.1669 + 11.1670 + 11.1671 +#+end_src 11.1672 + 11.1673 + 11.1674 +#+results: eyes 11.1675 +: #'body.eye/test-world 11.1676 + 11.1677 +Note the use of continuation passing style for connecting the eye to a 11.1678 +function to process the output. The example code will create two 11.1679 +videos of the same rotating cube from different angles, sutiable for 11.1680 +stereoscopic vision. 11.1681 + 11.1682 + 11.1683 + 11.1684 + 11.1685 + 11.1686 + 11.1687 +* COMMENT code generation 11.1688 +#+begin_src clojure :tangle ../src/cortex/import.clj 11.1689 +<<import>> 11.1690 +#+end_src 11.1691 + 11.1692 +#+begin_src clojure :tangle ../src/hello/brick_wall.clj 11.1693 +<<brick-wall-header>> 11.1694 +<<brick-wall-body>> 11.1695 +#+end_src 11.1696 + 11.1697 +#+begin_src clojure :tangle ../src/hello/hello_simple_app.clj 11.1698 +<<hello-simple-app>> 11.1699 +#+end_src 11.1700 + 11.1701 +#+begin_src clojure :tangle ../src/cortex/world.clj 11.1702 +<<world-inputs>> 11.1703 +<<world>> 11.1704 +<<world-shapes>> 11.1705 +<<world-view>> 11.1706 +#+end_src 11.1707 + 11.1708 +#+begin_src clojure :tangle ../src/cortex/other_games.clj 11.1709 +<<other-games>> 11.1710 +#+end_src 11.1711 + 11.1712 +#+begin_src clojure :tangle ../src/hello/loop.clj 11.1713 +<<hello-loop>> 11.1714 +#+end_src 11.1715 + 11.1716 +#+begin_src clojure :tangle ../src/hello/collision.clj 11.1717 +<<hello-collision>> 11.1718 +#+end_src 11.1719 + 11.1720 +#+begin_src clojure :tangle ../src/hello/terrain.clj 11.1721 +<<hello-terrain>> 11.1722 +#+end_src 11.1723 + 11.1724 +#+begin_src clojure :tangle ../src/hello/animation.clj 11.1725 +<<hello-animation>> 11.1726 +#+end_src 11.1727 + 11.1728 +#+begin_src clojure :tangle ../src/hello/material.clj 11.1729 +<<material>> 11.1730 +#+end_src 11.1731 + 11.1732 +#+begin_src clojure :tangle ../src/body/eye.clj 11.1733 +<<eyes>> 11.1734 +#+end_src 11.1735 + 11.1736 +#+begin_src clojure :tangle ../src/test/vision.clj 11.1737 +<<test-vision>> 11.1738 +#+end_src 11.1739 + 11.1740 + 11.1741 +
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/org/skin.org Sun Oct 16 05:12:19 2011 -0700 12.3 @@ -0,0 +1,322 @@ 12.4 +#+title: SKIN! 12.5 +#+author: Robert McIntyre 12.6 +#+email: rlm@mit.edu 12.7 +#+MATHJAX: align:"left" mathml:t path:"../aurellem/src/MathJax/MathJax.js" 12.8 +#+STYLE: <link rel="stylesheet" type="text/css" href="../aurellem/src/css/aurellem.css"/> 12.9 +#+BABEL: :exports both :noweb yes :cache no :mkdirp yes 12.10 +#+INCLUDE: ../aurellem/src/templates/level-0.org 12.11 +#+description: Simulating touch in JMonkeyEngine 12.12 + 12.13 + 12.14 + 12.15 + 12.16 +* skin! 12.17 + 12.18 +#+srcname: skin-main 12.19 +#+begin_src clojure 12.20 +(ns body.skin) 12.21 +(use 'cortex.world) 12.22 +(use 'cortex.import) 12.23 +(use 'clojure.contrib.def) 12.24 +(cortex.import/mega-import-jme3) 12.25 +(rlm.rlm-commands/help) 12.26 + 12.27 +(import java.util.logging.Level) 12.28 +(import java.util.logging.Logger) 12.29 + 12.30 + 12.31 + 12.32 +;; looks like we can use GhostControls for implementing touch. 12.33 +;; for example: 12.34 +(def rc (GhostControl. (BoxCollisionShape. (Vector3f. 2 2 2)))) 12.35 +(def shifted-sphere 12.36 + (doto (CompoundCollisionShape.) 12.37 + (.addChildShape (SphereCollisionShape. 3) (Vector3f. 1 0 0)))) 12.38 + 12.39 +(def gc (GhostControl. shifted-sphere)) 12.40 +(def b (box 1 1 1 :mass 1)) 12.41 +(.addControl b rc) 12.42 +(.addControl b gc) 12.43 + 12.44 + 12.45 +(defn looksies! [] 12.46 + (view b)) 12.47 + 12.48 +;; overlapping objects can be gotten via =.getOverlappingCount= and 12.49 +;; =.getOverlappingObjects= 12.50 + 12.51 +;; looks like I might be able to place a small "touch-sphere" whther 12.52 +;; on every triangle in the object's mesh, or at the verticies, using 12.53 +;; .getTriangleCount() on the mesh gotten by .getMesh() 12.54 + 12.55 +;; this way, I can create a mesh and just divide up it's faces using 12.56 +;; blender, and this will create the touch sensor distribution. 12.57 + 12.58 + 12.59 +(defn make-touch-sphere [#^Geometry geom] 12.60 + (let [tri (Triangle.) 12.61 + mesh (.getMesh geom) 12.62 + controls! (transient [])] 12.63 + (dorun 12.64 + (for [n (range (.getTriangleCount mesh))] 12.65 + (do 12.66 + (.getTriangle mesh n tri) 12.67 + (.calculateCenter tri) 12.68 + (let [control 12.69 + (doto 12.70 + (GhostControl. 12.71 + (doto (CompoundCollisionShape.) 12.72 + (.addChildShape 12.73 + (SphereCollisionShape. (float 0.1)) 12.74 + (.mult (.getCenter tri) (float 1))) 12.75 + (.setMargin -0.1))) 12.76 + (.setApplyPhysicsLocal true))] 12.77 + 12.78 + (.addControl geom control) 12.79 + (conj! controls! control))))) 12.80 + (persistent! controls!))) 12.81 + 12.82 + 12.83 +(defn make-touch [#^Geometry geom] 12.84 + (let [tri (Triangle.) 12.85 + mesh (.getMesh geom) 12.86 + controls! (transient [])] 12.87 + (dorun 12.88 + (for [n (range (.getTriangleCount mesh))] 12.89 + (do 12.90 + (.getTriangle mesh n tri) 12.91 + (.calculateCenter tri) 12.92 + (.calculateNormal tri) 12.93 + (println-repl tri) 12.94 + (println-repl (.get1 tri)) 12.95 + (println-repl (.get2 tri)) 12.96 + (println-repl (.get3 tri)) 12.97 + (println-repl (.getCenter tri)) 12.98 + (println-repl (.getNormal tri)) 12.99 + (let [control 12.100 + (doto 12.101 + (GhostControl. 12.102 + 12.103 + (doto (CompoundCollisionShape.) 12.104 + (.addChildShape 12.105 + (SimplexCollisionShape. Vector3f/ZERO) 12.106 + (.mult (.getCenter tri) (float 1))) 12.107 + (.setMargin 0) 12.108 + )) 12.109 + (.setApplyPhysicsLocal true))] 12.110 + 12.111 + (.addControl geom control) 12.112 + (conj! controls! control))))) 12.113 + (persistent! controls!))) 12.114 + 12.115 +(defn make-fucked-touch [#^Geometry geom] 12.116 + (let [tri (Triangle.) 12.117 + mesh (.getMesh geom) 12.118 + controls! (transient [])] 12.119 + (dorun 12.120 + (for [n (range (.getTriangleCount mesh))] 12.121 + (do 12.122 + (.getTriangle mesh n tri) 12.123 + (.calculateCenter tri) 12.124 + (.calculateNormal tri) 12.125 + (println-repl tri) 12.126 + (println-repl (.get1 tri)) 12.127 + (println-repl (.get2 tri)) 12.128 + (println-repl (.get3 tri)) 12.129 + (println-repl (.getCenter tri)) 12.130 + (println-repl (.getNormal tri)) 12.131 + (let [control1 12.132 + (doto 12.133 + (GhostControl. 12.134 + (doto (CompoundCollisionShape.) 12.135 + (.addChildShape 12.136 + (SimplexCollisionShape. Vector3f/ZERO) 12.137 + (.get1 tri)))) 12.138 + (.setApplyPhysicsLocal true))] 12.139 + 12.140 + (.addControl geom control1) 12.141 + (conj! controls! control1) 12.142 + ) 12.143 + 12.144 + ;; (let [control1 12.145 + ;; (doto 12.146 + ;; (GhostControl. 12.147 + ;; (doto (CompoundCollisionShape.) 12.148 + ;; (.addChildShape 12.149 + ;; (SimplexCollisionShape. Vector3f/ZERO) 12.150 + ;; (.get2 tri)))) 12.151 + ;; (.setApplyPhysicsLocal true))] 12.152 + 12.153 + ;; (.addControl geom control1) 12.154 + ;; (conj! controls! control1) 12.155 + ;; ) 12.156 + 12.157 + ;; (let [control1 12.158 + ;; (doto 12.159 + ;; (GhostControl. 12.160 + ;; (doto (CompoundCollisionShape.) 12.161 + ;; (.addChildShape 12.162 + ;; (SimplexCollisionShape. Vector3f/ZERO) 12.163 + ;; (.get3 tri)))) 12.164 + ;; (.setApplyPhysicsLocal true))] 12.165 + 12.166 + ;; (.addControl geom control1) 12.167 + ;; (conj! controls! control1) 12.168 + 12.169 + ))) 12.170 + (persistent! controls!))) 12.171 + 12.172 + 12.173 + 12.174 +(use 'hello.brick-wall) 12.175 + 12.176 +(defn touch-reception [controls] 12.177 + (let [control 12.178 + (first 12.179 + (filter 12.180 + #(< 2 (.getOverlappingCount %)) controls))] 12.181 + (if (not (nil? control)) 12.182 + (println 12.183 + (seq 12.184 + (.getOverlappingObjects control)))))) 12.185 + 12.186 +(defn touch-print [controls] 12.187 + (println 12.188 + (map #(count (.getNonGhostOverlappingObjects %)) controls))) 12.189 + 12.190 +(defn set-debug-color [#^ColorRGBA color #^GhostControl gc] 12.191 + (.setColor (.getMaterial (.getChild (.getDebugShape gc) 0)) "Color" color)) 12.192 + 12.193 +(defn html-color [html-str] 12.194 + ((fn [[r g b]] (ColorRGBA. r g b (float 1))) 12.195 + (map #(float (/ (Integer/parseInt % 16) 255)) 12.196 + (map (partial apply str) (partition 2 html-str))))) 12.197 + 12.198 + 12.199 +(defn color-touch [controls] 12.200 + (no-exceptions 12.201 + (dorun 12.202 + (map 12.203 + (fn [control] 12.204 + (case (count (.getNonGhostOverlappingObjects control)) 12.205 + 0 (set-debug-color ColorRGBA/Gray control) 12.206 + 1 (set-debug-color ColorRGBA/Blue control) 12.207 + 2 (set-debug-color ColorRGBA/Green control) 12.208 + 3 (set-debug-color ColorRGBA/Yellow control) 12.209 + 4 (set-debug-color ColorRGBA/Orange control) 12.210 + 5 (set-debug-color ColorRGBA/Red control) 12.211 + 6 (set-debug-color ColorRGBA/Magenta control) 12.212 + 7 (set-debug-color ColorRGBA/Pink control) 12.213 + 8 (set-debug-color ColorRGBA/White control))) 12.214 + controls)))) 12.215 + 12.216 +(defn enable-debug [world] 12.217 + (.enableDebug 12.218 + (.getPhysicsSpace 12.219 + (.getState 12.220 + (.getStateManager world) 12.221 + BulletAppState)) 12.222 + (asset-manager))) 12.223 + 12.224 +(def with-debug 12.225 + '(1 1 1 1 0 1 2 0 2 0 1 1 1 1 0 2 0 2 2 1 0 0 0 1 0 0 0 0 1 0)) 12.226 +(def no-debug 12.227 + '(1 1 1 1 0 1 2 0 2 0 1 1 1 1 0 2 0 2 2 1 0 0 0 1 0 0 0 0 1 0)) 12.228 + 12.229 + 12.230 + 12.231 +(defn transparent-sphere [] 12.232 + (doto 12.233 + (make-shape 12.234 + (merge base-shape 12.235 + {:position (Vector3f. 0 2 0) 12.236 + :name "the blob." 12.237 + :material "Common/MatDefs/Misc/Unshaded.j3md" 12.238 + :texture "Textures/purpleWisp.png" 12.239 + :physical? true 12.240 + :mass 70 12.241 + :color ColorRGBA/Blue 12.242 + :shape (Sphere. 10 10 1)})) 12.243 + (-> (.getMaterial) 12.244 + (.getAdditionalRenderState) 12.245 + (.setBlendMode RenderState$BlendMode/Alpha)) 12.246 + (.setQueueBucket RenderQueue$Bucket/Transparent))) 12.247 + 12.248 +(defn transparent-box [] 12.249 + (doto 12.250 + (make-shape 12.251 + (merge base-shape 12.252 + {:position (Vector3f. 0 2 0) 12.253 + :name "the blob." 12.254 + :material "Common/MatDefs/Misc/Unshaded.j3md" 12.255 + :texture "Textures/purpleWisp.png" 12.256 + :physical? true 12.257 + :mass 70 12.258 + :color ColorRGBA/Blue 12.259 + :shape (Box. 1 1 1)})) 12.260 + (-> (.getMaterial) 12.261 + (.getAdditionalRenderState) 12.262 + (.setBlendMode RenderState$BlendMode/Alpha)) 12.263 + (.setQueueBucket RenderQueue$Bucket/Transparent))) 12.264 + 12.265 + 12.266 + 12.267 +(defn no-logging [] 12.268 + (.setLevel (Logger/getLogger "com.jme3") Level/OFF)) 12.269 + 12.270 +(defn set-accuracy [world new-accuracy] 12.271 + (let [physics-manager (.getState (.getStateManager world) BulletAppState)] 12.272 + (.setAccuracy (.getPhysicsSpace physics-manager) (float new-accuracy)))) 12.273 + 12.274 +(defn test-skin [] 12.275 + (let [b 12.276 + ;;(transparent-box) 12.277 + (transparent-sphere) 12.278 + 12.279 + controls 12.280 + ;;(make-touch-sphere b) 12.281 + (make-touch b) 12.282 + ] 12.283 + 12.284 + (world 12.285 + (doto (Node.) (.attachChild b) 12.286 + (.attachChild 12.287 + (doto 12.288 + (box 5 0.2 5 :mass 0 :position (Vector3f. 0 -2 0) 12.289 + :material "Common/MatDefs/Misc/Unshaded.j3md" 12.290 + :texture "Textures/redWisp.png") 12.291 + (-> (.getMaterial) 12.292 + (.getAdditionalRenderState) 12.293 + (.setBlendMode RenderState$BlendMode/Alpha)) 12.294 + (.setQueueBucket RenderQueue$Bucket/Transparent)))) 12.295 + 12.296 + {"key-return" (fire-cannon-ball) 12.297 + "key-space" (fn [game value] 12.298 + (touch-print controls))} 12.299 + (fn [world] 12.300 + (Capture/SimpleCaptureVideo world (file-str "~/out-temp/blob.avi")) 12.301 + (no-logging) 12.302 + (enable-debug world) 12.303 + (set-accuracy world (/ 1 60)) 12.304 + ) 12.305 + 12.306 + (fn [& _] 12.307 + (touch-print controls) 12.308 + (color-touch controls) 12.309 + )))) 12.310 + 12.311 +#+end_src 12.312 + 12.313 + 12.314 + 12.315 + 12.316 + 12.317 + 12.318 +* COMMENT code generation 12.319 +#+begin_src clojure :tangle ../src/body/skin.clj 12.320 +<<skin-main>> 12.321 +#+end_src 12.322 + 12.323 + 12.324 + 12.325 +