jax 0.0.0.9 → 0.0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (426) hide show
  1. data/CHANGELOG +6 -0
  2. data/jax.gemspec +6 -2
  3. data/lib/jax/generators/app/templates/public/javascripts/jax.js +1 -1
  4. data/lib/jax/version.rb +1 -1
  5. metadata +3 -533
  6. data/guides/assets/images/book_icon.gif +0 -0
  7. data/guides/assets/images/bullet.gif +0 -0
  8. data/guides/assets/images/chapters_icon.gif +0 -0
  9. data/guides/assets/images/check_bullet.gif +0 -0
  10. data/guides/assets/images/credits_pic_blank.gif +0 -0
  11. data/guides/assets/images/edge_badge.png +0 -0
  12. data/guides/assets/images/feature_tile.gif +0 -0
  13. data/guides/assets/images/footer_tile.gif +0 -0
  14. data/guides/assets/images/getting_started/clean_passing_jasmine_suite.png +0 -0
  15. data/guides/assets/images/getting_started/dungeon-complete.png +0 -0
  16. data/guides/assets/images/getting_started/dungeon-normal-map.png +0 -0
  17. data/guides/assets/images/getting_started/dungeon-rainbow-textured.png +0 -0
  18. data/guides/assets/images/getting_started/dungeon-rainbow.png +0 -0
  19. data/guides/assets/images/getting_started/dungeon-textured-lighting.png +0 -0
  20. data/guides/assets/images/getting_started/dungeon-textured.png +0 -0
  21. data/guides/assets/images/getting_started/rock.png +0 -0
  22. data/guides/assets/images/getting_started/rock_normal.png +0 -0
  23. data/guides/assets/images/getting_started/teapot-red-directional-point.png +0 -0
  24. data/guides/assets/images/getting_started/teapot-red-directional.png +0 -0
  25. data/guides/assets/images/getting_started/teapot-red-nolight.png +0 -0
  26. data/guides/assets/images/getting_started/teapot-red-spot-point-directional.png +0 -0
  27. data/guides/assets/images/getting_started/teapot-white.png +0 -0
  28. data/guides/assets/images/getting_started/teapot-with-model.png +0 -0
  29. data/guides/assets/images/grey_bullet.gif +0 -0
  30. data/guides/assets/images/header_backdrop.png +0 -0
  31. data/guides/assets/images/header_tile.gif +0 -0
  32. data/guides/assets/images/icons/README +0 -5
  33. data/guides/assets/images/icons/callouts/1.png +0 -0
  34. data/guides/assets/images/icons/callouts/10.png +0 -0
  35. data/guides/assets/images/icons/callouts/11.png +0 -0
  36. data/guides/assets/images/icons/callouts/12.png +0 -0
  37. data/guides/assets/images/icons/callouts/13.png +0 -0
  38. data/guides/assets/images/icons/callouts/14.png +0 -0
  39. data/guides/assets/images/icons/callouts/15.png +0 -0
  40. data/guides/assets/images/icons/callouts/2.png +0 -0
  41. data/guides/assets/images/icons/callouts/3.png +0 -0
  42. data/guides/assets/images/icons/callouts/4.png +0 -0
  43. data/guides/assets/images/icons/callouts/5.png +0 -0
  44. data/guides/assets/images/icons/callouts/6.png +0 -0
  45. data/guides/assets/images/icons/callouts/7.png +0 -0
  46. data/guides/assets/images/icons/callouts/8.png +0 -0
  47. data/guides/assets/images/icons/callouts/9.png +0 -0
  48. data/guides/assets/images/icons/caution.png +0 -0
  49. data/guides/assets/images/icons/example.png +0 -0
  50. data/guides/assets/images/icons/home.png +0 -0
  51. data/guides/assets/images/icons/important.png +0 -0
  52. data/guides/assets/images/icons/next.png +0 -0
  53. data/guides/assets/images/icons/note.png +0 -0
  54. data/guides/assets/images/icons/prev.png +0 -0
  55. data/guides/assets/images/icons/tip.png +0 -0
  56. data/guides/assets/images/icons/up.png +0 -0
  57. data/guides/assets/images/icons/warning.png +0 -0
  58. data/guides/assets/images/nav_arrow.gif +0 -0
  59. data/guides/assets/images/tab_grey.gif +0 -0
  60. data/guides/assets/images/tab_info.gif +0 -0
  61. data/guides/assets/images/tab_note.gif +0 -0
  62. data/guides/assets/images/tab_red.gif +0 -0
  63. data/guides/assets/images/tab_yellow.gif +0 -0
  64. data/guides/assets/images/tab_yellow.png +0 -0
  65. data/guides/assets/javascripts/guides.js +0 -7
  66. data/guides/assets/javascripts/syntaxhighlighter/shBrushAS3.js +0 -59
  67. data/guides/assets/javascripts/syntaxhighlighter/shBrushAppleScript.js +0 -75
  68. data/guides/assets/javascripts/syntaxhighlighter/shBrushBash.js +0 -59
  69. data/guides/assets/javascripts/syntaxhighlighter/shBrushCSharp.js +0 -65
  70. data/guides/assets/javascripts/syntaxhighlighter/shBrushColdFusion.js +0 -100
  71. data/guides/assets/javascripts/syntaxhighlighter/shBrushCpp.js +0 -125
  72. data/guides/assets/javascripts/syntaxhighlighter/shBrushCss.js +0 -91
  73. data/guides/assets/javascripts/syntaxhighlighter/shBrushDelphi.js +0 -55
  74. data/guides/assets/javascripts/syntaxhighlighter/shBrushDiff.js +0 -41
  75. data/guides/assets/javascripts/syntaxhighlighter/shBrushErlang.js +0 -52
  76. data/guides/assets/javascripts/syntaxhighlighter/shBrushGroovy.js +0 -67
  77. data/guides/assets/javascripts/syntaxhighlighter/shBrushJScript.js +0 -52
  78. data/guides/assets/javascripts/syntaxhighlighter/shBrushJava.js +0 -57
  79. data/guides/assets/javascripts/syntaxhighlighter/shBrushJavaFX.js +0 -58
  80. data/guides/assets/javascripts/syntaxhighlighter/shBrushPerl.js +0 -72
  81. data/guides/assets/javascripts/syntaxhighlighter/shBrushPhp.js +0 -88
  82. data/guides/assets/javascripts/syntaxhighlighter/shBrushPlain.js +0 -33
  83. data/guides/assets/javascripts/syntaxhighlighter/shBrushPowerShell.js +0 -74
  84. data/guides/assets/javascripts/syntaxhighlighter/shBrushPython.js +0 -64
  85. data/guides/assets/javascripts/syntaxhighlighter/shBrushRuby.js +0 -55
  86. data/guides/assets/javascripts/syntaxhighlighter/shBrushSass.js +0 -94
  87. data/guides/assets/javascripts/syntaxhighlighter/shBrushScala.js +0 -51
  88. data/guides/assets/javascripts/syntaxhighlighter/shBrushSql.js +0 -66
  89. data/guides/assets/javascripts/syntaxhighlighter/shBrushVb.js +0 -56
  90. data/guides/assets/javascripts/syntaxhighlighter/shBrushXml.js +0 -69
  91. data/guides/assets/javascripts/syntaxhighlighter/shCore.js +0 -17
  92. data/guides/assets/stylesheets/fixes.css +0 -16
  93. data/guides/assets/stylesheets/main.css +0 -445
  94. data/guides/assets/stylesheets/print.css +0 -52
  95. data/guides/assets/stylesheets/reset.css +0 -43
  96. data/guides/assets/stylesheets/style.css +0 -13
  97. data/guides/assets/stylesheets/syntaxhighlighter/shCore.css +0 -226
  98. data/guides/assets/stylesheets/syntaxhighlighter/shCoreDefault.css +0 -328
  99. data/guides/assets/stylesheets/syntaxhighlighter/shCoreDjango.css +0 -331
  100. data/guides/assets/stylesheets/syntaxhighlighter/shCoreEclipse.css +0 -339
  101. data/guides/assets/stylesheets/syntaxhighlighter/shCoreEmacs.css +0 -324
  102. data/guides/assets/stylesheets/syntaxhighlighter/shCoreFadeToGrey.css +0 -328
  103. data/guides/assets/stylesheets/syntaxhighlighter/shCoreMDUltra.css +0 -324
  104. data/guides/assets/stylesheets/syntaxhighlighter/shCoreMidnight.css +0 -324
  105. data/guides/assets/stylesheets/syntaxhighlighter/shCoreRDark.css +0 -324
  106. data/guides/assets/stylesheets/syntaxhighlighter/shThemeDefault.css +0 -117
  107. data/guides/assets/stylesheets/syntaxhighlighter/shThemeDjango.css +0 -120
  108. data/guides/assets/stylesheets/syntaxhighlighter/shThemeEclipse.css +0 -128
  109. data/guides/assets/stylesheets/syntaxhighlighter/shThemeEmacs.css +0 -113
  110. data/guides/assets/stylesheets/syntaxhighlighter/shThemeFadeToGrey.css +0 -117
  111. data/guides/assets/stylesheets/syntaxhighlighter/shThemeJaxGuides.css +0 -116
  112. data/guides/assets/stylesheets/syntaxhighlighter/shThemeMDUltra.css +0 -113
  113. data/guides/assets/stylesheets/syntaxhighlighter/shThemeMidnight.css +0 -113
  114. data/guides/assets/stylesheets/syntaxhighlighter/shThemeRDark.css +0 -113
  115. data/guides/jax_guides.rb +0 -41
  116. data/guides/jax_guides/common.rb +0 -7
  117. data/guides/jax_guides/generator.rb +0 -287
  118. data/guides/jax_guides/helpers.rb +0 -29
  119. data/guides/jax_guides/indexer.rb +0 -69
  120. data/guides/jax_guides/levenshtein.rb +0 -31
  121. data/guides/jax_guides/textile_extensions.rb +0 -61
  122. data/guides/partials/_top_nav.html.erb +0 -17
  123. data/guides/source/getting_started.textile +0 -1210
  124. data/guides/source/index.html.erb +0 -61
  125. data/guides/source/layout.html.erb +0 -147
  126. data/guides/source/lighting.textile +0 -5
  127. data/guides/source/materials.textile +0 -5
  128. data/guides/source/matrices.textile +0 -5
  129. data/guides/source/shaders.textile +0 -499
  130. data/guides/source/testing.textile +0 -5
  131. data/guides/w3c_validator.rb +0 -91
  132. data/public/images/brickwall.jpg +0 -0
  133. data/public/images/jacks.jpg +0 -0
  134. data/public/images/normal_map.jpg +0 -0
  135. data/public/images/rock.png +0 -0
  136. data/public/images/rockNormal.png +0 -0
  137. data/public/images/rss.png +0 -0
  138. data/public/webgl_not_supported.html +0 -26
  139. data/spec/example_app/Gemfile +0 -1
  140. data/spec/example_app/Rakefile +0 -15
  141. data/spec/example_app/app/controllers/application_controller.js +0 -5
  142. data/spec/example_app/app/controllers/courthouse_controller.js +0 -24
  143. data/spec/example_app/app/controllers/lighting_controller.js +0 -67
  144. data/spec/example_app/app/controllers/noise_controller.js +0 -50
  145. data/spec/example_app/app/controllers/picking_controller.js +0 -32
  146. data/spec/example_app/app/controllers/shadows_controller.js +0 -130
  147. data/spec/example_app/app/controllers/textures_controller.js +0 -52
  148. data/spec/example_app/app/helpers/application_helper.js +0 -3
  149. data/spec/example_app/app/helpers/noise_helper.js +0 -3
  150. data/spec/example_app/app/helpers/picking_helper.js +0 -3
  151. data/spec/example_app/app/helpers/textures_helper.js +0 -3
  152. data/spec/example_app/app/models/blob.js +0 -38
  153. data/spec/example_app/app/models/character.js +0 -23
  154. data/spec/example_app/app/models/door.js +0 -10
  155. data/spec/example_app/app/models/scene.js +0 -19
  156. data/spec/example_app/app/resources/blobs/default.yml +0 -2
  157. data/spec/example_app/app/resources/characters/judge.yml +0 -3
  158. data/spec/example_app/app/resources/doors/courthouse_door.yml +0 -5
  159. data/spec/example_app/app/resources/light_sources/directional_light.yml +0 -13
  160. data/spec/example_app/app/resources/light_sources/point_light.yml +0 -31
  161. data/spec/example_app/app/resources/light_sources/spot_light.yml +0 -15
  162. data/spec/example_app/app/resources/light_sources/sun.yml +0 -7
  163. data/spec/example_app/app/resources/light_sources/textures_point.yml +0 -34
  164. data/spec/example_app/app/resources/materials/blob.yml +0 -28
  165. data/spec/example_app/app/resources/materials/bricks.yml +0 -36
  166. data/spec/example_app/app/resources/materials/lighting_with_shadows.yml +0 -27
  167. data/spec/example_app/app/resources/scenes/courthouse.yml +0 -4
  168. data/spec/example_app/app/shaders/blob/common.ejs +0 -13
  169. data/spec/example_app/app/shaders/blob/fragment.ejs +0 -8
  170. data/spec/example_app/app/shaders/blob/manifest.yml +0 -15
  171. data/spec/example_app/app/shaders/blob/material.js +0 -51
  172. data/spec/example_app/app/shaders/blob/vertex.ejs +0 -40
  173. data/spec/example_app/app/views/courthouse/index.js +0 -4
  174. data/spec/example_app/app/views/lighting/index.js +0 -4
  175. data/spec/example_app/app/views/noise/index.js +0 -4
  176. data/spec/example_app/app/views/picking/index.js +0 -4
  177. data/spec/example_app/app/views/shadows/index.js +0 -6
  178. data/spec/example_app/app/views/textures/index.js +0 -4
  179. data/spec/example_app/config/application.rb +0 -6
  180. data/spec/example_app/config/boot.rb +0 -13
  181. data/spec/example_app/config/routes.rb +0 -8
  182. data/spec/example_app/public/images/185__normalmap.png +0 -0
  183. data/spec/example_app/public/images/face_norm.jpg +0 -0
  184. data/spec/example_app/public/images/rock.png +0 -0
  185. data/spec/example_app/public/images/rockNormal.png +0 -0
  186. data/spec/example_app/public/images/rss.png +0 -0
  187. data/spec/example_app/public/webgl_not_supported.html +0 -26
  188. data/spec/example_app/script/jax +0 -7
  189. data/spec/example_app/spec/javascripts/controllers/noise_controller_spec.js +0 -11
  190. data/spec/example_app/spec/javascripts/controllers/picking_controller_spec.js +0 -11
  191. data/spec/example_app/spec/javascripts/controllers/textures_controller_spec.js +0 -11
  192. data/spec/example_app/spec/javascripts/helpers/application_helper_spec.js +0 -12
  193. data/spec/example_app/spec/javascripts/helpers/noise_helper_spec.js +0 -12
  194. data/spec/example_app/spec/javascripts/helpers/picking_helper_spec.js +0 -12
  195. data/spec/example_app/spec/javascripts/models/blob_spec.js +0 -11
  196. data/spec/example_app/spec/javascripts/resources/directional_light_spec.js +0 -16
  197. data/spec/example_app/spec/javascripts/resources/point_light_spec.js +0 -17
  198. data/spec/example_app/spec/javascripts/resources/spot_light_spec.js +0 -18
  199. data/spec/example_app/spec/javascripts/shaders/blob_spec.js +0 -27
  200. data/spec/example_app/spec/javascripts/support/jasmine.yml +0 -89
  201. data/spec/example_app/spec/javascripts/support/jasmine_runner.rb +0 -32
  202. data/spec/example_app/spec/javascripts/support/spec_helpers/jax_spec_environment_helper.js +0 -33
  203. data/spec/example_app/spec/javascripts/support/spec_helpers/jax_spec_helper.js +0 -93
  204. data/spec/example_app/spec/javascripts/support/spec_layout.html.erb +0 -111
  205. data/spec/javascripts/helpers/jax_spec_environment_helper.js +0 -33
  206. data/spec/javascripts/helpers/jax_spec_helper.js +0 -216
  207. data/spec/javascripts/jax/builtin/meshes/cube_spec.js +0 -10
  208. data/spec/javascripts/jax/builtin/meshes/plane_spec.js +0 -8
  209. data/spec/javascripts/jax/builtin/meshes/quad_spec.js +0 -8
  210. data/spec/javascripts/jax/builtin/meshes/sphere_spec.js +0 -8
  211. data/spec/javascripts/jax/builtin/meshes/teapot_spec.js +0 -8
  212. data/spec/javascripts/jax/builtin/meshes/torus_spec.js +0 -8
  213. data/spec/javascripts/jax/compatibility_spec.js +0 -50
  214. data/spec/javascripts/jax/context_spec.js +0 -82
  215. data/spec/javascripts/jax/controller_spec.js +0 -145
  216. data/spec/javascripts/jax/core/delegation_spec.js +0 -48
  217. data/spec/javascripts/jax/core/events_spec.js +0 -17
  218. data/spec/javascripts/jax/core/matrix_stack_spec.js +0 -28
  219. data/spec/javascripts/jax/core/utils_spec.js +0 -275
  220. data/spec/javascripts/jax/helper_spec.js +0 -33
  221. data/spec/javascripts/jax/model_spec.js +0 -97
  222. data/spec/javascripts/jax/optimizations/material_limitations_spec.js +0 -87
  223. data/spec/javascripts/jax/optimizations/shaders/basic_spec.js +0 -68
  224. data/spec/javascripts/jax/prototype/extensions_spec.js +0 -34
  225. data/spec/javascripts/jax/route_set_spec.js +0 -67
  226. data/spec/javascripts/jax/view_manager_spec.js +0 -17
  227. data/spec/javascripts/jax/webgl/camera_spec.js +0 -159
  228. data/spec/javascripts/jax/webgl/framebuffer_spec.js +0 -46
  229. data/spec/javascripts/jax/webgl/lighting_spec.js +0 -61
  230. data/spec/javascripts/jax/webgl/material_spec.js +0 -118
  231. data/spec/javascripts/jax/webgl/mesh_spec.js +0 -86
  232. data/spec/javascripts/jax/webgl/shader/manifest_spec.js +0 -60
  233. data/spec/javascripts/jax/webgl/shader_chain_spec.js +0 -192
  234. data/spec/javascripts/jax/webgl/shader_spec.js +0 -276
  235. data/spec/javascripts/jax/webgl/tangent_space_spec.js +0 -142
  236. data/spec/javascripts/jax/webgl/texture_spec.js +0 -162
  237. data/spec/javascripts/jax/webgl_spec.js +0 -5
  238. data/spec/javascripts/jax/world_spec.js +0 -161
  239. data/spec/javascripts/jax_spec.js +0 -27
  240. data/spec/javascripts/shaders/core_materials_spec.js +0 -42
  241. data/spec/javascripts/shaders/fog_spec.js +0 -19
  242. data/spec/javascripts/shaders/lighting_spec.js +0 -19
  243. data/spec/javascripts/shaders/normal_map_spec.js +0 -19
  244. data/spec/javascripts/shaders/paraboloid_spec.js +0 -37
  245. data/spec/javascripts/shaders/preprocessor_spec.js +0 -50
  246. data/spec/javascripts/shaders/shadow_map_spec.js +0 -19
  247. data/spec/javascripts/shaders/texture_spec.js +0 -40
  248. data/src/constants.yml +0 -1
  249. data/src/constants.yml.erb +0 -1
  250. data/src/jax.js +0 -119
  251. data/src/jax/anim_frame.js +0 -51
  252. data/src/jax/builtin/all.js +0 -7
  253. data/src/jax/builtin/meshes/cube.js +0 -83
  254. data/src/jax/builtin/meshes/plane.js +0 -63
  255. data/src/jax/builtin/meshes/quad.js +0 -86
  256. data/src/jax/builtin/meshes/sphere.js +0 -65
  257. data/src/jax/builtin/meshes/teapot.js +0 -48
  258. data/src/jax/builtin/meshes/torus.js +0 -64
  259. data/src/jax/compatibility.js +0 -303
  260. data/src/jax/context.js +0 -420
  261. data/src/jax/core.js +0 -42
  262. data/src/jax/core/math.js +0 -55
  263. data/src/jax/core/matrix_stack.js +0 -311
  264. data/src/jax/core/util.js +0 -253
  265. data/src/jax/events.js +0 -234
  266. data/src/jax/geometry.js +0 -7
  267. data/src/jax/geometry/plane.js +0 -113
  268. data/src/jax/mvc/controller.js +0 -195
  269. data/src/jax/mvc/helper.js +0 -35
  270. data/src/jax/mvc/model.js +0 -302
  271. data/src/jax/mvc/route_set.js +0 -125
  272. data/src/jax/mvc/view.js +0 -43
  273. data/src/jax/mvc/view_manager.js +0 -47
  274. data/src/jax/noise.js +0 -206
  275. data/src/jax/prototype/class.js +0 -81
  276. data/src/jax/prototype/core.js +0 -112
  277. data/src/jax/prototype/extensions.js +0 -142
  278. data/src/jax/vendor/ejs.js +0 -4
  279. data/src/jax/vendor/glMatrix.js +0 -3
  280. data/src/jax/webgl.js +0 -98
  281. data/src/jax/webgl/camera.js +0 -526
  282. data/src/jax/webgl/core.js +0 -6
  283. data/src/jax/webgl/core/buffer.js +0 -161
  284. data/src/jax/webgl/core/edge.js +0 -6
  285. data/src/jax/webgl/core/events.js +0 -25
  286. data/src/jax/webgl/core/face.js +0 -8
  287. data/src/jax/webgl/core/framebuffer.js +0 -211
  288. data/src/jax/webgl/material.js +0 -392
  289. data/src/jax/webgl/mesh.js +0 -427
  290. data/src/jax/webgl/scene.js +0 -11
  291. data/src/jax/webgl/scene/frustum.js +0 -260
  292. data/src/jax/webgl/scene/light_manager.js +0 -136
  293. data/src/jax/webgl/scene/light_source.js +0 -294
  294. data/src/jax/webgl/shader.js +0 -296
  295. data/src/jax/webgl/shader/delegator.js +0 -13
  296. data/src/jax/webgl/shader/delegator/attribute.js +0 -53
  297. data/src/jax/webgl/shader/delegator/uniform.js +0 -71
  298. data/src/jax/webgl/shader/manifest.js +0 -64
  299. data/src/jax/webgl/shader/program.js +0 -140
  300. data/src/jax/webgl/shader_chain.js +0 -264
  301. data/src/jax/webgl/texture.js +0 -312
  302. data/src/jax/webgl/world.js +0 -240
  303. data/vendor/ejs/.svn/all-wcprops +0 -17
  304. data/vendor/ejs/.svn/entries +0 -114
  305. data/vendor/ejs/.svn/text-base/Eratta.txt.svn-base +0 -3
  306. data/vendor/ejs/.svn/text-base/license.txt.svn-base +0 -21
  307. data/vendor/ejs/Eratta.txt +0 -3
  308. data/vendor/ejs/example/.svn/all-wcprops +0 -29
  309. data/vendor/ejs/example/.svn/entries +0 -164
  310. data/vendor/ejs/example/.svn/prop-base/ejs.gif.svn-base +0 -5
  311. data/vendor/ejs/example/.svn/text-base/demo.html.svn-base +0 -54
  312. data/vendor/ejs/example/.svn/text-base/ejs.gif.svn-base +0 -0
  313. data/vendor/ejs/example/.svn/text-base/example.css.svn-base +0 -31
  314. data/vendor/ejs/example/.svn/text-base/example.js.svn-base +0 -88
  315. data/vendor/ejs/example/demo.html +0 -54
  316. data/vendor/ejs/example/ejs.gif +0 -0
  317. data/vendor/ejs/example/example.css +0 -31
  318. data/vendor/ejs/example/example.js +0 -88
  319. data/vendor/ejs/lib/.svn/all-wcprops +0 -11
  320. data/vendor/ejs/lib/.svn/entries +0 -62
  321. data/vendor/ejs/lib/.svn/text-base/ejs_fulljslint.js.svn-base +0 -3774
  322. data/vendor/ejs/lib/ejs_fulljslint.js +0 -3774
  323. data/vendor/ejs/license.txt +0 -21
  324. data/vendor/ejs/script/.svn/all-wcprops +0 -17
  325. data/vendor/ejs/script/.svn/entries +0 -96
  326. data/vendor/ejs/script/.svn/prop-base/yuicompressor-2.2.4.jar.svn-base +0 -5
  327. data/vendor/ejs/script/.svn/text-base/production.bat.svn-base +0 -4
  328. data/vendor/ejs/script/.svn/text-base/yuicompressor-2.2.4.jar.svn-base +0 -0
  329. data/vendor/ejs/script/production.bat +0 -4
  330. data/vendor/ejs/script/yuicompressor-2.2.4.jar +0 -0
  331. data/vendor/ejs/src/.svn/all-wcprops +0 -17
  332. data/vendor/ejs/src/.svn/entries +0 -96
  333. data/vendor/ejs/src/.svn/text-base/ejs.js.svn-base +0 -505
  334. data/vendor/ejs/src/.svn/text-base/view.js.svn-base +0 -200
  335. data/vendor/ejs/src/ejs.js +0 -505
  336. data/vendor/ejs/src/view.js +0 -200
  337. data/vendor/ejs/test/.svn/all-wcprops +0 -23
  338. data/vendor/ejs/test/.svn/entries +0 -139
  339. data/vendor/ejs/test/.svn/text-base/index.html.svn-base +0 -18
  340. data/vendor/ejs/test/.svn/text-base/setup_test.html.svn-base +0 -44
  341. data/vendor/ejs/test/.svn/text-base/sidebar.html.svn-base +0 -28
  342. data/vendor/ejs/test/included/.svn/all-wcprops +0 -23
  343. data/vendor/ejs/test/included/.svn/entries +0 -130
  344. data/vendor/ejs/test/included/.svn/text-base/prototype.js.svn-base +0 -4184
  345. data/vendor/ejs/test/included/.svn/text-base/test.css.svn-base +0 -44
  346. data/vendor/ejs/test/included/.svn/text-base/unittest.js.svn-base +0 -564
  347. data/vendor/ejs/test/included/prototype.js +0 -4184
  348. data/vendor/ejs/test/included/test.css +0 -44
  349. data/vendor/ejs/test/included/unittest.js +0 -564
  350. data/vendor/ejs/test/index.html +0 -18
  351. data/vendor/ejs/test/setup_test.html +0 -44
  352. data/vendor/ejs/test/sidebar.html +0 -28
  353. data/vendor/ejs/test/templates/.svn/all-wcprops +0 -11
  354. data/vendor/ejs/test/templates/.svn/entries +0 -62
  355. data/vendor/ejs/test/templates/.svn/text-base/test.ejs.svn-base +0 -3
  356. data/vendor/ejs/test/templates/test.ejs +0 -3
  357. data/vendor/ejs/test/tests/.svn/all-wcprops +0 -23
  358. data/vendor/ejs/test/tests/.svn/entries +0 -130
  359. data/vendor/ejs/test/tests/.svn/text-base/rendering.js.svn-base +0 -94
  360. data/vendor/ejs/test/tests/.svn/text-base/templating.js.svn-base +0 -43
  361. data/vendor/ejs/test/tests/.svn/text-base/views.js.svn-base +0 -100
  362. data/vendor/ejs/test/tests/rendering.js +0 -94
  363. data/vendor/ejs/test/tests/templating.js +0 -43
  364. data/vendor/ejs/test/tests/views.js +0 -100
  365. data/vendor/glmatrix/benchmark/js/CanvasMatrix.js +0 -722
  366. data/vendor/glmatrix/benchmark/js/EWGL_math.js +0 -742
  367. data/vendor/glmatrix/benchmark/js/mjs.js +0 -1230
  368. data/vendor/glmatrix/benchmark/matrix_benchmark.html +0 -419
  369. data/vendor/glmatrix/glMatrix-min.js +0 -32
  370. data/vendor/glmatrix/glMatrix.js +0 -1772
  371. data/vendor/glmatrix/unit tests/glMatrix-test.js +0 -74
  372. data/vendor/glmatrix/unit tests/qunit.css +0 -119
  373. data/vendor/glmatrix/unit tests/qunit.js +0 -1069
  374. data/vendor/glmatrix/unit tests/test_glMatrix.html +0 -21
  375. data/vendor/pdoc_template/html/assets/images/pdoc/alias.png +0 -0
  376. data/vendor/pdoc_template/html/assets/images/pdoc/class.png +0 -0
  377. data/vendor/pdoc_template/html/assets/images/pdoc/class_deprecated.png +0 -0
  378. data/vendor/pdoc_template/html/assets/images/pdoc/class_method.png +0 -0
  379. data/vendor/pdoc_template/html/assets/images/pdoc/class_property.png +0 -0
  380. data/vendor/pdoc_template/html/assets/images/pdoc/constant.png +0 -0
  381. data/vendor/pdoc_template/html/assets/images/pdoc/constructor.png +0 -0
  382. data/vendor/pdoc_template/html/assets/images/pdoc/deprecated.png +0 -0
  383. data/vendor/pdoc_template/html/assets/images/pdoc/description.png +0 -0
  384. data/vendor/pdoc_template/html/assets/images/pdoc/information.png +0 -0
  385. data/vendor/pdoc_template/html/assets/images/pdoc/instance_method.png +0 -0
  386. data/vendor/pdoc_template/html/assets/images/pdoc/instance_property.png +0 -0
  387. data/vendor/pdoc_template/html/assets/images/pdoc/method.png +0 -0
  388. data/vendor/pdoc_template/html/assets/images/pdoc/method_deprecated.png +0 -0
  389. data/vendor/pdoc_template/html/assets/images/pdoc/mixin.png +0 -0
  390. data/vendor/pdoc_template/html/assets/images/pdoc/namespace.png +0 -0
  391. data/vendor/pdoc_template/html/assets/images/pdoc/property.png +0 -0
  392. data/vendor/pdoc_template/html/assets/images/pdoc/related_to.png +0 -0
  393. data/vendor/pdoc_template/html/assets/images/pdoc/search-background.png +0 -0
  394. data/vendor/pdoc_template/html/assets/images/pdoc/section-background.png +0 -0
  395. data/vendor/pdoc_template/html/assets/images/pdoc/section.png +0 -0
  396. data/vendor/pdoc_template/html/assets/images/pdoc/selected-section-background.png +0 -0
  397. data/vendor/pdoc_template/html/assets/images/pdoc/subclass.png +0 -0
  398. data/vendor/pdoc_template/html/assets/images/pdoc/superclass.png +0 -0
  399. data/vendor/pdoc_template/html/assets/images/pdoc/utility.png +0 -0
  400. data/vendor/pdoc_template/html/assets/javascripts/pdoc/application.js +0 -478
  401. data/vendor/pdoc_template/html/assets/javascripts/pdoc/prototype.js +0 -4874
  402. data/vendor/pdoc_template/html/assets/javascripts/pdoc/tabs.js +0 -506
  403. data/vendor/pdoc_template/html/assets/stylesheets/jax.css +0 -30
  404. data/vendor/pdoc_template/html/assets/stylesheets/pdoc/api.css +0 -681
  405. data/vendor/pdoc_template/html/assets/stylesheets/pdoc/pygments.css +0 -62
  406. data/vendor/pdoc_template/html/helpers.rb +0 -35
  407. data/vendor/pdoc_template/html/index.erb +0 -18
  408. data/vendor/pdoc_template/html/item_index.js.erb +0 -6
  409. data/vendor/pdoc_template/html/layout.erb +0 -67
  410. data/vendor/pdoc_template/html/leaf.erb +0 -22
  411. data/vendor/pdoc_template/html/node.erb +0 -30
  412. data/vendor/pdoc_template/html/partials/class_relationships.erb +0 -19
  413. data/vendor/pdoc_template/html/partials/classes.erb +0 -7
  414. data/vendor/pdoc_template/html/partials/constructor.erb +0 -5
  415. data/vendor/pdoc_template/html/partials/description.erb +0 -5
  416. data/vendor/pdoc_template/html/partials/link_list.erb +0 -1
  417. data/vendor/pdoc_template/html/partials/method_signatures.erb +0 -14
  418. data/vendor/pdoc_template/html/partials/methodized_note.erb +0 -9
  419. data/vendor/pdoc_template/html/partials/mixins.erb +0 -7
  420. data/vendor/pdoc_template/html/partials/namespaces.erb +0 -7
  421. data/vendor/pdoc_template/html/partials/related_utilities.erb +0 -5
  422. data/vendor/pdoc_template/html/partials/relationships.erb +0 -11
  423. data/vendor/pdoc_template/html/partials/short_description_list.erb +0 -7
  424. data/vendor/pdoc_template/html/partials/title.erb +0 -24
  425. data/vendor/pdoc_template/html/section.erb +0 -18
  426. data/vendor/yuicompressor-2.4.2.jar +0 -0
@@ -1,29 +0,0 @@
1
- module JaxGuides
2
- module Helpers
3
- def guide(name, url, options = {}, &block)
4
- link = content_tag(:a, :href => url) { name }
5
- result = content_tag(:dt, link)
6
-
7
- if options[:work_in_progress]
8
- result << content_tag(:dd, 'Work in progress', :class => 'work-in-progress')
9
- end
10
-
11
- result << content_tag(:dd, capture(&block))
12
- result
13
- end
14
-
15
- def author(name, nick, image = 'credits_pic_blank.gif', &block)
16
- image = "images/#{image}"
17
-
18
- result = content_tag(:img, nil, :src => image, :class => 'left pic', :alt => name)
19
- result << content_tag(:h3, name)
20
- result << content_tag(:p, capture(&block))
21
- content_tag(:div, result, :class => 'clearfix', :id => nick)
22
- end
23
-
24
- def code(&block)
25
- c = capture(&block)
26
- content_tag(:code, c)
27
- end
28
- end
29
- end
@@ -1,69 +0,0 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/ordered_hash'
3
- require 'active_support/core_ext/string/inflections'
4
-
5
- module JaxGuides
6
- class Indexer
7
- attr_reader :body, :result, :warnings, :level_hash
8
-
9
- def initialize(body, warnings)
10
- @body = body
11
- @result = @body.dup
12
- @warnings = warnings
13
- end
14
-
15
- def index
16
- @level_hash = process(body)
17
- end
18
-
19
- private
20
-
21
- def process(string, current_level=3, counters=[1])
22
- s = StringScanner.new(string)
23
-
24
- level_hash = ActiveSupport::OrderedHash.new
25
-
26
- while !s.eos?
27
- re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$}
28
- s.match?(re)
29
- if matched = s.matched
30
- matched =~ re
31
- level, idx, title = $1.to_i, $2, $3.strip
32
-
33
- if level < current_level
34
- # This is needed. Go figure.
35
- return level_hash
36
- elsif level == current_level
37
- index = counters.join(".")
38
- idx ||= '#' + title_to_idx(title)
39
-
40
- raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{idx}). #{index} #{title}")
41
-
42
- key = {
43
- :title => title,
44
- :id => idx
45
- }
46
- # Recurse
47
- counters << 1
48
- level_hash[key] = process(s.post_match, current_level + 1, counters)
49
- counters.pop
50
-
51
- # Increment the current level
52
- last = counters.pop
53
- counters << last + 1
54
- end
55
- end
56
- s.getch
57
- end
58
- level_hash
59
- end
60
-
61
- def title_to_idx(title)
62
- idx = title.strip.parameterize.sub(/^\d+/, '')
63
- if warnings && idx.blank?
64
- puts "BLANK ID: please put an explicit ID for section #{title}, as in h5(#my-id)"
65
- end
66
- idx
67
- end
68
- end
69
- end
@@ -1,31 +0,0 @@
1
- module JaxGuides
2
- module Levenshtein
3
- # Based on the pseudocode in http://en.wikipedia.org/wiki/Levenshtein_distance.
4
- def self.distance(s1, s2)
5
- s = s1.unpack('U*')
6
- t = s2.unpack('U*')
7
- m = s.length
8
- n = t.length
9
-
10
- # matrix initialization
11
- d = []
12
- 0.upto(m) { |i| d << [i] }
13
- 0.upto(n) { |j| d[0][j] = j }
14
-
15
- # distance computation
16
- 1.upto(m) do |i|
17
- 1.upto(n) do |j|
18
- cost = s[i] == t[j] ? 0 : 1
19
- d[i][j] = [
20
- d[i-1][j] + 1, # deletion
21
- d[i][j-1] + 1, # insertion
22
- d[i-1][j-1] + cost, # substitution
23
- ].min
24
- end
25
- end
26
-
27
- # all done
28
- return d[m][n]
29
- end
30
- end
31
- end
@@ -1,61 +0,0 @@
1
- #require 'active_support/core_ext/object/inclusion'
2
- # above file isn't in release yet, so we'll re-code it here -- it's only 1 method
3
- class Object
4
- # Returns true if this object is included in the argument. Argument must be
5
- # any object which responds to +#include?+. Usage:
6
- #
7
- # characters = ["Konata", "Kagami", "Tsukasa"]
8
- # "Konata".in?(characters) # => true
9
- #
10
- # This will throw an ArgumentError if the argument doesn't respond
11
- # to +#include?+.
12
- def in?(another_object)
13
- another_object.include?(self)
14
- rescue NoMethodError
15
- raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
16
- end
17
- end
18
-
19
- require File.join(File.dirname(__FILE__), 'common')
20
-
21
- module JaxGuides
22
- module TextileExtensions
23
- def notestuff(body)
24
- body.gsub!(/^(IMPORTANT|CAUTION|WARNING|NOTE|INFO)[.:](.*)$/) do |m|
25
- css_class = $1.downcase
26
- css_class = 'warning' if css_class.in?(['caution', 'important'])
27
-
28
- result = "<div class='#{css_class}'><p>"
29
- result << $2.strip
30
- result << '</p></div>'
31
- result
32
- end
33
- end
34
-
35
- def tip(body)
36
- body.gsub!(/^TIP[.:](.*)$/) do |m|
37
- result = "<div class='info'><p>"
38
- result << $1.strip
39
- result << '</p></div>'
40
- result
41
- end
42
- end
43
-
44
- def plusplus(body)
45
- body.gsub!(/\+(.*?)\+/) do |m|
46
- "<notextile><tt>#{$1}</tt></notextile>"
47
- end
48
-
49
- # The real plus sign
50
- body.gsub!('<plus>', '+')
51
- end
52
-
53
- def code(body)
54
- body.gsub!(%r{<(#{JaxGuides.code_aliases})>(.*?)</\1>}m) do |m|
55
- es = ERB::Util.h($2)
56
- css_class = $1.in?(['erb', 'shell']) ? 'html' : $1
57
- %{<notextile><div class="code_container"><code class="#{css_class}">#{es}</code></div></notextile>}
58
- end
59
- end
60
- end
61
- end
@@ -1,17 +0,0 @@
1
- <div id="topNav">
2
- <div class="wrapper">
3
- <strong>More at <a href="http://jaxgl.com">jaxgl.com:</a> </strong>
4
- <a href="http://blog.jaxgl.com/what-is-jax">Overview</a> |
5
- <%if @link_to_guides%>
6
- <a href="http://guides.jaxgl.com">Guides</a> |
7
- <%end%>
8
- <!-- <a href="http://jaxgl.com/download">Download</a> | -->
9
- <!-- <a href="http://jaxgl.com/deploy">Deploy</a> | -->
10
- <a href="http://github.com/sinisterchipmunk/jax">Code</a> |
11
- <%if !@hide_links_to_api_docs%>
12
- <a href="http://guides.jaxgl.com/api/js/index.html">Documentation</a> |
13
- <%end%>
14
- <a href="http://blog.jaxgl.com/forum">Forums</a> |
15
- <a href="http://blog.jaxgl.com/">Blog</a>
16
- </div>
17
- </div>
@@ -1,1210 +0,0 @@
1
- h2. Getting Started with Jax
2
-
3
- This guide will show you how to start developing WebGL applications using Jax. It covers the following topics:
4
-
5
- * Installing Jax and creating a new Jax application
6
- * The layout of a Jax application
7
- * The MVC (Model, View, Controller) architecture and how it applies to Jax
8
- * How to quickly generate the starting pieces of a Jax application
9
-
10
- endprologue.
11
-
12
- NOTE: Paths in this guide are relative to a Jax application unless otherwise specified.
13
-
14
- h3. Assumptions
15
-
16
- This guide is designed for beginners who want to get started on Jax applications from scratch. It does not assume you have any prior experience with Jax, but it does assume you are a JavaScript developer. Jax is a framework written largely in JavaScript, and without an understanding of basic JavaScript principles, you will be facing a steep learning curve.
17
-
18
- The guide also assumes you have the following software installed:
19
-
20
- * The "Ruby":http://ruby-lang.org language.
21
- * The "RubyGems":http://rubyforge.org/frs/?group_id=126 packaging system.
22
-
23
- h3. What is Jax?
24
-
25
- Jax is a development framework for JavaScript-driven WebGL applications. It is designed to make WebGL application development easier by making assumptions about what every WebGL application must be capable of. For instance, every WebGL application must manage one or more WebGL Contexts. Jax completely automates this task.
26
-
27
- Jax has been heavily influenced by the popular Ruby on Rails web application framework, and borrows most of its key philosophies from Rails. These concepts include the following:
28
-
29
- * Opinionation. Like Rails, Jax is opinionated software, which means that it makes an assumption that there is a "best" way to do things. If you embrace the approaches that Jax makes, you will likely discover that you can write WebGL applications in a fraction of the time it took without Jax.
30
- * DRY -- "Don't Repeat Yourself" -- means that constantly repeating the same code is a Bad Thing, and Jax makes every effort to remove the need to do this. JavaScript is itself a not-very-DRY language, (count the number of times you see the word "function"!), so there's a limit to how DRY a Jax application can be. That doesn't stop Jax from trying, however.
31
- * Convention Over Configuration -- means that Jax is going to make a lot of assumptions about what you want to do and how you want to do it. Jax won't stop you from manual configuration, but it provides a lot of defaults to try and guess what you want before you get that far.
32
-
33
- In addition to these points, Jax makes the assumption that smaller files are easier to maintain. JavaScript code tends to quickly grow very large and unwieldy, and it doesn't take long for it to become so convoluted that even its original authors have trouble maintaining it. Jax resolves this problem by splitting what would be a few monolithic source files into many smaller, more specialized files, and places these files into categories according to their function.
34
-
35
- h4. The MVC Architecture
36
-
37
- At the heart of Jax is the Model, View, Controller (usually just called MVC) architecture. This cleanly organizes the code base into three distinct regions, making it (usually) plainly obvious where a particular code change should take place and making the entire application far easier to maintain.
38
-
39
- h5. Models
40
-
41
- Models represent containers for data and the rules governing how that data is to be manipulated. In Jax, once it is determined that an action should take place, it is up to the model to actually perform the action. If, for instance, a "Monster" is expected to take damage from an attack, it is up to the "Monster" model to modify the appropriate data, perhaps begin an appropriate animation sequence, and do anything else related to taking damage.
42
-
43
- The bulk of your application's business logic will be concentrated in the models.
44
-
45
- h5. Views
46
-
47
- Views are a presentation layer; they do not manipulate data, handle user input, or contain any other non-presentation logic. In the case of Jax, views are used to render the current scene and are rarely any different from one another. A view could be changed, for instance, to add motion blur or other visual effects to the scene.
48
-
49
- h5. Controllers
50
-
51
- Controllers are the managers of your application. They tell models what to do and when to do it; in Jax, they are responsible for setting up the scene and tearing it down. They also handle all forms of user input, and can even be called automatically over time.
52
-
53
- h3. Creating a New Jax Project
54
-
55
- This guide will walk you through creating a sample application from scratch. The application will create a 3D "dungeon" that the player can move around in. It will be a first-person application, rendered as if through the eyes of the player's character.
56
-
57
- Here's a screenshot of the finished dungeon scene:
58
-
59
- !images/getting_started/dungeon-complete.png(The Dungeon)!
60
-
61
-
62
- h4. Installing Jax
63
-
64
- The first step to creating any Jax application is to make sure Jax itself is installed. Assuming you have already set up its primary dependencies, "Ruby":http://www.ruby-lang.org and "RubyGems":http://rubyforge.org/frs/?group_id=126, this should be as simple as running the following command:
65
-
66
- <shell>
67
- Usually, run this as the root user:
68
- # gem install jax
69
- </shell>
70
-
71
- This will install Jax and a number of other Ruby Gems that it relies upon.
72
-
73
- h4. Creating the Dungeon Application
74
-
75
- Now that Jax is installed, we can go ahead and generate the Dungeon application. Start a terminal or command prompt, switch to a directory you have permission to write to, and enter the following command:
76
-
77
- <shell>
78
- $ jax new dungeon
79
- </shell>
80
-
81
- This will create a Jax application called Dungeon in the +dungeon+ directory. You'll want to change into that directory for the remainder of this guide:
82
-
83
- <shell>
84
- $ cd dungeon
85
- </shell>
86
-
87
- The files that Jax has created, and descriptions of what they are for, are given below:
88
-
89
- | File/Folder | Purpose |
90
- | Gemfile | Specifies the gems your Jax application depends on. |
91
- | Rakefile | Contains batch programs that can be run from a terminal with the +rake+ command. |
92
- | app/ | Contains the majority of your application code, including controllers, models, views, and so forth. |
93
- | config/ | Contains general application configuration options which control how Jax functions behind the scenes. |
94
- | public/ | Contains static assets that your application depends on, such as stylesheets, images, 3D models, and so on. Also contains the Jax core javascripts. |
95
- | script/ | Contains the jax script that starts your application and invokes code generators. You can also place custom scripts here. |
96
- | spec/ | Contains test files, or _specs_, used to test your Jax application. |
97
- | tmp/ | Contains temporary files. These files are generated by Jax when building or testing your application, and will be frequently overwritten. |
98
-
99
- h4. Installing the Required Gems
100
-
101
- Jax applications manage their gem dependencies using "Bundler":http://gembundler.com. Since our application doesn't
102
- require any dependencies other than Jax itself, we can go ahead and lock in place the version of Jax we intend to use
103
- by running:
104
-
105
- <shell>
106
- $ bundle install
107
- </shell>
108
-
109
- h3. Jax Lives!
110
-
111
- The next thing we need to do is start the Jax application server. Do that with the following command:
112
-
113
- <shell>
114
- $ rake jasmine
115
- </shell>
116
-
117
- _Jasmine_ is the name of the test suite which will be used to run your JavaScript unit tests. You can learn more about Jasmine by "visiting its home on GitHub":https://github.com/pivotal/jasmine-gem.
118
-
119
- h4. The Development Environment
120
-
121
- In Jax, Jasmine doubles as both the test suite and the development environment. Jax alters the Jasmine run template to include an HTML5 canvas element and the necessary JavaScript code to create a Jax context, so every time you refresh the browser, you'll both run the Jasmine test suite and see your Jax application in action.
122
-
123
- Open a browser and point it to the Jasmine suite, "http://localhost:8888":http://localhost:8888, and you will see a white Canvas and some passing (green) unit tests.
124
-
125
- !images/getting_started/clean_passing_jasmine_suite.png(The Default Jasmine Suite)!
126
-
127
- Let's take a moment to explain what you're seeing. At the top-left, the Jasmine and Jax versions are shown. "Jasmine" is a link to the Jasmine homepage, where you can find more documentation. Below these headers is a green element displaying the number of specs (tests), how many failures were encountered, and how long it took to execute. To the right of this same element is a link that you can click to re-run the test suite, and above that are some checkboxes you can use to change what Jasmine is showing you.
128
-
129
- When tests fail, they take up a large chunk of the screen. The idea is that you should never have failing specs, unless you're in the middle of making them pass. If the specs are passing, then they'll take up a minimum of real estate on the screen and leave room for the Jax context, where you can _see_ your code in action.
130
-
131
- By default, the Jax development environment is purposely kept very simple. You can customize the HTML elements by altering the file +spec/javascripts/support/spec_layout.html.erb+, if your Jax application expects certain additional HTML elements to exist.
132
-
133
- TIP. You should almost never have to restart the Jasmine server. Every time you reload the test suite, Jax rebuilds your application, so the resulting JavaScript file is always up-to-date.
134
-
135
- h4. Tea Time
136
-
137
- One of the most common tests you'll find in graphics programming is rendering the obligatory Teapot. Many frameworks have the Utah Teapot built into them, and Jax is no exception. (If you want, you can also read some interesting information on the "origin of the Utah Teapot":http://en.wikipedia.org/wiki/Utah_teapot back in 1975.)
138
-
139
- h5. Generating the Controller
140
-
141
- So let's get started on this graphical "Hello, World". First, we need to generate a controller, which will be responsible for setting up the teapot scene. By convention, we'll name the controller after the type of scene it represents:
142
-
143
- <shell>
144
- $ jax generate controller teapot index
145
- </shell>
146
-
147
- Notice we added an extra argument: +index+. Controllers consist of JavaScript functions called _actions_, and this extra argument tells Jax to create an action in our Teapot controller called +index+. Actions are used by controllers to accomplish a given task. By convention, the +index+ action will be used to set up a scene for the first time. In this case, it will be responsible for creating a Teapot and positioning it within the scene.
148
-
149
- h5. Setting the Default Route
150
-
151
- When you generated the Teapot controller, you may have noticed that Jax inserted a line into the +config/routes.rb+ file. The line it inserted will register the controller with Jax so that it knows how to find the controller when it is needed. However, it doesn't actually tell Jax _when_ the controller is needed. Instead of doing this manually, we'd prefer Jax to start at the Teapot controller by default. Therefore, we need to edit the +config/routes.rb+ file and add a _root_. Add the following to the second line of this file:
152
-
153
- <ruby>
154
- root 'teapot'
155
- </ruby>
156
-
157
- Your +config/routes.rb+ file, with the automatically-generated comments omitted, should now look like this:
158
-
159
- <ruby>
160
- Dungeon.routes.map do
161
- root 'teapot'
162
- map 'teapot/index'
163
- end
164
- </ruby>
165
-
166
- h5. Adding the Teapot
167
-
168
- Refreshing your development page, you should see the Jax canvas turn from white to black. This means that Jax is now invoking your controller, and is causing WebGL to render to the canvas. Unfortunately, there's nothing to render because your controller hasn't yet set up the scene, so the result is just a black image.
169
-
170
- In order to add some color, you need to edit your +app/controllers/teapot_controller.js+ file. Inside of the +index+ function, add the following JavaScript code:
171
-
172
- <js>
173
- var teapot = new Jax.Model({
174
- position: [0, 0, -5],
175
- mesh: new Jax.Mesh.Teapot()
176
- });
177
- this.world.addObject(teapot);
178
- </js>
179
-
180
- By default, Jax positions the camera at the origin at (0, 0, 0), looking along the negative Z axis. (This is in line with OpenGL and, more importantly, WebGL standards.)
181
-
182
- Therefore, the above code will create a new Teapot object, position it 5 units in "front" of the camera, and add it to the scene (which Jax calls +world+).
183
-
184
- Reload your suite again, and you'll see the fruits of your labor!
185
-
186
- !images/getting_started/teapot-white.png(The Default Teapot)!
187
-
188
- h5. Using Materials for Color
189
-
190
- We have a teapot, but we don't have a very _impressive_ teapot. In fact, it looks rather flat, uninspiring and _white_.
191
- Let's change that. In a terminal, run the following command within your Jax application:
192
-
193
- <shell>
194
- $ jax generate material teapot
195
- </shell>
196
-
197
- This will generate a Jax material. By default, Jax will also add in support for scene lighting.
198
-
199
- TIP: This guide won't cover Jax materials in depth, but they're very powerful and very versatile. For more information regarding Jax materials, see the "Jax Material Guide":materials.html.
200
-
201
- Edit the newly-generated material file, +app/resources/material/teapot.yml+. Under +ambient+ coloring, change the +green+ and +blue+ values to 0.0, and leave the +red+ and +alpha+ values at 1.0.
202
-
203
- <yaml>
204
- ambient:
205
- red: 1.0
206
- green: 0.0
207
- blue: 0.0
208
- alpha: 1.0
209
- </yaml>
210
-
211
- Again, edit the +app/controllers/teapot_controller.js+ file and alter this line:
212
-
213
- <js>
214
- mesh: new Jax.Mesh.Teapot()
215
- </js>
216
-
217
- Replace it with the following:
218
-
219
- <js>
220
- mesh: new Jax.Mesh.Teapot({ material: Material.find("teapot") })
221
- </js>
222
-
223
- This will produce a completely opaque, red teapot. It looks depressingly two-dimensional because it's composed entirely of a single color. Next, we'll make it look more like the 3D object it really is!
224
-
225
- <img src="images/getting_started/teapot-red-nolight.png" style="width:300px;height:200px;float:left;margin-right:2em;"/>
226
- <img src="images/getting_started/teapot-red-spot-point-directional.png" style="width:300px;height:200px;clear:right;"/>
227
-
228
- h4. Using Light Sources
229
-
230
- To add a more realistic shading effect to the teapot, we'll need to add some light sources. Jax supports three kinds of lights:
231
-
232
- * Directional
233
- * Point
234
- * Spot
235
-
236
- TIP: Lighting support requires a fair number of shader variables. Nearly all video cards on the market should support it, but it _does_ exceed the theoretical lowest potential limit on shader variables. Jax will automatically drop lighting support if it can't be used, and render the scene without it. If, after completing this section, your model still looks "flat", try viewing it on a different machine.
237
-
238
- h5. Directional Lights
239
-
240
- Directional lights have no point of origin. They shine in the same direction upon every object in the scene. In the real world, they don't exist, but in the virtual world, they are useful for simulating very powerful, distant entities such as the sun.
241
-
242
- Let's start simple by adding a directional light to our scene. First, from the command line, use the light source generator:
243
-
244
- <shell>
245
- $ jax generate light sun directional
246
- </shell>
247
-
248
- Next, we need the controller to add it to the scene. Edit your controller file at +app/controllers/teapot_controller.js+ and add the following line to the +index+ action:
249
-
250
- <js>
251
- index: function() {
252
- // ...
253
- this.world.addLightSource(LightSource.find("sun"));
254
- }
255
- </js>
256
-
257
- Now edit the file the generator created, +app/resources/light_sources/sun.yml+, and change the +direction+ field to look like this:
258
-
259
- <yaml>
260
- direction:
261
- x: -1
262
- y: -1
263
- z: 1
264
- </yaml>
265
-
266
- This tells the light to shine left, down, and backward relative to world space.
267
-
268
- INFO: Remember that, currently, our camera is unmodified, so world space and camera space are the same thing. When we get around to rotating the camera, the difference between camera and world space will become more important. For more information about the various spaces and the differences between them, take a look at the "Matrices Guide":matrices.html.
269
-
270
- Reload your scene, and you should finally be starting to see some detail!
271
-
272
- !images/getting_started/teapot-red-directional.png(Teapot With Directional Light)!
273
-
274
-
275
- h5. Point Lights
276
-
277
- These are omnidirectional lights that have an origin at a specific point in world space. They shine in all directions from their position, and every object (indeed, every pixel) is lit at a different angle. In the real world, this is technically the only kind of light.
278
-
279
- Point lights are useful for simulating relatively small light sources like candles. Let's add one now. Once again, run the light source generator:
280
-
281
- <shell>
282
- $ jax generate light candle point
283
- </shell>
284
-
285
- Again, add the light source to the scene from the controller +app/controllers/teapot_controller.js+:
286
-
287
- <js>
288
- index: function() {
289
- // ...
290
- this.world.addLightSource(LightSource.find("candle"));
291
- }
292
- </js>
293
-
294
- And again, edit the generated file, +app/resources/light_sources/candle.yml+, and change the +position+ field to look like this:
295
-
296
- <yaml>
297
- position:
298
- x: -1.2
299
- y: -1.2
300
- z: -4.0
301
- </yaml>
302
-
303
- This will position the candle very close to the teapot indeed -- just barely to the left, bottom, and front of it.
304
-
305
- In the same file, let's alter the color of the light itself. Candles are often a reddish-yellow color, so let's make ours the same:
306
-
307
- <yaml>
308
- color:
309
- ambient:
310
- red: 0.6
311
- green: 0.6
312
- blue: 0.0
313
- alpha: 1.0
314
-
315
- diffuse:
316
- red: 1.0
317
- green: 1.0
318
- blue: 0.0
319
- alpha: 1.0
320
-
321
- specular:
322
- red: 0.2
323
- green: 0.2
324
- blue: 0.0
325
- alpha: 1.0
326
- </yaml>
327
-
328
- Reload the scene again and you should see your candle's effect upon the teapot.
329
-
330
- !images/getting_started/teapot-red-directional-point.png(Teapot With Directional and Point Lights)!
331
-
332
-
333
- h5. Spot Lights
334
-
335
- These are just like point lights, except they have a cutoff angle. In the real world, they're analogous to flashlights in that their lit area is cone-shaped and has a specific direction. They are useful for simulating flashlights, automobile headlights, and so on.
336
-
337
- We'll use the spot light to simulate a searchlight.
338
-
339
- Generate the spot light in the same manner as the last two:
340
-
341
- <shell>
342
- $ jax generate light searchlight spot
343
- </shell>
344
-
345
- Like the others, add this light to the scene in +app/controllers/teapot_controller.js+:
346
-
347
- <js>
348
- index: function() {
349
- // ...
350
- this.world.addLightSource(LightSource.find("searchlight"));
351
- }
352
- </js>
353
-
354
- This time, we'll alter the position and direction to shine toward, but not directly upon, the teapot; we'll also alter the color, the angle of the spotlight (that is, the width of the cone), and the spot exponent, which represents how fast light fades out from the center of the cone. With a spot exponent of zero, the spotlight's cone should be quite clearly defined.
355
-
356
- The file to edit is +app/resources/light_sources/searchlight.yml+:
357
-
358
- <yaml>
359
- position:
360
- x: 0
361
- y: 0
362
- z: -3.25
363
-
364
- direction:
365
- x: 0.05
366
- y: -0.025
367
- z: -1
368
-
369
- type: SPOT_LIGHT
370
-
371
- # the cone is approx. 20 degrees wide, in radians
372
- angle: 0.349
373
-
374
- # this light has fairly sharp edges.
375
- spot_exponent: 8
376
-
377
- attenuation:
378
- constant: 1.0
379
- linear: 0
380
- quadratic: 0
381
-
382
- color:
383
- ambient:
384
- red: 0
385
- green: 0
386
- blue: 0.4
387
- alpha: 1
388
-
389
- diffuse:
390
- red: 0
391
- green: 0
392
- blue: 1.0
393
- alpha: 1.0
394
-
395
- specular:
396
- red: 0
397
- green: 1
398
- blue: 0
399
- alpha: 1.0
400
- </yaml>
401
-
402
- Once again, you're ready to reload the scene. Note how the spotlight is bright in the center, but begins to fade a little bit toward the edges. The amount of fading is controlled with the +spot_exponent+ setting. In general, a higher number will produce a spotlight that fades more quickly, while a lower number will produce sharper edges. A spotlight with a spot exponent of 0 will have extremely sharp edges.
403
-
404
- Take some time to alter the various lighting settings, above, and note the differences in the results.
405
-
406
- WARNING: Jax supports an arbitrary number of light sources; that is, there's no hard limit on how many lights you can have in your scene. However, be aware that the scene must be rendered once <em>for each light source</em>, so you can expect to take a significant performance hit by simply tossing lights around without carefully considering the implications. Always keep in mind the quality of the hardware you're targeting; the more light sources you activate, the lower the framerate on your clients' systems.
407
-
408
- !images/getting_started/teapot-red-spot-point-directional.png(Teapot With Directional, Spot and Point Lights)!
409
-
410
-
411
- h4. Making the Most of Models
412
-
413
- So far, we've only made use of the controller and the view. (We've only actually altered the controller, because the view that Jax generates is fine for most applications.)
414
-
415
- Now we'd like to add some lower-level business logic to our application. In particular, we'll make the teapot rotate in
416
- place. This sort of logic is exactly what models are designed to encapsulate.
417
-
418
- So far, we've been using a model created on-the-fly with the following code:
419
-
420
- <js>
421
- var teapot = new Jax.Model({
422
- position: [0, 0, -5],
423
- mesh: new Jax.Mesh.Teapot({
424
- material: Material.find("teapot")
425
- })
426
- });
427
- </js>
428
-
429
- There's nothing wrong with doing this as long as you don't need any logic beyond what Jax.Model gives you. However,
430
- the code to cause the model to rotate is very much model-specific behavior. It would be bad form to define this within
431
- the controller, even if we're defining it on the model. (Note that this is very much possible -- it's just bad practice.)
432
-
433
- h5. Generating the Model
434
-
435
- Instead, let's generate an appropriate Teapot model from the terminal:
436
-
437
- <shell>
438
- $ jax generate model teapot
439
- </shell>
440
-
441
- This will generate a model file at +app/models/teapot.js+ as well as a <em>default resource</em> at +app/resources/teapots/default.yml+.
442
-
443
- h5. Resources
444
-
445
- Models in Jax actually result from the combination of two files: first is the model source code, the JavaScript file which determines the rules governing how the model can be used; second is the resource file, which specifies the raw data behind a given model.
446
-
447
- A good way to think of resources is as database entries, even though they actually occupy a file on the file system. While the model itself may represent a database table, the resource files each describe exactly one record within that table.
448
-
449
- When a model is first generated, Jax also generates a <em>default resource</em>. The default resource is loaded for <em>each and every resource</em>, and specifies <em>default values</em> for the various fields. This allows the individual resource files to completely omit certain attributes, instead allowing the <em>default resource</em> to specify what those attributes should default to.
450
-
451
- In the case of our teapot, we're going to set a default size and speed of rotation for all Teapot models to use. First, edit the resource file. You'll see that, save for a few comments, it's completely empty. Add the following:
452
-
453
- <yaml>
454
- size: 1.0
455
- rotation_speed: 1.0 # in radians per second
456
- </yaml>
457
-
458
- Now, create a new file alongside +app/resources/teapot/default.yml+ called +app/resources/teapot/actual.yml+. Inside that file, we'll specify the position of the teapot we plan to use:
459
-
460
- <yaml>
461
- position:
462
- x: 0
463
- y: 0
464
- z: -5
465
- </yaml>
466
-
467
- You'll notice that we've specified the exact same position that the current teapot already occupies.
468
-
469
- h5. Model Source
470
-
471
- Now that we have some attributes to deal with, we need to add a little bit of logic to the model source in +app/models/teapot.js+. Open that file up and add the following code to the +after_initialize+ function:
472
-
473
- <js>
474
- this.mesh = new Jax.Mesh.Teapot({
475
- size: this.size,
476
- material: Material.find("teapot")
477
- });
478
- </js>
479
-
480
- The above code will pass the teapot's +size+ attribute into its mesh.
481
-
482
- TIP: The mesh is used for rendering -- we'll discuss it in detail later on. For now, just understand that without a Mesh, a model technically _exists_, but it is invisible. The Mesh is the actual 3D data used by WebGL to draw the object, in this case a teapot.
483
-
484
- NOTE: Notice that we didn't do anything with the +position+ attribute, which we created in +app/resources/teapot/actual.yml+. This is because Jax is smart enough to use basic attributes such as +position+ and +direction+ to orient the model without the need for us to explicitly get involved. If we _did_ want to deal with the +position+ attribute directly, we'd have to be careful about it because it is only specified on an _instance_ of the model; the <em>default resource</em> doesn't include the +position+ attribute, so it's quite possible to create a teapot that has no +position+. (In that case, Jax would place it at the origin.)
485
-
486
- h5. Using the Model
487
-
488
- We've now done all the setup necessary to expose our new model to Jax. The only thing left is to _use_ it. Edit your controller, +app/controllers/teapot_controller.js+, and replace the following code:
489
-
490
- <js>
491
- var teapot = new Jax.Model({
492
- position: [0, 0, -5],
493
- mesh: new Jax.Mesh.Teapot({
494
- material: Material.find("teapot")
495
- })
496
- });
497
- this.world.addObject(teapot);
498
- </js>
499
-
500
- with:
501
-
502
- <js>
503
- this.world.addObject(Teapot.find("actual"));
504
- </js>
505
-
506
- Now, when you reload your Jax suite, you should see exactly the same image as before -- except that now, the teapot is being loaded from a resource file instead of being created on-the-fly.
507
-
508
- In addition to being more organized, less confusing and easier to maintain, this gives you a strong foundation upon which to build more complicated model-specific logic. Perfect, because that's what we're going to do next.
509
-
510
- IMPORTANT: When you call +Teapot.find+, Jax is actually <em>creating a new instance</em> of the resource. That means, if you call +Teapot.find+ twice with the same name, you'll get two completely separate instances of the same model! This is quite powerful for creating many instances of a model that are all expected to look and act similarly, such as generic monsters, but can be confusing if you assume that +Teapot.find+ will return a single instance more than once.
511
-
512
- h5. Adding Model Logic
513
-
514
- Once again, edit the +app/models/teapot.js+ model source file. Following the +after_initialize+ method, add a new one called +update+. It takes a single +timechange+ argument. Don't forget to put a comma after the closing curly-brace for +after_initialize+, or you'll have a JavaScript syntax error! Here's what your entire model file should now look like:
515
-
516
- <js>
517
- /**
518
- * class Teapot < Jax.Model
519
- *
520
- **/
521
- var Teapot = (function() {
522
- return Jax.Model.create({
523
- after_initialize: function() {
524
- this.mesh = new Jax.Mesh.Teapot({
525
- size: this.size,
526
- material: Material.find("teapot")
527
- });
528
- },
529
-
530
- update: function(timechange) {
531
- // we're going to add more code here!
532
- }
533
- });
534
- })();
535
- </js>
536
-
537
- As the comment in the model file indicates, we're going to insert some code into the +update+ function. But first, let's talk about that function.
538
-
539
- +update+ is a special function made available to all models _and_ all controllers. If Jax detects that such a method exists, it will call the +update+ method every few milliseconds. The +timechange+ argument is the amount of time, in seconds, since the last time +update+ was called.
540
-
541
- NOTE: The +timechange+ argument is crucial because you can use it to track changes over time. For instance, when we calculate an object's rotation, we'll multiply the total amount of rotation by +timechange+. If you don't do this, the code will still work, but it will run faster on some computers than it will on others (dependent upon framerate). By multiplying against time elapsed, you control the speed of rotation so that it is the same on all devices, regardless of the relative speed of the device.
542
-
543
- Add the following code to the +update+ method to cause the teapot to rotate at the speed specified in its resource file:
544
-
545
- <js>
546
- var rotation_axis = [0, 1, 0];
547
- this.camera.rotate(this.rotation_speed * timechange, rotation_axis);
548
- </js>
549
-
550
- This will cause the teapot to rotate about the Y axis at a speed of +rotation_speed+ radians per second. Because the speed was specified in the resource files, you now have enough code in place to actually create several different teapots rotating at different speeds! Just add resource files for each teapot you intend to use; remember to set the individual teapots up in the +teapot_controller.js+ file, and you're all set!
551
-
552
- h3. Handling Input
553
-
554
- Nearly every application is going to have to deal with user input at one time or another. If it doesn't, it can never become more than yet another WebGL demo with a rotating globe.
555
-
556
- Since we're working toward creating a dungeon in which the user can freely navigate, it would be a good idea to go ahead and add those navigational controls now. We already have a point of reference -- the rotating teapot -- but our scene has not gotten too complex yet, so this is a perfect time to think about camera controls.
557
-
558
- User input is meant to be handled entirely within the controller, so edit +app/controllers/teapot_controller.js+. We'll be focusing on the controller pretty explicitly for awhile.
559
-
560
- h4. Mouse Events
561
-
562
- For this guide, we're going to allow the user to _rotate_ the camera in-place (as if turning their head) only when the mouse is _dragged_. Jax actually supports virtually every possible mouse event, and they are all handled identically to what we're about to do.
563
-
564
- NOTE: Most first-person applications track whether the mouse was _moved_ instead of just when it was _dragged_, but this is a bit of a sticking point with WebGL. The HTML5 standard provides no way to capture mouse input once the mouse itself is blocked from movement; that is, if you move the mouse to the left-hand side of the screen and continue to move it leftward, there will be no way to tell when you are attempting to move it past the edge of the screen. Similarly, there is no way under the current standard to reposition the mouse toward the center of the screen; this would pose too many security issues. Therefore, traditional "mouse-look" is not currently possible because mouse events will suddenly stop firing when the mouse becomes blocked by the screen dimensions. A cheap and cumbersome, but theoretically workable, solution is to listen to mouse _drag_ events instead of mouse _move_ events. This forces the user to move the mouse back to its origin before dragging again, thus not breaking the application. (This issue alone is probably going to pose significant design frustrations for WebGL-based first-person application developers.)
565
-
566
- In your controller, define a new action called +mouse_dragged+. It takes a single +event+ argument:
567
-
568
- <js>
569
- mouse_dragged: function(event) {
570
- this.player.camera.rotate(0.01, [event.diffy, -event.diffx, 0]);
571
- this.player.camera.orient(this.player.camera.getViewVector(), [0,1,0]);
572
- }
573
- </js>
574
-
575
- This simply rotates 0.01 radians for every pixel the mouse is moved. The reason we use the mouse Y axis for the rotation X axis and vice versa, is because we are specifying the axis to rotate _about_. In 3D, rotating about the Y axis results in camera movement along the X axis (looking "left" or "right"). Conversely, rotating about the X axis results in movement along the Y axis (looking "up" or "down").
576
-
577
- A good way to remember the difference is to hold your hands out in front of you, as if holding a ball. (Hold a real ball, if you have one handy!) Rotate the "ball" left and right; which axis are you pivoting on? It's the up/down axis that holds still, so it's the up/down (Y) axis that you're actually rotating _about_.
578
-
579
- Rotating about the Z axis is to rotate about a line drawn straight out in front of you, and results in a "tilt" clockwise or counter-clockwise.
580
-
581
- If you reload your test suite, you'll now see that you can drag the mouse to cause the appropriate rotation.
582
-
583
- The last line in the mouse_dragged function may be a bit confusing. The most effective way to see what it does is to simply comment that last line out, reload your test suite, and see how the camera functions. You'll notice that first of all, you can rotate the camera into a completely upside-down state; not ideal for a first-person application, but quite appropriate for a flight simulator.
584
-
585
- The second thing you may notice with the last line commented out is that the camera has a way of losing its "up" axis, as if you'd rotated about the Z axis. This is due to floating point imprecision. In many applications, it's no big deal, but it's probably not a good thing to allow players to lose their sense of "up" in a first-person environment. A quick and dirty, but effective, way to work around this is to explicitly reorient the camera so that "up" is always pointing in the same hard-coded direction (usually, along the positive Y axis). That's what that last line was responsible for. After doing the appropriate camera rotations, it gathered the new view direction from the camera, and restricted it to be in line with the world "up" axis.
586
-
587
- As we eluded to before, Jax can handle a lot more than +mouse_dragged+ events. Here's the full list:
588
-
589
- | Event | Condition |
590
- | mouse_entered | When the mouse enters the WebGL canvas |
591
- | mouse_exited | When the mouse exits the WebGL canvas |
592
- | mouse_clicked | When the mouse is clicked over the WebGL canvas |
593
- | mouse_moved | When the mouse is moved while no buttons are pressed |
594
- | mouse_dragged | When the mouse is moved while at least one button is pressed |
595
- | mouse_pressed | When a button on the mouse is pressed |
596
- | mouse_released| When a button on the mouse is released |
597
-
598
- Any or all of these can be handled just like the +mouse_dragged+ example, above, by merely defining a function of the appropriate name. In all cases, the function may take a single +event+ argument.
599
-
600
- h4. Keyboard Events
601
-
602
- Keyboard events are generally easier to map because there's no need to worry about translating two-dimensional movements into three; however, handling them properly involves a little more source code than mouse events.
603
-
604
- The best way to handle keyboard events is to detect when a key is pressed and when it is released, _not_ to continually poll whether it is "currently pressed". However, this approach, while more efficient, is more difficult to code for, because it involves setting tracking variables which determine the amount of total movement to apply.
605
-
606
- For instance, if the user presses the "forward" key, but then simultaneously presses the "backward" key, they should cancel each other out. On the other hand, pressing the "forward" key while simultaneously pressing the "strafe right" key should produce a diagonal forward-right movement.
607
-
608
- In your +app/controllers/teapot_controller.js+ file, add the private tracking variable just above the +return+ statement. The first few lines of the controller should now look like this:
609
-
610
- <js>
611
- //= require "application_controller"
612
-
613
- var TeapotController = (function() {
614
- var movement = { forward: 0, backward: 0, left: 0, right: 0 };
615
-
616
- return Jax.Controller.create("teapot", ApplicationController, {
617
- /* the rest of the controller code */
618
- </js>
619
-
620
- With this variable in place, create two more actions for handling +key_pressed+ and +key_released+ events:
621
-
622
- <js>
623
- key_pressed: function(event) {
624
- switch(event.keyCode) {
625
- case KeyEvent.DOM_VK_W: movement.forward = 1; break;
626
- case KeyEvent.DOM_VK_S: movement.backward = -1; break;
627
- case KeyEvent.DOM_VK_A: movement.left = -1; break;
628
- case KeyEvent.DOM_VK_D: movement.right = 1; break;
629
- }
630
- },
631
-
632
- key_released: function(event) {
633
- switch(event.keyCode) {
634
- case KeyEvent.DOM_VK_W: movement.forward = 0; break;
635
- case KeyEvent.DOM_VK_S: movement.backward = 0; break;
636
- case KeyEvent.DOM_VK_A: movement.left = 0; break;
637
- case KeyEvent.DOM_VK_D: movement.right = 0; break;
638
- }
639
- },
640
- </js>
641
-
642
- NOTE: If you're an experienced JavaScript keyboard-input-handling guru, you'll note that the +KeyEvent+ object exists only in FireFox. Well, fret not! Jax has already looked into this potential gotcha, and defines a compatibility layer for you. So code on!
643
-
644
- If you reload your test suite, you won't get any errors, but nothing will _happen_. That's because all we're doing is updating our tracking variable; we're not actually using it.
645
-
646
- To make use of the tracking variable, add one more method:
647
-
648
- <js>
649
- update: function(timechange) {
650
- var speed = 1.5 * timechange;
651
-
652
- this.player.camera.move((movement.forward + movement.backward) * speed);
653
- this.player.camera.strafe((movement.left + movement.right) * speed);
654
- },
655
- </js>
656
-
657
- Similarly to the +update+ method of our +Teapot+ model, the controller's +update+ method will be called by Jax once every few milliseconds. In it, we simply move in the total direction represented by the combination of our four tracking values: forward and backward, left and right. Since one is always negative (if pressed) and the other is always positive (if pressed), they will cancel each other out if both are pressed at once, and the camera will not move.
658
-
659
- If the camera _does_ move, it will do so at a speed of 1.5 units per second.
660
-
661
- NOTE: Astute readers will note that there's nothing technically keeping us from maintaining a handle to the +Teapot+ that we added in the +index+ action, and simply rotating it from the controller's +update+ method rather than doing so from within the model. While this is technically true, it would be considered bad practice to manipulate the model that way because you would in effect be moving model-level logic into the controller. If, however, you were to move the model based on <em>user input</em>, then it would be perfectly acceptable to do so via the controller. In general, only deal with set-up, tear-down and user input within the controller. Virtually everything else should happen in the models.
662
-
663
- Here is a table listing the keyboard events you can handle within a Jax application, and the conditions under which they are fired:
664
-
665
- | Event | Condition |
666
- | key_pressed | Fired when a physical keyboard button is pressed. Do not confuse this with +key_typed+. If the button is held down, additional +key_pressed+ events are *not* fired. |
667
- | key_typed | Fired when a character is typed. If the button is held down, additional +key_typed+ events will be fired -- one for each corresponding character. |
668
- | key_released | Fired when a physical keyboard button is released. |
669
-
670
- IMPORTANT: Unlike mouse events, which only fire in direct relation to the HTML +canvas+ element, keyboard events will fire whenever the corresponding event occurs anywhere in the document -- _except_ when a form input element is focused. If an element is accepting input from the user, Jax will not capture those events, so you'll have to revert to standard HTML event capturing.
671
-
672
- h3. Scaffolding
673
-
674
- You've just completed a very common process in Jax. So common, in fact, that it has its own generator. We can generate a _scaffold_ to have Jax build a controller, model and material for us. As you can imagine, scaffolding is most useful in situations where the controller name and the model name are the same, as is the case with our Dungeon:
675
-
676
- <shell>
677
- $ jax generate scaffold dungeon
678
- </shell>
679
-
680
- The _scaffold_ generator will create the following files:
681
-
682
- | File | Purpose |
683
- | app/models/dungeon.js | The +Dungeon+ model |
684
- | app/controllers/dungeon_controller.js | The +DungeonController+ |
685
- | app/views/dungeon/index.js | The +DungeonController+'s primary view |
686
- | app/helpers/dungeon_helper.js | Helper code for +DungeonHelper+ |
687
- | app/resources/dungeons/default.yml | The default resource for the +Dungeon+ model |
688
- | app/resources/materials/dungeon.yml | A default material for the +Dungeon+'s mesh |
689
- | config/routes.rb | Inserts the route mapping for the +DungeonController+ |
690
- | spec/javascripts/models/dungeon.js | Tests for the +Dungeon+ model |
691
- | spec/javascripts/controllers/dungeon_controller_spec.js | Tests for the +DungeonController+ |
692
- | spec/javascripts/helpers/dungeon_helper.js | Tests for the +DungeonHelper+ helper |
693
-
694
- h3. Helpers
695
-
696
- In both models and controllers, it's easy to end up duplicating previous efforts. For instance, in this example we're going to use exactly the same keyboard and mouse input handling for the +Dungeon+ controller as we have already implemented for the +Teapot+ controller.
697
-
698
- Though it might seem tempting to copy-and-paste the code from one controller into the other, Jax frowns upon this. As an interpreted language, one of JavaScript's greatest strengths is also its greatest weakness. Code is not evaluated until it is time to execute it, so if there is an error in the code, you won't know it until it actually runs. Therefore, every line of additional code in an application should be carefully thought out because every line of code potentially introduces another point of failure. Following this line of thought, copy and paste is a very dangerous tool that should only be done in extreme circumstances. What's more, copying code in this way is a quick path to a maintenance nightmare; if you have to change anything, you'll have to make the same changes elsewhere, and this problem only grows more complex as more controllers and models are added to the mix.
699
-
700
- Fortunately, Jax presents a solution to this redundancy problem: Helpers.
701
-
702
- Helpers are modules of code which can be easily mixed in with other elements of your project. You can name helpers whatever you want and create as many as you want. By default, whenever you generate a controller, Jax creates a corresponding helper to go with it. You can rename this file if you like, or completely remove it.
703
-
704
- By default, even if a helper is named similarly, it will not be used. If you wish to take advantage of a helper, you must ask for it explicitly. This is easy to do, and it's the exact same process to include a helper into a model as it is to include one into a controller.
705
-
706
- NOTE: A special exception is the +ApplicationHelper+, which is one of the default files generated with every Jax application. The +ApplicationHelper+ will automatically be mixed into _all_ controllers and models, unless you delete the +ApplicationHelper+.
707
-
708
- h4. Extract the Code
709
-
710
- First, we need to extract the code we want into the helper we want to use. Create a new file called +app/helpers/user_input_helper.js+ and make it look like this:
711
-
712
- <js>
713
- var UserInputHelper = (function() {
714
- return Jax.Helper.create({
715
-
716
- });
717
- })();
718
- </js>
719
-
720
- Cut all of the mouse and keyboard input code, including the +update+ method, out of the +Teapot+ controller, and paste it into the +UserInputHelper+. Don't forget to include the tracking variable +movement+!
721
-
722
- The complete +UserInputHelper+:
723
-
724
- <js>
725
- var UserInputHelper = (function() {
726
- var movement = { forward: 0, backward: 0, left: 0, right: 0 };
727
-
728
- return Jax.Helper.create({
729
- mouse_dragged: function(event) {
730
- this.player.camera.rotate(0.01, [event.diffy, -event.diffx, 0]);
731
- this.player.camera.orient(this.player.camera.getViewVector(), [0,1,0]);
732
- },
733
-
734
- key_pressed: function(event) {
735
- switch(event.keyCode) {
736
- case KeyEvent.DOM_VK_W: movement.forward = 1; break;
737
- case KeyEvent.DOM_VK_S: movement.backward = -1; break;
738
- case KeyEvent.DOM_VK_A: movement.left = -1; break;
739
- case KeyEvent.DOM_VK_D: movement.right = 1; break;
740
- }
741
- },
742
-
743
- key_released: function(event) {
744
- switch(event.keyCode) {
745
- case KeyEvent.DOM_VK_W: movement.forward = 0; break;
746
- case KeyEvent.DOM_VK_S: movement.backward = 0; break;
747
- case KeyEvent.DOM_VK_A: movement.left = 0; break;
748
- case KeyEvent.DOM_VK_D: movement.right = 0; break;
749
- }
750
- },
751
-
752
- update: function(timechange) {
753
- var speed = 1.5 * timechange;
754
-
755
- this.player.camera.move((movement.forward + movement.backward) * speed);
756
- this.player.camera.strafe((movement.left + movement.right) * speed);
757
- }
758
- });
759
- })();
760
- </js>
761
-
762
- h4. Call the Helper
763
-
764
- With all of the copied code removed from the +Teapot+ controller, it should now be looking pretty sparse once more. Don't worry -- we're about to pull all of that functionality right back into the controller, but we're going to do it in a more modular way.
765
-
766
- Since Jax won't just automatically include the helper for us, we need to tell it to do so. Define a new function in your controller called +helpers+. It's a very simple function that just returns a hard-coded array of helpers:
767
-
768
- <js>
769
- helpers: function() {
770
- return [ UserInputHelper ];
771
- }
772
- </js>
773
-
774
- That's it! Jax will detect the presence of this function and use it to mix in the helper methods with this controller <em>as if they were defined by the controller itself</em>.
775
-
776
- If you reload your Jax suite, you'll see that you can still navigate around the rotating teapot just as before. There's no logical difference, but the code is much more organized, easier to maintain and introduces fewer points of failure by virtue of requiring less code.
777
-
778
- h3. The Dungeon
779
-
780
- We're now ready to build up our dungeon. First, we need a way to tell Jax what the dungeon should look like. It would also be a good idea to mark out where we plan to place light sources, where the player should start, and what direction the camera should be facing at the start of the demo. All of these things represent dungeon data, so we can put them into our resource file.
781
-
782
- h4. Define the Resource
783
-
784
- Create the file +app/resources/dungeons/first.yml+ and add the following content:
785
-
786
- <yaml>
787
- map:
788
- - XXXXXXXXX
789
- - X XXX 'X
790
- - X' X X
791
- - XXXXXXXXX
792
-
793
- starting:
794
- position: 1, 1
795
- direction: 0, 1
796
- </yaml>
797
-
798
- NOTE: It's usually a good idea to define the obvious components of a model in its resource file before starting to add any model logic. By the time you've decided what data to store, you'll often have a good idea of how you want to parse it. This doesn't mean you can't come back and alter it as needed, of course.
799
-
800
- As you can see, we've decided to have an attribute +map+ which is an array of strings. Each character in each string can be one of the letter X, empty space, or an apostrophe ('). The apostrophes will represent point lights, which we'll use to light up the dungeon. The X's mark walls, while white space represent passable hallways.
801
-
802
- The other attribute we've created is called +starting+, and contains two sub-attributes: +position+ and +direction+. Notice that we've specified both of these as vectors in two dimensions, not three. This is because the map itself is laid out in two dimensions; we'll do the necessary conversions into 3D (by simply setting the third component to 0) in a moment.
803
-
804
- h4. Write Some Tests
805
-
806
- After you decide on a format for your resources, you should write some basic tests to make sure that it is parsed as you expect it to be. This provides a little added peace-of-mind that Jax is doing its job, but more importantly, it exposes the layers of functionality that you have yet to write. You may be surprised to find that quite a bit of your "work" has already been taken care of for you!
807
-
808
- NOTE: This guide will only touch on the basics of unit testing in Jax; for a much more detailed guide on testing your Jax applications, see the "testing guide":testing.html.
809
-
810
- Deciding on what level of detail to test is a minor black art. If you write tests that are too vague, you'll end up with flimsy specs that let you get away with writing unstable code and introduce hard-to-debug issues. On the other hand, if the tests are too specific, you'll find yourself having to go back and modify the them every time you make a minor change to the resource, such as setting +position+ to (1, 2) instead of (1, 1). Worse, constantly switching back and forth between tests and data often leads developers to begin ignoring their tests entirely, which is just as bad as not testing at all.
811
-
812
- As a rule of thumb, try to test the _structure_ of your resource without testing its _content_. For example, instead of making sure an array contains the values [1, 0, 1], simply make sure that it _is_ an array, and that it contains exactly three values.
813
-
814
- There are 3 basic conditions that we should spec out for our +Dungeon+ resource. They are:
815
-
816
- # It should have a map, which should be an array of strings
817
- # It should have a starting position and direction
818
- # The starting position and direction should both be converted into 3-dimensional vectors, not left as strings or 2-dimensional vectors.
819
-
820
- Edit your +spec/javascripts/models/dungeon_spec.js+ file. Just after the +var model;+ declaration, we'll create a test specifically for the _first_ +Dungeon+ resource. Insert the following code beginning on line 3:
821
-
822
- <js>
823
- describe("'first' resource", function() {
824
- beforeEach(function() { model = Dungeon.find("first"); });
825
-
826
- it("should have a map, which should be a an array of strings", function() {
827
- var map = model.map;
828
- expect(map.length).toBeGreaterThan(0);
829
- for (var i = 0; i < map.length; i++)
830
- expect(typeof map[i]).toEqual('string');
831
- });
832
-
833
- it("should have a 3-element starting position", function() {
834
- expect(model.starting.position.length).toEqual(3);
835
- });
836
-
837
- it("should have a 3-element starting direction", function() {
838
- expect(model.starting.direction.length).toEqual(3);
839
- });
840
- });
841
- </js>
842
-
843
- Reloading your suite, you'll now have some failing tests. Notice that only two are failing, however. The first one is passing! This is because Jax has automatically converted the dungeon's +map+ attribute into an array of strings.
844
-
845
- h4. Adding Business Logic
846
-
847
- Two of our tests are still failing because the model doesn't know how to translate the starting +position+ and +direction+ attributes into 3-dimensional vectors.
848
-
849
- Open your +app/models/dungeon.js+ file. On line 6, just above <tt>return Jax.Model.Create ...</tt>, add the following private method:
850
-
851
- <js>
852
- function parse(str) {
853
- var vec = str.split(/,\s*/);
854
- return [parseFloat(vec[0]), 0.0, parseFloat(vec[1])];
855
- }
856
- </js>
857
-
858
- This method is _private_ because it is declared within a generic function (see the line just preceding it), making it accessible only to code that is _also_ in the same function. That limits the scope of the +parseVectorString+ function just to the +Dungeon+ model.
859
-
860
- Now, on line 12, within the +after_initialize+ method body, add the following:
861
-
862
- <js>
863
- if (this.starting) {
864
- this.starting.position = parse(this.starting.position);
865
- this.starting.direction = parse(this.starting.direction);
866
- }
867
- </js>
868
-
869
- WARNING: The reason we test to see if +this.starting+ exists is because we didn't add a corresponding entry to the dungeon's +default.yml+ file. This means that it's possible to instantiate a dungeon without any +starting+ attribute. Had we moved the +starting+ attribute into the +default+ resource, we would not have to make this check.
870
-
871
- Here's the complete +app/models/dungeon.js+ source code listing:
872
-
873
- <js>
874
- /**
875
- * class Dungeon < Jax.Model
876
- *
877
- **/
878
- var Dungeon = (function() {
879
- function parse(str) {
880
- var vec = str.split(/,\s*/);
881
- return [parseFloat(vec[0]), 0.0, parseFloat(vec[1])];
882
- }
883
-
884
- return Jax.Model.create({
885
- after_initialize: function() {
886
- if (this.starting) {
887
- this.starting.position = parse(this.starting.position);
888
- this.starting.direction = parse(this.starting.direction);
889
- }
890
- }
891
- });
892
- })();
893
- </js>
894
-
895
- h4. The Mesh
896
-
897
- As you saw with the _Teacup_ example, in order to draw on the screen, we must first define a Mesh. Meshes represent the 3D data used by WebGL. Previously, we used a built-in type of Mesh -- the +Teapot+. There are a growing number of built-in meshes that you can use for simple objects, and they are listed in the table below:
898
-
899
- | Mesh type | Description | Usage Example |
900
- | Cube | Six perpendicular Quads forming a closed box. The faces of the Cube point outward. | <tt>new Jax.Mesh.Cube({width:1, height:2, depth:3});</tt> |
901
- | Plane | An arbitrary number of Quads placed adjacently to form one large, multi-polygon square. | <tt>new Jax.Mesh.Plane({width:500, height:500, x_segments:20, y_segments:20});</tt> |
902
- | Quad | A single-polygon square. | <tt>new Jax.Mesh.Quad({width:1, height:1});</tt> |
903
- | Sphere | A multi-polygon mesh approximating the shape of a ball. Slices and stacks comprise the number of polygons; the more slices and stacks, the more perfect the sphere will be. | <tt>new Jax.Mesh.Sphere({slices:30, stacks:30, radius:1});</tt> |
904
- | Teapot | A Jax implementation of the "Utah Teapot":http://en.wikipedia.org/wiki/Utah_teapot. | <tt>new Jax.Mesh.Teapot({size:1});</tt> |
905
- | Torus | A "donut"-shaped mesh. The number of polygons is comprised of the number of sides and rings; the more polygons, the more perfect the torus will be. | <tt>new Jax.Mesh.Torus({inner_radius:0.6, outer_radius:1.8, sides:128, rings:256});</tt> |
906
-
907
- Unfortunately, no graphics library can pre-build every possible mesh -- and besides, you probably wouldn't want them to try. Whether you store your meshes in static files and load them with AJAX, or write algorithms to build them dynamically, one way or another you're going to need to know how to build your own Mesh.
908
-
909
- WARNING: Meshes are confusing. You're going to need to deal with raw 3D data and you're going to need to use some math to put it all together. If you are new to graphical programming, you should brush up on the principles of basic mesh construction before continuing.
910
-
911
- h5. A Simple Mesh Example
912
-
913
- As mesh construction goes, Jax is pretty easy to use. Here's how you would create a Quad mesh:
914
-
915
- <js>
916
- var QuadMesh = Jax.Class.create(Jax.Mesh, {
917
- init: function(vertices, colors, textureCoords, normals, vertexIndices) {
918
- this.draw_mode = GL_TRIANGLE_STRIP;
919
-
920
- var size = 1.0; // 1 unit high, 1 unit wide
921
- var width = size / 2, height = size / 2;
922
-
923
- vertices.push(-width, height, 0);
924
- vertices.push(-width, -height, 0);
925
- vertices.push( width, height, 0);
926
- vertices.push( width, -height, 0);
927
-
928
- colors.push(1,1,1,1);
929
- colors.push(1,1,1,1);
930
- colors.push(1,1,1,1);
931
- colors.push(1,1,1,1);
932
-
933
- textureCoords.push(0, 1);
934
- textureCoords.push(0, 0);
935
- textureCoords.push(1, 1);
936
- textureCoords.push(1, 0);
937
-
938
- normals.push(0,0,1);
939
- normals.push(0,0,1);
940
- normals.push(0,0,1);
941
- normals.push(0,0,1);
942
-
943
- vertexIndices.push(0, 1, 2, 3);
944
- }
945
- });
946
- </js>
947
-
948
- Most of the above should look familiar if you've ever dealt with 3D models before.
949
-
950
- First, we tell Jax to create a new mesh type by inheriting from the base +Jax.Mesh+ class. There's nothing special about this that you haven't seen elsewhere already.
951
-
952
- We only need to add one function, +init+, to the mesh. When Jax determines that it's time to build (or rebuild) the mesh, it will call +init+ with a number of arguments. All of these arguments are arrays. Although they are all effectively optional, omitting them will limit their compatibility with certain shaders:
953
-
954
- | Argument | Description | If Empty... |
955
- | +vertices+ | Vertex coordinate data. Must be stored in multiples of 3 (X, Y, Z). | ... the mesh can't be rendered. |
956
- | +colors+ | Per-vertex color information. Must be stored in multiples of 4 (red, green, blue, alpha transparency) and must have one color per vertex stored in +vertices+. | ... the color data will default to all 1's (pure white, completely opaque). |
957
- | +textureCoords+ | Texture coordinate information. Multiples of 2 (U, V). Must specify one texture coordinate per vertex. Individual materials may scale these numbers as needed. | ... the mesh cannot be rendered with any texture-based shaders. Attempting to do so will probably result in a GL_INVALID_OPERATION error. |
958
- | +normals+ | Directional vectors representing the direction each vertex is pointing. Multiples of 3 (X, Y, Z). Must specify one normal per vertex. | ... the mesh cannot be lit effectively, and trying to render with lighting support or any other normals-based shader may result in a GL_INVALID_OPERATION error. Also, since tangent space cannot be calculated without normal data, normal maps and other shaders relying on tangent vectors will probably fail. |
959
- | +vertexIndices+ | Vertex index data. Stored in multiples of 1, each index is an integer value pointing to a vertex to be drawn. For instance, index 0 represents <tt>vertices[0..2]</tt>; index 1 represents <tt>vertices[3..5]</tt>; and so on. Note that each index corresponds identically to color index, textureCoords index, and so on. | If left empty, Jax will automatically use the +vertices+ data sequentially as if the vertex indices were equal to <tt>[0, 1, 2, ... n]</tt>. No errors should occur, though the mesh may not be rendered properly if the vertex data is not meant to be rendered sequentially. |
960
-
961
- INFO: In order to achieve maximum compatibility, you should do your best not to leave any of the data arrays empty, except perhaps +vertexIndices+ if your mesh doesn't rely on vertex indices.
962
-
963
- h5. The Dungeon Mesh
964
-
965
- Now that you have enough information to construct a mesh in Jax, it's time for us to build the mesh to be used for the dungeon. Remember the +map+ attribute of the +Dungeon+ model? We're now going to iterate through that map and dynamically build our dungeon mesh from it.
966
-
967
- INFO: Meshes do not have to be dynamically generated in the same way as we are doing with the +Dungeon+, but they do need to be given data from _somewhere_. You could load this data directly from a JSON file retrieved via AJAX, or build the mesh in a completely different way. The best approach to mesh building depends entirely upon the needs and expectations of your application.
968
-
969
- In the +after_initialize+ method of your +app/models/dungeon.js+ file, add the following code:
970
-
971
- <js>
972
- if (this.map) {
973
- var map = this.map;
974
- this.mesh = new Jax.Mesh({
975
- init: function(vertices, colors, texcoords, normals, indices) {
976
- var ofs = 0.5; // offset from center of each grid node
977
- var row;
978
-
979
- function drawSideWall(x, z, side) {
980
- x += ofs*side;
981
- // triangle 1 triangle 2
982
- vertices.push (x,-ofs,z-ofs, x,-ofs,z+ofs, x,ofs,z-ofs, x,ofs,z-ofs, x,-ofs,z+ofs, x,ofs,z+ofs);
983
- colors.push (1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1, 1,0,0,1);
984
- texcoords.push(0,0, 1,0, 0,1, 0,1, 1,0, 1,1);
985
- normals.push (-side,0,0, -side,0,0, -side,0,0, -side,0,0, -side,0,0, -side,0,0);
986
- }
987
-
988
- function drawFrontWall(x, z, side) {
989
- z -= ofs*side;
990
- // triangle 1 triangle 2
991
- vertices.push (x-ofs,-ofs,z, x+ofs,-ofs,z, x-ofs,ofs,z, x-ofs,ofs,z, x+ofs,-ofs,z, x+ofs,ofs,z);
992
- colors.push (0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1);
993
- texcoords.push(0,0, 1,0, 0,1, 0,1, 1,0, 1,1);
994
- normals.push (0,0,side, 0,0,side, 0,0,side, 0,0,side, 0,0,side, 0,0,side);
995
- }
996
-
997
- for (var y = 0; y < map.length; y++) {
998
- row = map[y];
999
- for (var x = 0; x < row.length; x++) {
1000
- var ch = row[x];
1001
- if (ch == 'X') {
1002
- } else {
1003
- /* walls */
1004
- if (x == 0 || row[x-1] == 'X') drawSideWall(x, y, -1); // left
1005
- if (y == 0 || map[y-1][x] == 'X') drawFrontWall(x, y, 1); // front
1006
- if (x == row.length-1 || row[x+1] == 'X') drawSideWall(x, y, 1); // right
1007
- if (y == map.length-1 || map[y+1][x] == 'X') drawFrontWall(x, y, -1); // back
1008
-
1009
- /* floor */
1010
- vertices.push(x-0.5,-0.5,y+0.5, x-0.5,-0.5,y-0.5, x+0.5,-0.5,y-0.5); // tri 1
1011
- vertices.push(x-0.5,-0.5,y+0.5, x+0.5,-0.5,y-0.5, x+0.5,-0.5,y+0.5); // tri 2
1012
- colors.push(1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1);
1013
- texcoords.push(0,1, 0,0, 1,0, 0,1, 1,0, 1,1);
1014
- normals.push(0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0);
1015
-
1016
- /* ceiling */
1017
- vertices.push(x-0.5, 0.5,y+0.5, x-0.5, 0.5,y-0.5, x+0.5, 0.5,y-0.5); // tri 1
1018
- vertices.push(x-0.5, 0.5,y+0.5, x+0.5, 0.5,y-0.5, x+0.5, 0.5,y+0.5); // tri 2
1019
- colors.push(1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1, 1,1,0,1);
1020
- texcoords.push(0,1, 0,0, 1,0, 0,1, 1,0, 1,1);
1021
- normals.push(0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0, 0,-1,0);
1022
- }
1023
- }
1024
- }
1025
- }
1026
- });
1027
-
1028
- this.mesh.material = Material.find("dungeon");
1029
- }
1030
- </js>
1031
-
1032
- h4. Drawing the Scene
1033
-
1034
- Now that we have our mesh, our model, its basic material and some input handling code in place, all we have to do is call these components from the controller. Doing so involves nothing you haven't already seen before in this guide. Here's the complete source code for +app/controllers/dungeon_controller.js+:
1035
-
1036
- <js>
1037
- //= require "application_controller"
1038
-
1039
- var DungeonController = (function() {
1040
- return Jax.Controller.create("dungeon", ApplicationController, {
1041
- index: function() {
1042
- var dungeon = Dungeon.find("first");
1043
- this.world.addObject(dungeon);
1044
- this.player.camera.setPosition(dungeon.starting.position);
1045
- this.player.camera.setDirection(dungeon.starting.direction);
1046
- },
1047
-
1048
- helpers: function() { return [UserInputHelper]; }
1049
- });
1050
- })();
1051
- </js>
1052
-
1053
- Don't forget to change the +root+ in +config/routes.rb+. Your +config/routes.rb+ file should now look like:
1054
-
1055
- <js>
1056
- Dungeon.routes.map do
1057
- map 'dungeon/index'
1058
- map 'teapot/index'
1059
- root 'dungeon'
1060
- end
1061
- </js>
1062
-
1063
- After making this change and reloading your Jax suite, you should be standing within (and able to walk / look around in) your new, brightly-colored Dungeon!
1064
-
1065
- !images/getting_started/dungeon-rainbow.png(A Brightly Colored Dungeon)!
1066
-
1067
-
1068
- h3. Adding Texture
1069
-
1070
- The dungeon is functional, but it's not very believable. In fact, a rainbow-colored dungeon isn't much of a dungeon at all! We made each wall a starkly different color for the purposes of this tutorial, but realistically, you'd probably never do this. Instead, you'd make it look more like the environment you're targeting, and the most basic way to do this is with texturing.
1071
-
1072
- h4. Updating the Material
1073
-
1074
- When we generated our +Dungeon+ scaffold, Jax created a basic material for us. However, that material only supports basic color and lighting. We could edit the material file by hand, but there are a lot of options, and they're different from shader to shader. Rather than force you to commit them all to memory or constantly look up the Jax documentation, you can rerun the Jax +material+ generator with the <tt>--append</tt> option:
1075
-
1076
- <shell>
1077
- $ jax generate material dungeon texture --append
1078
- </shell>
1079
-
1080
- NOTE: Any shaders that you invoke using the <tt>--append</tt> command will be _added_ to the list of shaders used by +material+. This command won't replace any shaders and cannot be used to remove any. Running the example above twice will produce a material that expects two separate textures.
1081
-
1082
- h4. Selecting a Texture
1083
-
1084
- We're going to use the texture below for the walls of the dungeon. Save this image into your Jax application as +public/images/rock.png+:
1085
-
1086
- !images/getting_started/rock.png(The Dungeon Wall Texture)!
1087
-
1088
- Now edit the +app/resources/materials/dungeon.yml+ file. Scroll to the bottom, and change the texture's +path+ to the path of the dungeon texture:
1089
-
1090
- <yaml>
1091
- - type: Texture
1092
- path: /images/rock.png
1093
- # ...
1094
- </yaml>
1095
-
1096
- Reload, and you'll see textured walls! However, all is not quite ideal. The walls of the dungeon are now textured, but they are still tinged with their original colors. This is because the per-vertex color data hasn't changed. Jax, receiving conflicting information about the mesh, has merged the two data sets. Actually, it's pretty normal for a graphics library to do this, because it allows you to do special coloring on a mesh irrespective of the textures used.
1097
-
1098
- <strong>To undo the colored walls, simply edit the +app/models/dungeon.js+ file and replace all the 0's in calls to +colors.push(...)+ to 1's. Reload, and all of the walls should be the expected color.</strong>
1099
-
1100
- <img src="images/getting_started/dungeon-rainbow-textured.png" style="width:300px;height:200px;margin-right:2em;float:left;" />
1101
- <img src="images/getting_started/dungeon-textured.png" style="clear:right;width:300px;height:200px;" />
1102
-
1103
-
1104
- h3. Light Sources
1105
-
1106
- Our dungeon is looking good, but we're not quite there, yet. We still need to add our point lights to the scene. Fortunately, as you saw with the Teacup example, adding lights is a piece of cake! First we need to generate the light source:
1107
-
1108
- <shell>
1109
- $ jax generate light torch point
1110
- </shell>
1111
-
1112
- Now we just need to add the light source to the appropriate locations in the scene. However, the appropriate locations are determined by the model. The controller shouldn't necessarily care about where the lights are positioned, as long as they are positioned appropriately. Since these lights are directly associated with the dungeon, it makes sense for the controller to delegate this job to the dungeon.
1113
-
1114
- In the +index+ action of the +app/controllers/dungeon_controller.js+ file, add the following line:
1115
-
1116
- <js>
1117
- dungeon.addTorches(this.world);
1118
- </js>
1119
-
1120
- h4. More Model Logic
1121
-
1122
- We've just instructed the controller to call a method of +Dungeon+ that doesn't exist. Obviously, the next step is to define that method in +app/models/dungeon.js+:
1123
-
1124
- <js>
1125
- addTorches: function(world) {
1126
- var map = this.map;
1127
- if (map) {
1128
- for (var z = 0; z < map.length; z++) {
1129
- var row = map[z];
1130
- for (var x = 0; x < row.length; x++) {
1131
- // each apostrophe (') represents a light source
1132
- if (row[x] == "'") {
1133
- var torch = LightSource.find("torch");
1134
- torch.camera.setPosition(x, 0, z);
1135
- world.addLightSource(torch);
1136
- }
1137
- }
1138
- }
1139
- }
1140
- }
1141
- </js>
1142
-
1143
- What this code does is search through the map looking for light sources (denoted by an apostrophe). When it finds one, it instantiates a new point light form the +torch+ resource, sets its position and then adds it to the scene. Since all the walls and floors are offset by half a unit in each direction, the point light will be positioned directly in the middle of the hallway.
1144
-
1145
- INFO: Nearly every tangible object in Jax has an internal camera. This tracks its position as well as its orientation. Models, light sources, anything that can be added to the world can be positioned and oriented in exactly the same way. In addition, since each Camera tracks its own frustum, (you don't need to know what this is right now), each object can "see" the other objects in the scene. This is very useful for AI programming.
1146
-
1147
- !images/getting_started/dungeon-textured-lighting.png(Dungeon with Texture and Lighting)!
1148
-
1149
-
1150
- h3. Finishing Touches
1151
-
1152
- There are a few things we could do to improve the Dungeon scene somewhat. First of all, the torch lights work well but some areas are rather dark. Instead of just adding more apostrophes to the map, we could create one additional point light that follows the player around as if they were holding a torch.
1153
-
1154
- Second, it would be nice to add even more detail to the scene. Even state-of-the-art hardware can only model so much complexity, but we can fake it by adding normal mapping.
1155
-
1156
- h4. The Lantern
1157
-
1158
- It might be preferable for the player to hold a light source somewhat different than those attached to the walls, just in case we need to tweak the color, attenuation, and so forth. Since it's not particularly troublesome to do so, let's just generate a new point light called +lantern+:
1159
-
1160
- <shell>
1161
- $ jax generate light lantern point
1162
- </shell>
1163
-
1164
- Now, add it to the world via the controller in the +index+ action of +app/controllers/dungeon_controller.js+:
1165
-
1166
- <js>
1167
- this.player.lantern = LightSource.find("lantern");
1168
- this.world.addLightSource(this.player.lantern);
1169
- </js>
1170
-
1171
- Now that the light source _exists_, we need to make it follow the player around. Since our +update+ method is part of the +UserInputHelper+, we'll want to add a condition to verify that the player actually _has_ a lantern, or else we'll accidentally break our Teapot demo.
1172
-
1173
- NOTE: This, incidentally, is why unit testing is so important for both models and controllers. We're skipping a lot of testing in order to reduce the length and complexity of this guide, but in a real-world application you should absolutely be testing every possible component. You are encouraged to take a look at the "Testing Guide":testing.html when you have completed this one.
1174
-
1175
- Add this code to the end of the +update+ method in the +app/helpers/user_input_helper.js+ file:
1176
-
1177
- <js>
1178
- if (this.player.lantern) {
1179
- // reposition the lantern to the player's location
1180
- this.player.lantern.camera.setPosition(this.player.camera.getPosition());
1181
- }
1182
- </js>
1183
-
1184
- Perfect! Now it's a quite well-lit dungeon indeed. If you prefer a darker dungeon, you can make the lights less illuminating by playing with the attenuation and color settings for the light sources. To get the most out of lights, you should also take a look at the "Lighting Guide":lighting.html.
1185
-
1186
- h4. Normal Mapping
1187
-
1188
- Normal mapping, also called DOT3 bump mapping, is a technique that converts a specially-encoded texture image into directional vectors, or _normals_, which are then used in lighting calculations. The end result is a surface that looks far more detailed than it really is. If you look at a single-polygon Quad with normal mapping, it may _look_ like it is composed of many thousands of polygons; however, if you look at that same quad from a severe angle, you'll see that it is still, in fact, quite flat. Normal mapping makes the (usually correct) assumption that these extreme angles will be few and far between, and manages to pull off some very impressive-looking effects without sacrificing much in the way of graphics processing power.
1189
-
1190
- In order to implement normal mapping, we'll first need a normal map. Save this image to +public/images/rock_normal.png+:
1191
-
1192
- !images/getting_started/rock_normal.png(The Dungeon Wall Normal Map)!
1193
-
1194
- Now append a +normal_map+ shader to your existing dungeon wall material:
1195
-
1196
- <shell>
1197
- $ jax generate material dungeon normal_map --append
1198
- </shell>
1199
-
1200
- Finally, edit the +app/resources/materials/dungeon.yml+ file and set the path to the normal map image:
1201
-
1202
- <yaml>
1203
- - type: NormalMap
1204
- path: /images/rock_normal.png
1205
- # ...
1206
- </yaml>
1207
-
1208
- You're done setting up normal mapping! We've already got all of our lights, meshes and whatnot in place, so all you have to do now is reload your application to see the normal map in action.
1209
-
1210
- !images/getting_started/dungeon-normal-map.png(Dungeon with Normal Mapping)!